Skip to content

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[]:

FieldTypeDefaultAllowed valuesDescription
systemboolfalsetrue | falseUse a real system interface instead of sing-box's internal network stack. Needs privileges and must not clash with an existing interface.
namestring(auto)<interface name>Interface name when system is enabled. An oc-prefixed name is generated by default.
udp_timeoutbadoption.Duration5m<duration>UDP NAT session expiry.
udp_mappingUDPNATBehaviorendpoint_independentendpoint_independent | address_dependent | address_and_port_dependentUDP NAT mapping: reuse one mapping per source for every destination, or split it per destination address / address and port.
udp_filteringUDPNATBehaviorendpoint_independentendpoint_independent | address_dependent | address_and_port_dependentUDP NAT filtering: accept replies from any remote, or only from addresses / address-and-port pairs already contacted.
udp_nat_maxuint320<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.
serverstring(required)<hostname or https:// URL>VPN server HTTPS URL; https:// is added if missing. User info, query strings and fragments are not supported.
flavorstringanyconnectanyconnect | gp | fortinet | f5 | pulse | ncServer family: Cisco AnyConnect, Palo Alto GlobalProtect, Fortinet, F5 BIG-IP, Pulse Connect Secure, Juniper Network Connect.
usernamestring(unset)<string>Fills matching username fields of the authentication form.
passwordstring(unset)<string>Fills matching password fields of the authentication form.
auth_groupstring(unset)<string>Preselects a matching group, realm, domain or gateway choice where the flavor supports it.
cookiestring(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)OpenConnectTokenOptionsSoftware token (TOTP, HOTP, RSA SecurID) or OIDC access token used to answer token prompts or Bearer authentication. See below.
reported_osstring(from platform)linux | linux-64 | win | mac-intel | android | apple-iosOS identity reported to AnyConnect, GlobalProtect and Pulse servers. Defaults to the running platform.
user_agentstring(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).
versionstringv9.21<string>Client version reported separately from user_agent; currently used by AnyConnect XML authentication.
local_hostnamestring(system hostname)<string>Hostname reported to the server; localhost if the system hostname is unavailable.
mobile*OpenConnectMobileOptions(unset)OpenConnectMobileOptionsReport 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)OpenConnectTNCCOptionsNetwork Connect TNCC compliance handling. See below.
fortinet_host_check*OpenConnectFortinetHostCheckOptions(disabled)OpenConnectFortinetHostCheckOptionsFortinet hostcheck result override; only active when hostcheck is non-empty. See below.
no_udpboolfalsetrue | falseDisable the DTLS / ESP data channel and carry all traffic over the TLS channel.
dtls_local_portuint160<port>Local UDP port for the direct DTLS / ESP data channel; 0 picks an ephemeral port.
compression_disabledboolfalsetrue | falseDisable AnyConnect compression negotiation. Conflicts with compression_mode: all.
compression_modestringstatelessstateless | allAnyConnect compression: stateless advertises oc-lz4 / lzs; all also offers stateful deflate for CSTP (DTLS stays stateless). Compression can leak information about tunnelled plaintext.
ipv6_disabledboolfalsetrue | falseDo not request or use IPv6 tunnel configuration.
http_keepalive_disabledboolfalsetrue | falseDisable HTTP connection reuse during authentication and configuration requests.
xml_post_disabledboolfalsetrue | falseSkip AnyConnect XML POST authentication and start with the legacy GET flow.
external_auth_disabledboolfalsetrue | falseDisable external-browser authentication (SSO / SAML) for AnyConnect, GlobalProtect and Fortinet; unexpected external-auth requests are rejected.
password_authentication_disabledboolfalsetrue | falseAbort 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_enabledboolfalsetrue | falseEnable 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.
pfsboolfalsetrue | falseRequire forward-secret cipher suites for TLS 1.2 and earlier. Off by default for servers that need RSA key exchange.
mtuuint320<576-65535>Preferred tunnel MTU; the negotiated MTU is capped to it (0 = negotiated). Non-zero values below 576 become 576.
base_mtuuint321406<1280-65535>Path MTU used to derive the tunnel MTU after outer overhead (AnyConnect, GlobalProtect, F5, Fortinet). Values below 1280 become 1280.
dpd_intervalbadoption.Duration(server-provided)<duration>Override the Dead Peer Detection interval. Positive values below 2s become 2s.
reconnect_timeoutbadoption.Duration300s<duration>Maximum accumulated backoff across failed reconnect attempts; the first retry happens immediately.
trojan_intervalbadoption.Duration(server-provided)<duration>Interval between GlobalProtect HIP reports / Network Connect TNCC checks. GlobalProtect uses 1h if the server sends none.
queue_lengthuint3232<packets>Packet queue length between the VPN transport and the tunnel interface. A full queue applies backpressure instead of dropping packets.
allow_insecure_cryptoboolfalsetrue | falseAllow weak TLS / DTLS cipher suites and TLS 1.0 for legacy servers. Does not disable certificate verification.
tlsOpenConnectTLSOptions(system trust)OpenConnectTLSOptionsOpenConnect'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 ​

FieldTypeDefaultAllowed valuesDescription
modestring(required)totp | hotp | stoken | oidcToken type: TOTP, HOTP, RSA SecurID software token (stoken), or an OIDC access token sent as HTTP Bearer authentication.
secretstring(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_pathstring(unset)<file path>Read the secret or OIDC access token from a file. Conflicts with secret.
pinstring(unset)<PIN>RSA SecurID PIN (stoken).
passwordstring(unset)<string>Password that decrypts a password-protected SecurID token (stoken).
device_idstring(unset)<string>Device ID that decrypts a device-bound SecurID token (stoken).
counteruint640<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.

FieldTypeDefaultAllowed valuesDescription
insecureboolfalsetrue | falseSkip server certificate and hostname verification. Lets an active attacker impersonate the server; prefer certificate_authority or peer_fingerprint.
server_namestring(host from server)<hostname>SNI and the name used for certificate verification.
peer_fingerprintbadoption.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_disabledboolfalsetrue | falseIgnore the system CA pool; establish trust with certificate_authority or peer_fingerprint instead.
certificate_authoritybadoption.Listable[string](unset)<PEM>Extra trusted CA certificates (PEM content), added to the system pool. Conflicts with certificate_authority_path.
certificate_authority_pathstring(unset)<file path>Extra trusted CA certificates read from a PEM file.
client_certificatebadoption.Listable[string](unset)<PEM>Client certificate chain (PEM content). Certificate and key must be set together.
client_certificate_pathstring(unset)<file path>Client certificate chain read from a PEM file.
client_keybadoption.Listable[string](unset)<PEM>Client private key (PEM content).
client_key_pathstring(unset)<file path>Client private key read from a PEM file.
client_key_passwordstring(unset)<string>Password for an encrypted client key.
mca_certificatebadoption.Listable[string](unset)<PEM>AnyConnect multiple-certificate authentication (MCA) certificate chain (PEM content). Certificate and key must be set together.
mca_certificate_pathstring(unset)<file path>MCA certificate chain read from a PEM file.
mca_keybadoption.Listable[string](unset)<PEM>MCA private key (PEM content).
mca_key_pathstring(unset)<file path>MCA private key read from a PEM file.
mca_key_passwordstring(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.

FieldTypeDefaultAllowed valuesDescription
form_idstring(unset)<string>Form identifier; matched together with name when submission_key is empty.
submission_keystring(unset)<string>Field submission key. Either this or both form_id and name are required; later matching entries take precedence.
namestring(unset)<string>Field name, matched together with form_id.
valuestring(unset)<string>Value filled in automatically. Conflicts with promote.
promoteboolfalsetrue | falseAsk for this field interactively instead of filling it. Conflicts with value.

Source: option/openconnect.go:112-118 · pinned at v1.14.2 (af6e64c)

tncc ​

FieldTypeDefaultAllowed valuesDescription
wrapper_pathstring(built-in)<file path>External TNCC wrapper executable. Conflicts with every other tncc field.
device_idstring(unset)<string>Device ID reported by the built-in handler.
user_agentstringNeoteris HC Http<string>User agent of the built-in handler.
machine_identification_enabledboolfalsetrue | falseLet 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 ​

FieldTypeDefaultAllowed valuesDescription
hostcheckstring(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_desktopstring(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 ​

FieldTypeDefaultAllowed valuesDescription
platform_versionstring(required)<string>Mobile OS version reported to the AnyConnect server.
device_typestring(required)<string>Device model or type.
device_unique_idstring(required)<string>Device identifier.

Source: option/openconnect.go:61-65 · pinned at v1.14.2 (af6e64c)

DNS server ​

type: "openconnect" under dns.servers[]:

FieldTypeDefaultAllowed valuesDescription
endpointstring(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_resolversboolfalsetrue | falseAlso 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_domainboolfalsetrue | falseRetry 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:

json
{
  "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:

json
{
  "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_openconnect build tag, and the default userspace mode (system: false) additionally needs with_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, cookie and form_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 openconnect DNS server to use them; with preferred_by as in the example above, only the VPN's own names are sent there.
  • compression_mode: all enables stateful compression, which carries extra confidentiality risks — only use it when the server requires it.
  • tls.insecure and allow_insecure_crypto are 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)

Core Tutorial by Argsment