base on A GlobalProtect VPN client for Linux, written in Rust, based on OpenConnect and Tauri, supports SSO with MFA, YubiKey, and client certificate authentication, etc. # GlobalProtect-openconnect A modern GlobalProtect VPN client for Linux, built on OpenConnect with full support for SSO authentication. This project provides both command-line and graphical interfaces for seamless VPN connectivity. <p align="center"> <img width="300" src="https://github.com/user-attachments/assets/2fb6116c-dc57-43f2-af75-9c3d97ab7122"> </p> > **Inspired by** [gp-saml-gui](https://github.com/dlenski/gp-saml-gui) ## Table of Contents - [Features](#features) - [Usage](#usage) - [Command-Line Interface](#command-line-interface) - [Graphical User Interface](#graphical-user-interface) - [Installation](#installation) - [Debian / Ubuntu](#debian--ubuntu) - [Arch Linux / Manjaro](#arch-linux--manjaro) - [Fedora 38+ / Rawhide](#fedora-38--rawhide) - [openSUSE Leap 15.6+ / Tumbleweed](#opensuse-leap-156--tumbleweed) - [Other RPM-based Distributions](#other-rpm-based-distributions) - [Gentoo](#gentoo) - [NixOS](#nixos) - [Other Distributions](#other-distributions) - [Building from Source](#building-from-source) - [Frequently Asked Questions](#frequently-asked-questions) - [License](#license) ## Features - **Cross-Platform Linux Support** – Optimized for various Linux distributions - **Dual Interface** – Available as both CLI and GUI applications - **Flexible Authentication** – Supports SSO, non-SSO, FIDO2 (e.g., YubiKey), and client certificate authentication - **Browser Integration** – Authenticate using your default browser or any specified browser - **Multi-Portal Support** – Connect to multiple portals and gateways - **Direct Gateway Connection** – Bypass portal selection when needed - **Auto-Connect** – Automatically connect on system startup - **System Tray Integration** – Convenient system tray icon (requires [gnome-shell-extension-appindicator](https://extensions.gnome.org/extension/615/appindicator-support/) on GNOME) ## Usage ### Command-Line Interface The CLI version is fully open source and feature-rich, providing nearly identical functionality to the GUI version. #### Basic Commands ```bash Usage: gpclient [OPTIONS] <COMMAND> Commands: connect Connect to a portal server disconnect Disconnect from the server launch-gui Launch the GUI help Print this message or the help of the given subcommand(s) Options: --fix-openssl Get around the OpenSSL 'unsafe legacy renegotiation' error --ignore-tls-errors Ignore TLS errors -h, --help Print help -V, --version Print version ``` > **Tip:** Use `gpclient help <command>` for detailed information on a specific command. #### External Browser Authentication For browser-based authentication with the CLI: **Method 1:** Using sudo with environment preservation: ```bash sudo -E gpclient connect --browser <portal> ``` **Method 2:** Using authentication piping: ```bash gpauth <portal> --browser 2>/dev/null | sudo gpclient connect <portal> --cookie-on-stdin ``` **Browser Options:** - Use `--browser <browser>` to specify a browser (e.g., `firefox`, `chrome`) - Use `--browser remote` for headless servers – this provides a URL you can access from another machine to complete authentication ### Graphical User Interface The GUI application provides an intuitive interface for managing VPN connections. Launch it from your application menu or via the terminal: ```bash gpclient launch-gui ``` > [!Note] > > The GUI version is partially open source. The background service ([gpservice](./apps/gpservice/)) is open source, while the GUI wrapper is proprietary. ## Installation ### Debian / Ubuntu #### Option 1: Install from PPA (Recommended) ```bash sudo add-apt-repository ppa:yuezk/globalprotect-openconnect sudo apt-get update sudo apt-get install globalprotect-openconnect ``` > [!Note] > > **For Linux Mint users:** If you encounter a GPG key error, import the key manually: > ```bash > sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7937C393082992E5D6E4A60453FC26B43838D761 > ``` #### Option 2: Install from DEB Package Download the latest `.deb` package from the [releases](https://github.com/yuezk/GlobalProtect-openconnect/releases) page, then install: ```bash sudo apt install --fix-broken globalprotect-openconnect_*.deb ``` ### Arch Linux / Manjaro #### Option 1: Install from AUR Package: [globalprotect-openconnect-git](https://aur.archlinux.org/packages/globalprotect-openconnect-git/) You can install it using an AUR helper like [`yay`](https://github.com/Jguer/yay): ```bash yay -S globalprotect-openconnect-git ``` #### Option 2: Install from the Official Extra Repository The package is also available in the official Arch Linux Extra repository. Package: [globalprotect-openconnect](https://archlinux.org/packages/extra/x86_64/globalprotect-openconnect/) > [!Note] > > Since the official package does not include the system tray support dependency, you need to install `libappindicator` manually: ```bash sudo pacman -S libappindicator globalprotect-openconnect ``` #### Option 3: Install from Package Download the latest package from the [releases](https://github.com/yuezk/GlobalProtect-openconnect/releases) page, then install: ```bash sudo pacman -U globalprotect-openconnect-*.pkg.tar.zst ``` ### Fedora 38+ / Rawhide #### Install from COPR The package is available on [COPR](https://copr.fedorainfracloud.org/coprs/yuezk/globalprotect-openconnect/) for RPM-based distributions: ```bash sudo dnf copr enable yuezk/globalprotect-openconnect sudo dnf install globalprotect-openconnect ``` ### openSUSE Leap 15.6+ / Tumbleweed #### Install from OBS (openSUSE Build Service) Packages are available on the [openSUSE Build Service](https://build.opensuse.org/package/show/home:yuezk/globalprotect-openconnect). Follow the [installation instructions](https://software.opensuse.org//download.html?project=home%3Ayuezk&package=globalprotect-openconnect) for your distribution. ### Other RPM-based Distributions #### Install from RPM Package Download the latest RPM package from the [releases](https://github.com/yuezk/GlobalProtect-openconnect/releases) page: ```bash sudo rpm -i globalprotect-openconnect-*.rpm ``` ### Gentoo Available via the `guru` and `lamdness` overlays: ```bash sudo eselect repository enable guru sudo emerge --sync guru sudo emerge --ask --verbose net-vpn/GlobalProtect-openconnect ``` ### NixOS This repository includes a flake for NixOS integration. #### Installation Steps 1. Add the flake to your `flake.nix`: ```nix { inputs = { # ... other inputs globalprotect-openconnect.url = "github:yuezk/GlobalProtect-openconnect"; }; outputs = { nixpkgs, ... }@inputs: { nixosConfigurations.<your-host> = nixpkgs.lib.nixosSystem { specialArgs = { inherit inputs; }; modules = [ ./configuration.nix ]; }; }; } ``` 2. Add the package to your `configuration.nix`: ```nix { config, pkgs, inputs, ... }: { # ... other configurations environment.systemPackages = with pkgs; [ # ... other packages ] ++ [ inputs.globalprotect-openconnect.packages.${pkgs.system}.default ]; } ``` 3. Apply the changes: ```bash sudo nixos-rebuild switch ``` ### Other Distributions #### Manual Installation 1. **Install dependencies:** - `webkit2gtk` - `libsecret` - `libayatana-appindicator` or `libappindicator-gtk3` 2. **Download and extract:** Download `globalprotect-openconnect_${version}_${arch}.bin.tar.xz` from the [releases](https://github.com/yuezk/GlobalProtect-openconnect/releases) page: ```bash tar -xJf globalprotect-openconnect_${version}_${arch}.bin.tar.xz ``` 3. **Install:** ```bash sudo make install ``` ## Building from Source You can build the application from source using either a DevContainer (recommended) or a local development environment. ### Method 1: Using DevContainer (Recommended) This project includes a DevContainer configuration that provides a consistent, reproducible build environment with all dependencies pre-installed. #### Prerequisites - [Docker](https://docs.docker.com/get-docker/) - [Visual Studio Code](https://code.visualstudio.com/) (optional, for IDE support) - [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) (if using VS Code) #### Build Steps 1. **Clone the repository:** ```bash git clone https://github.com/yuezk/GlobalProtect-openconnect.git cd GlobalProtect-openconnect git submodule update --init --recursive ``` 2. **Build the DevContainer image:** ```bash docker build -t gpoc-devcontainer .devcontainer/ ``` 3. **Build the project:** ```bash docker run --privileged --cap-add=NET_ADMIN --device=/dev/net/tun \ -v "$(pwd)":/workspace -w /workspace gpoc-devcontainer \ bash -c "export PATH=/usr/local/cargo/bin:\$PATH && make build" ``` 4. **Locate build artifacts:** The compiled binaries will be available in `target/release/`: - `gpclient` – CLI client - `gpservice` – Background service - `gpauth` – Authentication helper - `gpgui-helper` – GUI helper #### Alternative: Using VS Code 1. Open the project in VS Code 2. When prompted, click "Reopen in Container" (or run **Dev Containers: Reopen in Container**) 3. Once the container is ready, open a terminal and run: ```bash make build ``` ### Method 2: Local Development Build #### Prerequisites - [Rust 1.85 or later](https://www.rust-lang.org/tools/install) - [Tauri dependencies](https://tauri.app/start/prerequisites/) - `libopenconnect-dev` (or `openconnect-devel` on RPM-based systems) - `pkexec` and `gnome-keyring` (or `pam_kwallet` on KDE) - `nodejs` and `pnpm` (optional if using pre-built release tarballs with `BUILD_FE=0`) #### Build Steps 1. **Download source code:** Download `globalprotect-openconnect-${version}.tar.gz` from the [releases](https://github.com/yuezk/GlobalProtect-openconnect/releases) page. 2. **Extract and build:** ```bash tar -xzf globalprotect-openconnect-${version}.tar.gz cd globalprotect-openconnect-${version} make build BUILD_FE=0 ``` 3. **Install:** ```bash sudo make install ``` > **Note:** `DESTDIR` is not currently supported. ### Testing Your Build Verify the CLI client is working correctly: ```bash ./target/release/gpclient --help ``` ### Build Options - `BUILD_GUI=0` – Build CLI components only (excludes GUI) - `BUILD_FE=0` – Skip frontend build (uses pre-built assets) - `OFFLINE=1` – Build in offline mode using vendored dependencies ## Frequently Asked Questions ### Q: How do I resolve the "Secure Storage not ready" error? **Solution 1:** Update to version 2.2.0 or later, which includes a file-based storage fallback. **Solution 2:** Install the `gnome-keyring` package and restart your system. See related issues: [#321](https://github.com/yuezk/GlobalProtect-openconnect/issues/321), [#316](https://github.com/yuezk/GlobalProtect-openconnect/issues/316) ### Q: How do I fix the "cannot open display" error when using CLI? If you encounter `(gpauth:18869): Gtk-WARNING **: 10:33:37.566: cannot open display:`, try running the command with `sudo -E`: ```bash sudo -E gpclient connect <portal> ``` See related issue: [#316](https://github.com/yuezk/GlobalProtect-openconnect/issues/316) ## Licensing ### Trial and Pricing The **CLI version** is completely free and open source. The **GUI version** is a paid application with a **10-day trial period** after installation. ### Open Source Licenses This project consists of multiple components, each with its own license: | Component | Type | License | |-----------|------|---------| | [gpapi](./crates/gpapi) | Crate | [MIT](./crates/gpapi/LICENSE) | | [openconnect](./crates/openconnect) | Crate | [GPL-3.0](./crates/openconnect/LICENSE) | | [common](./crates/common) | Crate | [GPL-3.0](./crates/common/LICENSE) | | [auth](./crates/auth) | Crate | [GPL-3.0](./crates/auth/LICENSE) | | [gpservice](./apps/gpservice) | Application | [GPL-3.0](./apps/gpservice/LICENSE) | | [gpclient](./apps/gpclient) | Application | [GPL-3.0](./apps/gpclient/LICENSE) | | [gpauth](./apps/gpauth) | Application | [GPL-3.0](./apps/gpauth/LICENSE) | | [gpgui-helper](./apps/gpgui-helper) | Application | [GPL-3.0](./apps/gpgui-helper/LICENSE) | ", Assign "at most 3 tags" to the expected json: {"id":"7065","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"