Skip to main content

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

PyPI AUR CI License Python Downloads

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
👤 Multi-Profile Save and switch between named VPN configurations
📊 Status TUI Live connection status with traffic stats (rich optional)
⌨️ Shell Completion Tab completion for bash, zsh, and fish
🔀 Split-Tunnel Route only specific subnets through the VPN
🔑 Bitwarden TOTP Fetch TOTP from Bitwarden CLI (bw)
🔔 Notifications Desktop notifications for VPN events
🧙 Setup Wizard Interactive setup command for easy configuration

📦 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]"

# With connection status TUI (rich)
pip install "openconnect-saml[tui]"

# All extras (GUI + Chrome + FIDO2 + TUI)
pip install "openconnect-saml[gui,chrome,fido2,tui]"

# Arch Linux (AUR)
yay -S openconnect-saml
# or
paru -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:

  1. Auto: HTTP requests + form parsing → submits credentials without a browser
  2. 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:

  1. Install 2FAuth and add your VPN TOTP account
  2. Create a Personal Access Token in 2FAuth (Settings → OAuth → Personal Access Tokens)
  3. Note the account ID (visible in the URL when editing the account, or via API)
  4. Configure openconnect-saml with --totp-source 2fauth or totp_source = "2fauth" in config

⚠️ HTTPS is strongly recommended for the 2FAuth URL. HTTP connections will trigger a warning.

Bitwarden TOTP Provider

Fetch TOTP codes from your Bitwarden vault via the bw CLI:

# Via CLI flags
openconnect-saml --server vpn.example.com --headless --user user@domain.com \
  --totp-source bitwarden \
  --bw-item-id YOUR_VAULT_ITEM_UUID

# Or via config file (~/.config/openconnect-saml/config.toml)
[credentials]
username = "user@example.com"
totp_source = "bitwarden"

[bitwarden]
item_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Setup:

  1. Install the Bitwarden CLI (bw)
  2. Log in: bw login and unlock: bw unlock → export BW_SESSION
  3. Find your item ID: bw list items --search "VPN" → note the id field
  4. Configure: --totp-source bitwarden --bw-item-id <uuid>

Split-Tunnel Routing

Route only specific subnets through the VPN (split-tunneling):

# Include specific routes
openconnect-saml connect work --route 10.0.0.0/8 --route 172.16.0.0/12

# Exclude routes (bypass VPN for these)
openconnect-saml connect work --no-route 192.168.0.0/16

# Combine both
openconnect-saml connect work --route 10.0.0.0/8 --no-route 10.0.99.0/24

Or configure per-profile:

[profiles.work]
server = "vpn.company.com"
routes = ["10.0.0.0/8", "172.16.0.0/12"]
no_routes = ["192.168.0.0/16"]

Desktop Notifications

Get notified about VPN events (connect, disconnect, reconnect, errors):

# Enable via CLI
openconnect-saml connect work --notify

# Or in config
notifications = true

Supports: Linux (notify-send), macOS (osascript), fallback (terminal bell).

Setup Wizard

Interactive configuration wizard for first-time setup:

openconnect-saml setup

Guides through: server URL, username, TOTP provider, browser mode, auto-reconnect, and notifications. Saves a named profile.

Multi-Profile

Save named VPN configurations and switch between them:

# Add profiles
openconnect-saml profiles add work --server vpn.company.com --user user@company.com
openconnect-saml profiles add lab --server lab-vpn.company.com --user admin

# List profiles
openconnect-saml profiles list

# Connect to a profile
openconnect-saml connect work
openconnect-saml connect lab

# Override server from profile
openconnect-saml connect work --server alt-vpn.company.com

# Remove a profile
openconnect-saml profiles remove lab

# Legacy mode still works (backwards-compatible)
openconnect-saml --server vpn.example.com

Config file format:

[profiles.work]
server = "vpn.company.com"
user_group = "employees"
name = "Work VPN"

[profiles.work.credentials]
username = "user@company.com"
totp_source = "2fauth"

[profiles.lab]
server = "lab-vpn.company.com"
name = "Lab VPN"

[profiles.lab.credentials]
username = "admin"

Connection Status

View live VPN connection status:

# One-shot status
openconnect-saml status

# Live-updating status (refreshes every 2s)
openconnect-saml status --watch

Install with rich for a formatted table display:

pip install "openconnect-saml[tui]"

Shell Completion

# Generate completion scripts
openconnect-saml completion bash
openconnect-saml completion zsh
openconnect-saml completion fish

# Auto-install to default locations
openconnect-saml completion install

Advanced Options

--headless              # No browser, terminal-only authentication
--browser BACKEND       # Browser backend: qt, chrome, headless
--totp-source SOURCE    # TOTP provider: local, 2fauth, or bitwarden
--2fauth-url URL        # 2FAuth instance URL
--2fauth-token TOKEN    # 2FAuth Personal Access Token
--2fauth-account-id ID  # 2FAuth account ID for VPN TOTP
--bw-item-id UUID       # Bitwarden vault item ID for TOTP
--reconnect             # Auto-reconnect on VPN drops
--max-retries N         # Max reconnection attempts (default: unlimited)
--route CIDR            # Include route in VPN tunnel (repeatable)
--no-route CIDR         # Exclude route from VPN tunnel (repeatable)
--notify                # Enable desktop notifications
--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"

# Named profiles (optional)
[profiles.work]
server = "vpn.company.com"
user_group = "employees"
name = "Work VPN"

[profiles.work.credentials]
username = "user@company.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 .

📎 Links

Resource URL
GitHub mschabhuettl/openconnect-saml
PyPI pypi.org/project/openconnect-saml
AUR aur.archlinux.org/packages/openconnect-saml
Releases GitHub Releases
Issues Bug Tracker
Changelog CHANGELOG.md
License GPL-3.0
Original vlaci/openconnect-sso

🙏 Credits

📄 License

GPL-3.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openconnect_saml-0.6.0.tar.gz (89.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openconnect_saml-0.6.0-py3-none-any.whl (68.9 kB view details)

Uploaded Python 3

File details

Details for the file openconnect_saml-0.6.0.tar.gz.

File metadata

  • Download URL: openconnect_saml-0.6.0.tar.gz
  • Upload date:
  • Size: 89.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for openconnect_saml-0.6.0.tar.gz
Algorithm Hash digest
SHA256 4d720d7f121112c37fbb897b03b6915416745bdbeff7925b24bfe794efd88a2f
MD5 676703586b90a04a127d97d666529784
BLAKE2b-256 49cc60245f574ed69ba0b8cd9ba8a7d7600fe58346f5534c02f2b33f68fcbbf7

See more details on using hashes here.

Provenance

The following attestation bundles were made for openconnect_saml-0.6.0.tar.gz:

Publisher: publish.yml on mschabhuettl/openconnect-saml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openconnect_saml-0.6.0-py3-none-any.whl.

File metadata

File hashes

Hashes for openconnect_saml-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2f1f3376839f564f2f9a70e3e6284347e6d77abe12010753e850561bbf13127e
MD5 0c75ab7e7a16849e680c7f86c8fd8d62
BLAKE2b-256 3cde2ea9cfd19fb063e0839bb91d42fe92fbcac9e9a4b458b053694d97ae49c9

See more details on using hashes here.

Provenance

The following attestation bundles were made for openconnect_saml-0.6.0-py3-none-any.whl:

Publisher: publish.yml on mschabhuettl/openconnect-saml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page