OpenConnect wrapper with Azure AD (SAML) SSO support for Cisco SSL-VPNs
Project description
🔐 openconnect-saml
OpenConnect wrapper with Azure AD / SAML authentication for Cisco AnyConnect VPNs
Maintained fork of vlaci/openconnect-sso with improvements from kowyo/openconnect-lite
✨ Features
| Feature | Description |
|---|---|
| 🖥️ GUI Mode | Embedded Qt6 WebEngine browser with auto-fill |
| 🌐 Chrome Browser | Playwright-based Chromium backend — headless or visible |
| 🤖 Headless Mode | No display needed — works on servers, containers, SSH |
| 🔑 Auto-Login | Username, password, and TOTP auto-injection |
| 🌍 2FAuth | Fetch TOTP from a remote 2FAuth instance |
| 🔒 Keyring | Credentials stored securely (with in-memory fallback) |
| 📱 MFA Support | TOTP, Microsoft Authenticator number matching |
| 🔐 FIDO2/YubiKey | Hardware security key support for WebAuthn challenges |
| 🛡️ Security | XXE protection, no credential logging, safe config permissions |
| 🔄 Auto-Reconnect | Automatic re-authentication and reconnection on VPN drops |
| ⚙️ Systemd Service | Install as a persistent system service with one command |
| 🌐 Proxy | SOCKS and HTTP proxy support |
| 📜 Certificates | Client certificate handling with auto-fallback |
| 🐳 Docker-ready | Headless mode for containerized deployments |
📦 Installation
# Headless (no GUI dependencies)
pip install openconnect-saml
# With GUI browser (Qt6 WebEngine)
pip install "openconnect-saml[gui]"
# With Chrome/Chromium browser (Playwright)
pip install "openconnect-saml[chrome]"
playwright install chromium
# With FIDO2/YubiKey support
pip install "openconnect-saml[fido2]"
# Arch Linux (AUR)
yay -S openconnect-saml
Requires: Python ≥ 3.10 and OpenConnect in PATH
🚀 Quick Start
# GUI mode (default)
openconnect-saml --server vpn.example.com --user user@domain.com
# Headless mode (servers, containers, SSH)
openconnect-saml --server vpn.example.com --user user@domain.com --headless
📖 Usage
GUI Mode
openconnect-saml --server vpn.example.com
openconnect-saml --server vpn.example.com/usergroup
openconnect-saml --profile /opt/cisco/anyconnect/profile
Headless Mode
No display server required — perfect for servers, CI/CD, and containers:
# Auto-authenticate with saved credentials
openconnect-saml --server vpn.example.com --headless --user user@example.com
# Output auth cookie for scripting
openconnect-saml --server vpn.example.com --headless --authenticate json
How it works:
- Auto: HTTP requests + form parsing → submits credentials without a browser
- Fallback: If auto-auth fails (CAPTCHA, unsupported MFA) → prints URL + starts local callback server → authenticate in any browser
Chrome/Chromium Browser
Use Playwright-based Chromium instead of Qt WebEngine:
# Visible Chrome window
openconnect-saml --server vpn.example.com --browser chrome
# Headless Chrome (no display needed)
openconnect-saml --server vpn.example.com --browser headless
Auto-Reconnect
Keep the VPN alive — automatically re-authenticates and reconnects on drops:
# Unlimited retries with backoff (30s, 60s, 120s, 300s)
openconnect-saml --server vpn.example.com --headless --reconnect
# Limit to 5 reconnection attempts
openconnect-saml --server vpn.example.com --headless --reconnect --max-retries 5
Systemd Service
Install as a persistent system service:
# Install and enable
sudo openconnect-saml service install --server vpn.example.com --user user@domain.com
# Manage
sudo openconnect-saml service start --server vpn.example.com
sudo openconnect-saml service stop --server vpn.example.com
openconnect-saml service status
openconnect-saml service logs --server vpn.example.com
# Remove
sudo openconnect-saml service uninstall --server vpn.example.com
FIDO2/YubiKey
Hardware security key support for WebAuthn challenges during SAML authentication:
# FIDO2 is detected automatically during auth flows
# When a WebAuthn challenge is encountered:
# → Terminal prompt: "Touch your security key..."
# → PIN prompt if required
pip install "openconnect-saml[fido2]"
2FAuth TOTP Provider
Fetch TOTP codes from a 2FAuth instance instead of storing secrets locally:
# Via CLI flags
openconnect-saml --server vpn.example.com --headless --user user@domain.com \
--totp-source 2fauth \
--2fauth-url https://2fauth.example.com \
--2fauth-token YOUR_PERSONAL_ACCESS_TOKEN \
--2fauth-account-id 42
# Or via config file (~/.config/openconnect-saml/config.toml)
[credentials]
username = "user@example.com"
totp_source = "2fauth"
[2fauth]
url = "https://2fauth.example.com"
token = "eyJ0eXAiOiJKV1QiLC..."
account_id = 42
Setup:
- Install 2FAuth and add your VPN TOTP account
- Create a Personal Access Token in 2FAuth (Settings → OAuth → Personal Access Tokens)
- Note the account ID (visible in the URL when editing the account, or via API)
- Configure openconnect-saml with
--totp-source 2fauthortotp_source = "2fauth"in config
⚠️ HTTPS is strongly recommended for the 2FAuth URL. HTTP connections will trigger a warning.
Advanced Options
--headless # No browser, terminal-only authentication
--browser BACKEND # Browser backend: qt, chrome, headless
--totp-source SOURCE # TOTP provider: local (default) or 2fauth
--2fauth-url URL # 2FAuth instance URL
--2fauth-token TOKEN # 2FAuth Personal Access Token
--2fauth-account-id ID # 2FAuth account ID for VPN TOTP
--reconnect # Auto-reconnect on VPN drops
--max-retries N # Max reconnection attempts (default: unlimited)
--no-sudo # Don't use sudo (for --script-tun)
--ssl-legacy # Enable legacy SSL renegotiation
--csd-wrapper PATH # CSD/hostscan wrapper script
--timeout SECONDS # HTTP timeout (default: 30)
--window-size WxH # Browser window size (default: 800x600)
--on-connect CMD # Run command after VPN connects
--on-disconnect CMD # Run command after VPN disconnects
--reset-credentials # Clear saved keyring entries
--authenticate FORMAT # Auth only, output cookie (json|shell)
🐳 Docker
FROM python:3.12-slim
RUN pip install openconnect-saml && \
apt-get update && apt-get install -y openconnect && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["openconnect-saml", "--headless"]
docker run -it --cap-add=NET_ADMIN --device=/dev/net/tun \
vpn-client --server vpn.example.com --user user@example.com
⚙️ Configuration
Config file: ~/.config/openconnect-saml/config.toml
[default_profile]
server = "vpn.example.com"
user_group = ""
name = "My VPN"
[credentials]
username = "user@example.com"
Auto-fill Rules
Custom rules per URL pattern:
[[auto_fill_rules."https://*"]]
selector = "input[type=email]"
fill = "username"
[[auto_fill_rules."https://*"]]
selector = "input[name=passwd]"
fill = "password"
[[auto_fill_rules."https://*"]]
selector = "input[id=idTxtBx_SAOTCC_OTC]"
fill = "totp"
TOTP Configuration
For automated TOTP, add your secret to the config:
[credentials]
username = "user@example.com"
# TOTP secret is stored in keyring on first use
# Or set via: openconnect-saml --user user@example.com (prompts for secret)
To clear stored credentials:
openconnect-saml --user user@example.com --reset-credentials
🔄 Migrating from openconnect-sso
# Replace openconnect-sso with openconnect-saml
pip uninstall openconnect-sso
pip install openconnect-saml
# Rename config directory
mv ~/.config/openconnect-sso ~/.config/openconnect-saml
# Same CLI, new name
openconnect-saml --server vpn.example.com
🛠️ Development
git clone https://github.com/mschabhuettl/openconnect-saml
cd openconnect-saml
pip install -e ".[dev]"
pytest -v
ruff check .
🙏 Credits
- László Vaskó (vlaci) — original openconnect-sso
- Kowyo — openconnect-lite modernization
- Community contributors for issues, PRs, and testing
📄 License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openconnect_saml-0.4.0.tar.gz.
File metadata
- Download URL: openconnect_saml-0.4.0.tar.gz
- Upload date:
- Size: 68.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e46965afccadc2ed0ebbeab89ebea22fd70242ea0afcb579df1ee9d04d0b573
|
|
| MD5 |
ce38a84ad58636c5bb75b1f41aa9580a
|
|
| BLAKE2b-256 |
1ca1f3b8dd85445ae25de93bf48bd6948a6660a31757cc342fff3405b41a074a
|
Provenance
The following attestation bundles were made for openconnect_saml-0.4.0.tar.gz:
Publisher:
publish.yml on mschabhuettl/openconnect-saml
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openconnect_saml-0.4.0.tar.gz -
Subject digest:
5e46965afccadc2ed0ebbeab89ebea22fd70242ea0afcb579df1ee9d04d0b573 - Sigstore transparency entry: 1202386982
- Sigstore integration time:
-
Permalink:
mschabhuettl/openconnect-saml@d9ab7488216b567e69438ef2945da90639e199eb -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/mschabhuettl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d9ab7488216b567e69438ef2945da90639e199eb -
Trigger Event:
push
-
Statement type:
File details
Details for the file openconnect_saml-0.4.0-py3-none-any.whl.
File metadata
- Download URL: openconnect_saml-0.4.0-py3-none-any.whl
- Upload date:
- Size: 54.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2887c7c075dff5e7abc16aa3fc021e9d2ff1c7f70647d3a0a836e69e7d5a2f4
|
|
| MD5 |
1bb455fda559535484b49ea3a4cbe9dd
|
|
| BLAKE2b-256 |
fc4ff2ec233663d17be9fd4c2c2c367fd13b6cdc624f121357433307157b3794
|
Provenance
The following attestation bundles were made for openconnect_saml-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on mschabhuettl/openconnect-saml
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openconnect_saml-0.4.0-py3-none-any.whl -
Subject digest:
b2887c7c075dff5e7abc16aa3fc021e9d2ff1c7f70647d3a0a836e69e7d5a2f4 - Sigstore transparency entry: 1202386992
- Sigstore integration time:
-
Permalink:
mschabhuettl/openconnect-saml@d9ab7488216b567e69438ef2945da90639e199eb -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/mschabhuettl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d9ab7488216b567e69438ef2945da90639e199eb -
Trigger Event:
push
-
Statement type: