OpenConnect — sing-box
sing-box provides an OpenConnect client endpoint (type: "openconnect"). It connects to Cisco AnyConnect, Palo Alto GlobalProtect, Fortinet SSL VPN, F5 BIG-IP, Pulse Connect Secure and Juniper Network Connect servers and exposes the VPN as a routable endpoint carrying TCP, UDP and ICMP. A companion openconnect DNS server resolves names through the DNS settings the VPN server pushes.
Endpoint, client only
OpenConnect sits under the root endpoints[] array, like WireGuard and Tailscale, and is referenced from routing rules by its tag. Only the client side is implemented.
Endpoint options
type: "openconnect" under endpoints[]:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
system | bool | false | true | false | Use a real system interface instead of sing-box's internal network stack. Needs privileges and must not clash with an existing interface. |
name | string | (auto) | <interface name> | Interface name when system is enabled. An oc-prefixed name is generated by default. |
udp_timeout | badoption.Duration | 5m | <duration> | UDP NAT session expiry. |
udp_mapping | UDPNATBehavior | endpoint_independent | endpoint_independent | address_dependent | address_and_port_dependent | UDP NAT mapping: reuse one mapping per source for every destination, or split it per destination address / address and port. |
udp_filtering | UDPNATBehavior | endpoint_independent | endpoint_independent | address_dependent | address_and_port_dependent | UDP NAT filtering: accept replies from any remote, or only from addresses / address-and-port pairs already contacted. |
udp_nat_max | uint32 | 0 | <count> | Maximum number of UDP NAT sessions; the least recently used one is closed at the limit. 0 means 4096 on iOS and 4096–16384 (by total memory) elsewhere. |
server | string | (required) | <hostname or https:// URL> | VPN server HTTPS URL; https:// is added if missing. User info, query strings and fragments are not supported. |
flavor | string | anyconnect | anyconnect | gp | fortinet | f5 | pulse | nc | Server family: Cisco AnyConnect, Palo Alto GlobalProtect, Fortinet, F5 BIG-IP, Pulse Connect Secure, Juniper Network Connect. |
username | string | (unset) | <string> | Fills matching username fields of the authentication form. |
password | string | (unset) | <string> | Fills matching password fields of the authentication form. |
auth_group | string | (unset) | <string> | Preselects a matching group, realm, domain or gateway choice where the flavor supports it. |
cookie | string | (unset) | <session cookie> | Existing authenticated session, tried before prompting for credentials. Format depends on the flavor (e.g. webvpn for AnyConnect, SVPNCOOKIE for Fortinet, DSID for Network Connect); normal login is attempted if the server rejects it. |
token | *OpenConnectTokenOptions | (unset) | OpenConnectTokenOptions | Software token (TOTP, HOTP, RSA SecurID) or OIDC access token used to answer token prompts or Bearer authentication. See below. |
reported_os | string | (from platform) | linux | linux-64 | win | mac-intel | android | apple-ios | OS identity reported to AnyConnect, GlobalProtect and Pulse servers. Defaults to the running platform. |
user_agent | string | (flavor-specific) | <string> | User agent reported to the server. Defaults: an AnyConnect-compatible OpenConnect agent (AnyConnect, Network Connect, Pulse, F5), PAN GlobalProtect, Mozilla/5.0 SV1 (Fortinet). |
version | string | v9.21 | <string> | Client version reported separately from user_agent; currently used by AnyConnect XML authentication. |
local_hostname | string | (system hostname) | <string> | Hostname reported to the server; localhost if the system hostname is unavailable. |
mobile | *OpenConnectMobileOptions | (unset) | OpenConnectMobileOptions | Report an AnyConnect mobile-client identity. When set, all three sub-fields are required. See below. |
csd | *OpenConnectCSDOptions | (built-in) | { wrapper_path } | AnyConnect CSD / host-scan handling. Built in by default; wrapper_path runs an external wrapper executable instead. |
hip | *OpenConnectHIPOptions | (built-in) | { wrapper_path } | GlobalProtect HIP report handling. Built in by default; wrapper_path runs an external wrapper executable instead. |
tncc | *OpenConnectTNCCOptions | (built-in) | OpenConnectTNCCOptions | Network Connect TNCC compliance handling. See below. |
fortinet_host_check | *OpenConnectFortinetHostCheckOptions | (disabled) | OpenConnectFortinetHostCheckOptions | Fortinet hostcheck result override; only active when hostcheck is non-empty. See below. |
no_udp | bool | false | true | false | Disable the DTLS / ESP data channel and carry all traffic over the TLS channel. |
dtls_local_port | uint16 | 0 | <port> | Local UDP port for the direct DTLS / ESP data channel; 0 picks an ephemeral port. |
compression_disabled | bool | false | true | false | Disable AnyConnect compression negotiation. Conflicts with compression_mode: all. |
compression_mode | string | stateless | stateless | all | AnyConnect compression: stateless advertises oc-lz4 / lzs; all also offers stateful deflate for CSTP (DTLS stays stateless). Compression can leak information about tunnelled plaintext. |
ipv6_disabled | bool | false | true | false | Do not request or use IPv6 tunnel configuration. |
http_keepalive_disabled | bool | false | true | false | Disable HTTP connection reuse during authentication and configuration requests. |
xml_post_disabled | bool | false | true | false | Skip AnyConnect XML POST authentication and start with the legacy GET flow. |
external_auth_disabled | bool | false | true | false | Disable external-browser authentication (SSO / SAML) for AnyConnect, GlobalProtect and Fortinet; unexpected external-auth requests are rejected. |
password_authentication_disabled | bool | false | true | false | Abort AnyConnect authentication when the server returns a non-success form (like OpenConnect's --no-passwd). Does not affect other flavors or cookie. |
tcp_keep_alive_enabled | bool | false | true | false | Enable TCP keep-alive on direct VPN server connections (off by default, as in OpenConnect). Setting tcp_keep_alive or tcp_keep_alive_interval also enables it. |
pfs | bool | false | true | false | Require forward-secret cipher suites for TLS 1.2 and earlier. Off by default for servers that need RSA key exchange. |
mtu | uint32 | 0 | <576-65535> | Preferred tunnel MTU; the negotiated MTU is capped to it (0 = negotiated). Non-zero values below 576 become 576. |
base_mtu | uint32 | 1406 | <1280-65535> | Path MTU used to derive the tunnel MTU after outer overhead (AnyConnect, GlobalProtect, F5, Fortinet). Values below 1280 become 1280. |
dpd_interval | badoption.Duration | (server-provided) | <duration> | Override the Dead Peer Detection interval. Positive values below 2s become 2s. |
reconnect_timeout | badoption.Duration | 300s | <duration> | Maximum accumulated backoff across failed reconnect attempts; the first retry happens immediately. |
trojan_interval | badoption.Duration | (server-provided) | <duration> | Interval between GlobalProtect HIP reports / Network Connect TNCC checks. GlobalProtect uses 1h if the server sends none. |
queue_length | uint32 | 32 | <packets> | Packet queue length between the VPN transport and the tunnel interface. A full queue applies backpressure instead of dropping packets. |
allow_insecure_crypto | bool | false | true | false | Allow weak TLS / DTLS cipher suites and TLS 1.0 for legacy servers. Does not disable certificate verification. |
tls | OpenConnectTLSOptions | (system trust) | OpenConnectTLSOptions | OpenConnect's own TLS settings (not the shared sing-box TLS block). See below. |
form_entries | []OpenConnectFormEntryOptions | [] | [OpenConnectFormEntryOptions] | Authentication-form field overrides. See below. |
Source: option/openconnect.go:5-49 · pinned at v1.14.2 (af6e64c)
The endpoint also embeds the usual dial fields (detour, bind_interface, tcp_keep_alive, …); they apply to the connections made to the VPN server. csd and hip each take a single wrapper_path field.
token
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
mode | string | (required) | totp | hotp | stoken | oidc | Token type: TOTP, HOTP, RSA SecurID software token (stoken), or an OIDC access token sent as HTTP Bearer authentication. |
secret | string | (unset) | <secret> | Token secret: Base32, base32:-prefixed or an otpauth:// URI for TOTP / HOTP; the CTF token content for stoken; the access token for oidc (sent only when the server asks for Bearer auth). One of secret / secret_path is required. |
secret_path | string | (unset) | <file path> | Read the secret or OIDC access token from a file. Conflicts with secret. |
pin | string | (unset) | <PIN> | RSA SecurID PIN (stoken). |
password | string | (unset) | <string> | Password that decrypts a password-protected SecurID token (stoken). |
device_id | string | (unset) | <string> | Device ID that decrypts a device-bound SecurID token (stoken). |
counter | uint64 | 0 | <uint64> | Initial HOTP counter; 0 uses the counter from an otpauth:// URI when present. |
Source: option/openconnect.go:51-59 · pinned at v1.14.2 (af6e64c)
tls
OpenConnect uses its own TLS block rather than the shared TLS options — there is no uTLS, REALITY or ECH here.
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
insecure | bool | false | true | false | Skip server certificate and hostname verification. Lets an active attacker impersonate the server; prefer certificate_authority or peer_fingerprint. |
server_name | string | (host from server) | <hostname> | SNI and the name used for certificate verification. |
peer_fingerprint | badoption.Listable[string] | (unset) | <SHA-1 hex> | sha1:<hex> | sha256:<hex> | pin-sha256:<base64> | Allowed server certificate fingerprints: an unprefixed SHA-1 certificate hash (as OpenConnect's --servercert) or SPKI hashes. Prefixes of at least 4 characters are accepted; a match can authorize an otherwise untrusted certificate. |
system_trust_disabled | bool | false | true | false | Ignore the system CA pool; establish trust with certificate_authority or peer_fingerprint instead. |
certificate_authority | badoption.Listable[string] | (unset) | <PEM> | Extra trusted CA certificates (PEM content), added to the system pool. Conflicts with certificate_authority_path. |
certificate_authority_path | string | (unset) | <file path> | Extra trusted CA certificates read from a PEM file. |
client_certificate | badoption.Listable[string] | (unset) | <PEM> | Client certificate chain (PEM content). Certificate and key must be set together. |
client_certificate_path | string | (unset) | <file path> | Client certificate chain read from a PEM file. |
client_key | badoption.Listable[string] | (unset) | <PEM> | Client private key (PEM content). |
client_key_path | string | (unset) | <file path> | Client private key read from a PEM file. |
client_key_password | string | (unset) | <string> | Password for an encrypted client key. |
mca_certificate | badoption.Listable[string] | (unset) | <PEM> | AnyConnect multiple-certificate authentication (MCA) certificate chain (PEM content). Certificate and key must be set together. |
mca_certificate_path | string | (unset) | <file path> | MCA certificate chain read from a PEM file. |
mca_key | badoption.Listable[string] | (unset) | <PEM> | MCA private key (PEM content). |
mca_key_path | string | (unset) | <file path> | MCA private key read from a PEM file. |
mca_key_password | string | (unset) | <string> | Password for an encrypted MCA key. |
Source: option/openconnect.go:93-110 · pinned at v1.14.2 (af6e64c)
form_entries
Entries match by submission_key, or by form_id plus name; later matching entries win.
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
form_id | string | (unset) | <string> | Form identifier; matched together with name when submission_key is empty. |
submission_key | string | (unset) | <string> | Field submission key. Either this or both form_id and name are required; later matching entries take precedence. |
name | string | (unset) | <string> | Field name, matched together with form_id. |
value | string | (unset) | <string> | Value filled in automatically. Conflicts with promote. |
promote | bool | false | true | false | Ask for this field interactively instead of filling it. Conflicts with value. |
Source: option/openconnect.go:112-118 · pinned at v1.14.2 (af6e64c)
tncc
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
wrapper_path | string | (built-in) | <file path> | External TNCC wrapper executable. Conflicts with every other tncc field. |
device_id | string | (unset) | <string> | Device ID reported by the built-in handler. |
user_agent | string | Neoteris HC Http | <string> | User agent of the built-in handler. |
machine_identification_enabled | bool | false | true | false | Let the built-in handler report platform, hostname and observed MAC addresses. |
certificates | []OpenConnectTNCCCertificateOptions | [] | [{ certificate | certificate_path }] | Machine certificates (PEM content or path) used to answer certificate requests. Requires machine_identification_enabled. |
Source: option/openconnect.go:75-81 · pinned at v1.14.2 (af6e64c)
Each certificates[] entry takes either certificate (PEM content) or certificate_path.
fortinet_host_check
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
hostcheck | string | (unset) | <status>,<os-version> | Hostcheck result string, e.g. 0100,10.0.19042 — four 0/1 flags for third-party firewall, third-party antivirus, FortiClient firewall and FortiClient antivirus, then the OS version. Empty disables hostcheck. |
check_virtual_desktop | string | (empty) | <MAC>|<MAC>… | Virtual-desktop check result, conventionally |-joined MAC addresses. Sent as an empty field when unset. |
Source: option/openconnect.go:83-86 · pinned at v1.14.2 (af6e64c)
mobile
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
platform_version | string | (required) | <string> | Mobile OS version reported to the AnyConnect server. |
device_type | string | (required) | <string> | Device model or type. |
device_unique_id | string | (required) | <string> | Device identifier. |
Source: option/openconnect.go:61-65 · pinned at v1.14.2 (af6e64c)
DNS server
type: "openconnect" under dns.servers[]:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
endpoint | string | (required) | <endpoint tag> | Tag of the OpenConnect endpoint. Queries go to the resolvers the VPN server pushed: split-DNS rules use their own resolvers, pushed split-DNS and search-domain suffixes use the general ones, and the most specific suffix wins. |
accept_default_resolvers | bool | false | true | false | Also answer unmatched queries with the pushed general resolvers — only when the server tunnels all DNS or pushes no split-DNS rules or suffixes. Otherwise unmatched queries get NXDOMAIN. |
accept_search_domain | bool | false | true | false | Retry single-label names (e.g. intranet) with each pushed search domain until one resolves. |
Source: option/openconnect.go:120-124 · pinned at v1.14.2 (af6e64c)
Examples
Minimal AnyConnect client, routing a private range through the VPN:
{
"endpoints": [
{
"type": "openconnect",
"tag": "oc-client",
"server": "vpn.example.com",
"flavor": "anyconnect",
"username": "alice",
"password": "<password>"
}
],
"route": {
"rules": [
{ "ip_cidr": ["10.0.0.0/8"], "outbound": "oc-client" }
]
}
}GlobalProtect with a TOTP token, answering the VPN's split-DNS names through the pushed resolvers:
{
"dns": {
"servers": [
{ "type": "local", "tag": "local" },
{ "type": "openconnect", "tag": "oc-dns", "endpoint": "gp-client" }
],
"rules": [
{ "preferred_by": "oc-dns", "action": "route", "server": "oc-dns" }
],
"final": "local"
},
"endpoints": [
{
"type": "openconnect",
"tag": "gp-client",
"server": "https://gp.example.com",
"flavor": "gp",
"username": "alice",
"password": "<password>",
"token": { "mode": "totp", "secret": "<base32 secret>" }
}
]
}Notes
- The type is only compiled in with the
with_openconnectbuild tag, and the default userspace mode (system: false) additionally needswith_gvisor. Without them, the endpoint and the DNS server fail at startup with a rebuild hint. - Only non-interactive login works from a config file:
username/password,token,cookieandform_entries. SSO / SAML and any other prompt the config cannot answer are completed through the sing-box graphical clients or the Dashboard (Tools → Endpoints). - Pushed DNS settings are never installed into the operating system. Add an
openconnectDNS server to use them; withpreferred_byas in the example above, only the VPN's own names are sent there. compression_mode: allenables stateful compression, which carries extra confidentiality risks — only use it when the server requires it.tls.insecureandallow_insecure_cryptoare independent: the first skips certificate checks, the second only re-enables legacy ciphers and TLS 1.0.
Cross-core notes
- mihomo has no OpenConnect client; its closest VPN-style outbound is OpenVPN — mihomo.
- Xray-core has no OpenConnect support.
Source: option/openconnect.go:5-49 · v1.14.2 (af6e64c)
