Self-hosted service
yuezk/GlobalProtect-openconnect avatar
yuezk/GlobalProtect-openconnect

GlobalProtect-openconnect: a native Linux client for Palo Alto's portal, and where its source stops

A GlobalProtect VPN client for Linux, written in Rust, based on OpenConnect and Tauri, supports SSO with MFA, YubiKey, and client certificate authentication, etc.

2,287 stars264 forksRustGPL-3.0

At a glance

What is it?
A Rust workspace that drives OpenConnect against GlobalProtect gateways, with a fully open CLI and a partially closed GUI wrapper.
Who is it for?
GlobalProtect-openconnect is the practical answer for a Linux user whose employer issues GlobalProtect credentials and who does not want to run the vendor's Java client. The CLI is the part to judge the project by: it is fully open source, feature-comparable to the GUI, and split into separate connect, service and auth binaries in a Cargo workspace that declares Rust 1.89 and edition 2024.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A client for a specific portal, not a general VPN tool

The repository description is precise about scope: a GlobalProtect VPN client for Linux, written in Rust, based on OpenConnect and Tauri, supporting SSO with MFA, YubiKey and client certificate authentication. Read carefully, the subject of the project is Palo Alto Networks' GlobalProtect, and the technology is a native reimplementation of the client side of that protocol.

That distinction matters for anyone shopping for a Linux VPN tool. OpenConnect itself is a general-purpose SSL VPN client, and plenty of Linux setups never need this repository at all. You need it when your gateway speaks the GlobalProtect portal and gateway protocol and the vendor's own Linux offering is a Java application you would rather not run. The README credits gp-saml-gui as the inspiration, which places the project in the lineage of tools that wrap a headless client with something usable on a modern desktop.

The dependency picture in Cargo.toml confirms the shape of the work. The workspace pulls in reqwest with native-tls, json, form and query features, plus openssl, pem, xmltree, urlencoding and serde_urlencoded. That is a TLS HTTP client with XML parsing and form encoding, which is exactly the toolkit for driving a portal that returns XML and expects form posts. clap with derive provides the command line surface, axum serves a local endpoint, and tauri version 2 backs the graphical wrapper. uzers, netdev and sysinfo handle the privileged and network-facing concerns that a VPN client needs.

So the project is not implementing a VPN protocol from scratch. It is implementing the GlobalProtect conversation, reusing OpenConnect's TLS and transport behaviour, and shipping a Linux-native interface around it.

Three binaries and one workspace

The Cargo workspace is the clearest picture of what the project contains. Its members are declared as the crates directory plus four explicit application paths:

toml
members = [
  "crates/*",
  "apps/gpclient",
  "apps/gpservice",
  "apps/gpauth",
  "apps/gpgui-helper/src-tauri",
]

The three application binaries map onto the roles the README describes. gpclient is the command line interface and can hand off to the others. gpservice is the background service the README calls out as open source. gpauth is the authentication helper, and it is the binary that owns the browser flow. gpgui-helper is the Tauri wrapper for the graphical application.

The shared workspace package block pins the toolchain expectations that everything else inherits:

toml
[workspace.package]
rust-version = "1.89"
version = "2.6.5"
authors = ["Kevin Yue <[email protected]>"]
edition = "2024"
license = "GPL-3.0"

The Makefile repeats rust 1.89 as its default RUST_VERSION and offers an IGNORE_RUST_VERSION escape hatch that appends `--ignore-rust-version` to the cargo invocation. That pairing is deliberate: the build system expects a specific toolchain, knows how to bypass the check when needed, and exposes it as a make variable rather than something you discover by reading a failure.

The release profile is tuned for distribution rather than for debuggability. opt-level is set for size, link-time optimization is on, codegen-units is 1, panic is set to abort, and symbols are stripped. Every one of those trades build time and diagnostics for a smaller binary, which is the right trade for a VPN client that ships as a distribution package across a dozen Linux families.

Reading the CLI surface as the feature list

The README's basic command block is the most honest description of the CLI available, and it is worth reading as an interface contract rather than decoration:

bash
Usage: gpclient [OPTIONS] <COMMAND>

Commands:
  connect     Connect to a portal server
  disconnect  Disconnect from the server
  launch-gui  Launch the GUI
  hip         Generate HIP report
  help        Print this message or the help of the given subcommand(s)

The commands are minimal and the global options carry most of the operational weight. `--fix-openssl` selects an extended compatibility mode for OpenSSL operations to support a broader range of systems and formats. `--ignore-tls-errors` disables TLS error checking. `--log-format` takes text or json, and the README is explicit about why that matters: JSON is intended for non-interactive consumers while interactive prompts remain text. Verbosity follows the familiar repeatable pattern, with -v for debug and -vv for trace, and -q for warnings and -qq for errors.

That log-format option is a small design decision with real consequences. A VPN client runs unattended in scripts, in launchers, and in monitoring, and text prompts interleaved with log lines are unparseable. Splitting machine-readable logging from interactive prompts means a supervisor can consume the JSON stream without trying to reconstruct intent from prompts and answers on the same channel.

The `hip` subcommand deserves a note. A HIP report is the health information payload a GlobalProtect portal expects, so generating one locally is a prerequisite step rather than a diagnostic. Its presence as a first-class command tells you the project implements the portal handshake fully enough to satisfy the portal's health checks rather than only establishing a tunnel.

SSO is the hard part, and gpauth is how it is solved

Corporate GlobalProtect deployments authenticate through SSO, which means a browser, an identity provider, and often a second factor. On Linux that is the failure point for most clients. The project's answer is to split authentication into its own binary, gpauth, and give it two documented ways to deliver the resulting session to the privileged client.

The first method preserves the environment across a sudo boundary:

bash
sudo gpclient connect --browser <portal>

The second pipes the authentication result in, redirecting the helper's own chatter away:

bash
gpauth <portal> --browser 2>/dev/null | sudo gpclient connect <portal> --cookie-on-stdin

The second form is the more interesting one, because it separates the unprivileged step that talks to the browser from the privileged step that configures the tunnel. Piping means the authentication output never has to be written to a file that a later process might read.

Browser selection is exposed as four modes, and the fourth is the one that matters for anything without a desktop. `--browser` auto-selects Chrome, then Firefox, then the system default. `--browser default` forces the system default. `--browser <browser>` names one explicitly. `--browser remote` is documented for headless servers, providing a URL you open from another machine to complete authentication. On a headless server that is the difference between the client being usable and not being usable at all, since there is no local browser to launch and no display to launch it into.

The feature list also names non-SSO paths, FIDO2 such as a YubiKey, and client certificate authentication, alongside multi-portal support, direct gateway connection to bypass portal selection, auto-connect at system startup, and a system tray icon that on GNOME requires the appindicator shell extension.

The source boundary the README draws

There is one line in the README that deserves to be read twice, because it changes what the repository license covers. The CLI is described as fully open source and feature-rich, providing nearly identical functionality to the GUI version. The GUI is described as partially open source: the background service at apps/gpservice is open source, while the GUI wrapper is proprietary.

Set that against the repository's license metadata, which is GPL-3.0, and the same value appears in the Cargo workspace package block. Two facts sit side by side. The repository is licensed GPL-3.0 and the Makefile builds everything from it, including a gpgui-helper workspace member and a GPGUI_BINARY path pointing outside the source tree. The README states plainly that the GUI wrapper that a user launches is not part of the open source distribution.

Neither statement makes the other wrong, and the way to resolve them is a question about which artifact you are evaluating. If you are assessing the protocol handling, the portal logic and the authentication flow, all of that lives in gpclient, gpservice, gpauth and the shared crates, all under GPL-3.0, all in the repository. If you are assessing the window that appears when you type gpclient launch-gui, the README says that layer is proprietary, and reading its source is not something the repository offers.

The tree also contains a .gitmodules file, and the Makefile's GPGUI_BINARY default points at ../gpgui/target/release/gpgui, a sibling checkout rather than a subdirectory of this repository. That is consistent with the README's account and gives you a concrete way to check it yourself: look at how the GUI artifact enters your build, and see whether it came from this tree or from outside it.

Packaging breadth, and what to install

The distribution coverage is the most unusual thing about this project. The README documents install paths for Debian and Ubuntu, Arch Linux and Manjaro, Fedora 38 and Rawhide, openSUSE Leap 15.6 and Tumbleweed, other RPM-based distributions, Alpine Linux, Gentoo, NixOS, an official Docker image, and a catch-all for other distributions. That is a lot of packaging surface to keep working, and the presence of flake.nix and flake.lock alongside a Makefile confirms Nix is a first-class path rather than an afterthought.

On Ubuntu the recommended route is a personal package archive:

bash
sudo add-apt-repository ppa:yuezk/globalprotect-openconnect
sudo apt-get update
sudo apt-get install globalprotect-openconnect

RPM-based distributions use COPR instead, with `sudo dnf copr enable yuezk/globalprotect-openconnect` followed by installing the package. Arch has two routes, an AUR helper invocation of `yay -S globalprotect-openconnect-git`, and the official Extra repository, where the README notes the official package omits the system tray dependency and therefore needs `libappindicator` installed separately. That caveat is the kind of detail that only appears in documentation written by someone who has debugged it.

Two warnings deserve placement before any of these commands. The README says that for older Linux distributions you should use v2.3.13 instead of the latest release, because that older tag ships release assets for the common distro families. And the releases page contains a tag named snapshot whose own notes say it is for testing purposes only and not recommended for production use, with instructions to uninstall the previous version first and download the package from the assets section.

That snapshot tag is worth a second look because it is dated 2024-06-11, two years before v2.6.5 on 2026-08-05. A tag that is not a version and is not for production sitting on a releases page is an easy thing to install by accident. Read the tag name before you read the download button.

Recent fixes that describe the fragile parts

The most recent releases are small, and their content points at where this client is genuinely difficult: the seam between an HTTP portal that speaks in forms and a local client that has to behave predictably.

Release v2.6.5, dated 2026-08-05, fixed duplicate tray icons caused by both the Tauri configuration and the application code each creating one, and fixed a potential crash when VPN progress messages contain printf-style format specifiers. The first is a symptom of the wrapper boundary described earlier, since two layers both owning a tray icon is exactly what you would expect when a proprietary wrapper sits over open source service code. The second is a logging bug with an outsized failure mode: a progress message from the gateway carrying a percent sign would abort the process.

Release v2.6.4, dated 2026-06-25, fixed gateway login MFA one-time-password retries sending duplicate `passwd` form fields, which could cause authentication to fail with HTTP 512 after 2.6.0. That is a precise and useful description of an authentication bug: the retry logic resubmitted a form field, the portal rejected the malformed request with an unusual status code, and the visible symptom was an MFA prompt that could never succeed.

There is also a version-pinning story worth noting in the dependency list. One entry carries an explanatory comment:

toml
# Use 0.36 as newer versions requires Rust 1.88+
sysinfo = "0.36"

The comment's reasoning and the declared rust-version of 1.89 are close enough that the constraint looks like it could be revisited rather than being permanent, which is a hint about how the project tracks toolchain drift. Alongside it, specta is pinned with an exact version requirement of 2.0.0-rc.20, a release candidate held in place deliberately. Both choices are the kind of thing that tells you the dependency set is curated rather than auto-updated wholesale.

With 33 open issues and a push recorded on 2026-09-28, the project is clearly active. The open issue count is also a fair signal that the maintainer is carrying a support surface across many distributions at once, which is the honest cost of the breadth described above.

Editorial conclusion

GlobalProtect-openconnect is the practical answer for a Linux user whose employer issues GlobalProtect credentials and who does not want to run the vendor's Java client. The CLI is the part to judge the project by: it is fully open source, feature-comparable to the GUI, and split into separate connect, service and auth binaries in a Cargo workspace that declares Rust 1.89 and edition 2024. The GUI deserves a separate reading, because the README states the wrapper is proprietary while the background service beneath it is not. Check the CLI first, read the snapshot warning before installing from a releases page, and treat the pinned dependency versions in Cargo.toml as deliberate constraints rather than neglect.

Frequently asked questions

Is the GlobalProtect-openconnect GUI fully open source?

No, and the README says so directly. The background service at apps/gpservice is open source, as is the gpclient CLI, which the README describes as providing nearly identical functionality. The GUI wrapper is proprietary. The repository is licensed GPL-3.0, and the Makefile references a GPGUI_BINARY path outside the source tree, so the boundary is visible in the build files as well as the documentation.

How do I use SSO authentication from a headless Linux server?

Use the remote browser mode. The README documents `--browser remote` for headless servers, which provides a URL you can open from another machine to complete authentication. The authentication helper gpauth can also be piped into the client directly with gpauth <portal> --browser piped into sudo gpclient connect <portal> --cookie-on-stdin.

Can I install the latest release on an older Linux distribution?

The README advises using v2.3.13 instead of the latest release for older distributions, since that tag provides release assets for common distro families including .deb, .pkg.tar.zst, .rpm and generic Linux tarballs. It is also worth noting that the releases page includes a tag named snapshot dated 2024-06-11 whose own notes say it is for testing purposes only and not recommended for production use.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. README
  4. Releases
  5. yuezk/GlobalProtect-openconnect on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/yuezk-globalprotect-openconnect.svg)](https://hysenlabs.com/projects/yuezk-globalprotect-openconnect)