Native IKEv2 and IPsec
Pre-Alpha. This page describes behaviour that may change.
Ze implements native IKEv2 in Go for route-based IPsec VPN tunnels. It does not require strongSwan, libreswan, or another external IKE daemon. The IKE engine, cryptographic primitives, wire codec, and XFRM dataplane integration are all in-tree. The Ze binary negotiates IKE SAs, installs XFRM policies and states, and programs routes through XFRM interfaces from the same YANG config tree as every other subsystem.
Architecture
The IPsec stack is split across several packages:
| Package | Role |
|---|---|
internal/component/ike/wire |
IKEv2 wire format codec for RFC 7296 payloads |
internal/component/ike/crypto |
DH groups, PRFs, integrity, encryption, and key derivation |
internal/component/ike/transport |
UDP transport with NAT-T keepalives and port 4500 encapsulation |
internal/component/ike/eap |
EAP-MSCHAPv2 and EAP-TLS authentication |
internal/component/ike/engine |
IKE_SA_INIT, IKE_AUTH, CREATE_CHILD_SA, and INFORMATIONAL state machines for initiator and responder roles, rekeying, and DPD |
internal/component/ike/ipsec |
YANG schema, configuration, and validation |
internal/component/ike/dataplane |
XFRM policy and state programming through netlink |
internal/component/pki |
X.509 certificate and private-key store |
Configuration
IPsec is configured under vpn { ipsec { } }. Named ike-group and esp-group blocks hold the IKE and ESP proposals and lifetimes. Each site-to-site peer references a group pair, sets its authentication, and binds to an XFRM interface for route-based forwarding.
pki {
certificate my-cert {
certificate-file /etc/ze/certs/router.pem;
private-key-file /etc/ze/certs/router-key.pem;
}
ca-certificate my-ca {
certificate-file /etc/ze/certs/ca.pem;
}
}
vpn {
ipsec {
interface eth0;
ike-group site-b-ike {
key-exchange ikev2;
lifetime 28800;
proposal 1 {
encryption aes256gcm;
hash sha256;
dh-group 14;
}
dead-peer-detection { interval 30; timeout 120; }
}
esp-group site-b-esp {
lifetime 3600;
pfs enable;
proposal 1 { encryption aes256gcm; }
}
site-to-site {
peer site-b {
ike-group site-b-ike;
esp-group site-b-esp;
connection-type initiate;
local-address 198.51.100.2;
remote-address 198.51.100.1;
authentication {
mode x509;
certificate my-cert;
ca-certificate my-ca;
}
vti { bind vti0; }
}
}
}
}
Traffic selectors are not listed per tunnel. Route-based IPsec encrypts traffic that the routing table forwards through the bound XFRM interface. The generated configuration reference documents the XFRM interface leaves.
IKE features
| Feature | Detail |
|---|---|
| IKEv2, RFC 7296 | Full IKE_SA_INIT, IKE_AUTH, CREATE_CHILD_SA, and INFORMATIONAL exchange support |
| Role | Initiator or responder per peer through connection-type initiate or respond |
| Proposals | AES-CBC, AES-GCM 128/256, and ChaCha20-Poly1305; MODP 2048/3072/4096/8192 and ECP 256/384/521 DH groups; SHA-256/384/512 PRFs |
| Authentication | Pre-shared key, X.509 certificates, EAP-MSCHAPv2, and EAP-TLS; Ze acts as the EAP authenticator in responder mode |
| NAT-T, RFC 3948 | Automatic NAT detection, UDP encapsulation on port 4500, and keepalives |
| DPD | Dead Peer Detection through INFORMATIONAL exchanges with configurable interval and timeout |
| Rekeying | On-wire CREATE_CHILD_SA rekeying for IKE and Child SAs, make-before-break installation, and collision handling |
| MOBIKE, RFC 4555 | Not implemented; an endpoint address change requires the SA to be re-established |
| Denial-of-service | COOKIE challenge before a half-open slot is committed, and the INVALID_KE_PAYLOAD group retry |
| Dataplane read-back | show vpn ipsec dataplane reads the kernel SAD and SPD, and a drift check compares them against the engine |
| Virtual IP pool | Not wired. The remote-access container parses and no session reads it |
IKEv2 responder role
Ze can act as an IKEv2 responder or initiator. A peer with connection-type respond waits for an unsolicited inbound IKE_SA_INIT from its configured remote-address, answers IKE_AUTH, and installs the first Child SA. connection-type initiate, the default, starts the exchange. The UDP transport always listens, so responder mode does not need a separate listen switch.
As responder, Ze authenticates with a pre-shared key, X.509 certificate, or EAP. For EAP it acts as the EAP-MSCHAPv2 or EAP-TLS server for a road-warrior client. It presents its own certificate or PSK first, runs the EAP method, and derives session keys from the EAP MSK.
A new inbound IKE_SA_INIT can proceed while an older established SA for the same peer is still being maintained. Once the replacement authenticates, its INITIAL_CONTACT notification tells the remote endpoint to discard the stale SA. This lets a responder recover immediately after an operator clear or peer restart instead of waiting for Dead Peer Detection.
vpn {
ipsec {
interface eth0;
ike-group IKE-PSK {
key-exchange ikev2;
proposal 1 { encryption aes256gcm; hash sha256; dh-group 14; }
}
esp-group ESP-PSK {
pfs enable;
proposal 1 { encryption aes256gcm; }
}
site-to-site {
peer swan {
ike-group IKE-PSK;
esp-group ESP-PSK;
connection-type respond;
local-address 172.28.0.2;
remote-address 172.28.0.3;
authentication {
mode pre-shared-secret;
local-id "172.28.0.2";
remote-id "172.28.0.3";
pre-shared-secret "$9$encoded";
}
}
}
}
}
For a road-warrior EAP server, set authentication { mode eap-mschapv2 } or eap-tls, then reference a device certificate and ca-certificate from the PKI store. Configure the client as a site-to-site peer.
The remote-access container is not wired yet. It parses, and no session reads
it, so a pool assigns no address and an eap-user list authenticates nobody.
plan/spec-ipsec-remote-access.md owns the work.
Every EAP mode and X.509 needs a ca-certificate. The daemon refuses a remote
certificate it cannot chain to that anchor. A certificate with no trust anchor
authenticates nobody: any self-signed certificate that carries a valid signature
would pass. RFC 7296 Section 2.16 requires EAP to run with a public-key
authentication of the responder, and the anchor is what makes that signature
attributable.
ca-certificate holds one anchor, not a path. A two-level authority therefore
needs the peer to send its intermediate certificates. RFC 7296 Section 3.6 puts
the peer certificate first, and Ze reads every later CERT payload as a link
toward the anchor. Ze sends its own chain the same way: the certificate entry
first, then the intermediate that PKI entry carries. A refusal names the anchor
and the number of intermediate certificates the peer supplied.
Ze refuses a config that names no ca-certificate at commit and at reload. It
does NOT refuse it under ze config validate, which reads the schema and does
not run a plugin's config verifier.
Pre-shared secret encoding
pre-shared-secret-encoding reads the secret as ascii, the default, or as hex. RFC
7296 Section 2.15 requires the management interface to accept both. Use hex for a secret
that holds non-printable octets.
The encoding is stated and is never guessed from the value. A secret such as
abcdef0123456789 is valid ASCII and valid hex. A daemon that read the value to decide
would silently reinterpret a deployed secret. A hex value with an odd length or a
non-hexadecimal character is refused at commit rather than read as ASCII.
The at-rest $9$ obfuscation is unwrapped first, so a hex secret can also be stored
obfuscated.
Remote identity
remote-id is the identity Ze expects the remote endpoint to assert. When it is
set, Ze enforces it twice on every authentication.
| Check | What it compares | Refusal |
|---|---|---|
| Policy | The IDi or IDr payload the peer sent against remote-id |
Every authentication mode |
| Certificate | The same asserted identity against the remote certificate | Certificate modes only |
The certificate check is the half that matters when one authority issues to several clients. The chain proves the authority issued the certificate. It does not prove the certificate speaks for this peer, because the peer chooses the identity its signature covers. Without the second check any client of that authority authenticates as any peer.
A subject alternative name binds. The subject common name binds only when the
certificate carries no alternative name extension at all. The two fields do not
carry the same attestation: X.509 name constraints reach the alternative names
and never the subject distinguished name. An authority that permits only
dNSName .branch.example.com leaves the common name free, so a common name read
after a present alternative name would defeat that constraint.
The value you configure picks the field. An address remote-id binds against
the address alternative names alone, never against a domain name that spells the
same address. Certificate authorities issue an address alternative name under a
tighter policy than a name, and this stops the peer from choosing the weaker
field.
Ze compares six identity types: ID_IPV4_ADDR, ID_IPV6_ADDR, ID_FQDN,
ID_RFC822_ADDR, ID_KEY_ID, and ID_DER_ASN1_DN. A domain name and a mail
address compare with the ASCII letters folded. An address compares as an address,
so the encoding does not matter. A distinguished name compares in RFC 4514 string
form, and it binds against the certificate subject octet for octet. A peer that
asserts ID_DER_ASN1_GN is refused, because Ze compares no general name.
A local-id written as a distinguished name is still refused at commit. Ze
derives the type it SENDS from the shape of the value, and that derivation has no
ID_DER_ASN1_DN form.
RFC 7296 Section 4 requires that you can configure Ze to accept a PKIX peer whose
identity is ID_KEY_ID. An opaque key id matches no certificate field, so Ze
cannot derive that binding and denies it by default. Set remote-id-type key-id
to state that the chain to ca-certificate plus the exact key id IS the binding
you intend.
remote-id-type also pins ONE identity type for the peer. Without it a text
remote-id accepts ID_FQDN, ID_RFC822_ADDR and ID_KEY_ID alike. All three
compare as text. Set remote-id-type rfc822-address when you know the value is a
mail address. Ze then refuses a peer that asserts ID_FQDN with the same text.
The leaf governs what Ze ACCEPTS. The type Ze sends still follows local-id.
An unset remote-id runs neither check. Every certificate the configured
authority issued then authenticates as this peer. Ze logs a warning that names
the peer and the identity it accepted. Set remote-id whenever the authority
issues to more than one client.
Certificate chain length
certificate-count bounds the X.509 chain in both directions. It is the most
certificates Ze sends for the peer, and the most it accepts from it. RFC 7296
Section 3.6 sets that figure at four, and the default is four, so a peer you never
configure gets the conformant behaviour.
A peer that sends more than the bound is REFUSED. Ze does not truncate the chain. A silent trim hides from you that the limit was reached. It also makes the surviving certificates depend on the order the peer chose.
Name the intermediates on the PKI certificate entry, in order from the issuer of
your device certificate toward the trust anchor. Ze refuses at commit a peer whose
certificate-count is smaller than the chain its PKI entry holds.
Hash and URL certificates
hash-and-url replaces the certificate on the wire with a 20-octet SHA-1 hash and
a URL that resolves to it (RFC 7296 Section 3.6). It keeps IKE messages short. Set
certificate-url to the http URL where you publish this device's certificate.
The leaf defaults to false, and that default is a security property. Resolving a URL a peer sends makes an outbound request on behalf of a peer that is NOT yet authenticated. Ze must fetch the certificate before it CAN authenticate the peer. With the leaf false Ze advertises nothing, a conforming peer sends no such payload, and Ze drops one from a peer that sends it anyway.
When you enable it, the lookup is bounded:
| Control | Bound |
|---|---|
| Scheme | http only. Every other scheme is refused before any name resolution |
| Response size | 64 KiB |
| Total timeout | 5 seconds |
| Redirects | None. A redirect is refused |
| Destination | Loopback, private, link-local, multicast and metadata addresses are denied |
| Hash | The SHA-1 is verified BEFORE any parser reads the fetched bytes |
| Cache | Keyed by the hash, never by the URL |
Use certificate-url-allow to permit a destination the deny list refuses. Run
ze doctor to check a configured certificate-url.
The EAP peer validates the authenticator's certificate chain against that trust anchor. EAP-TLS has no server hostname, so the check validates the chain without DNS-name matching.
EAP-TLS with TLS 1.3
RFC 9190 Section 2.5 requires an encrypted TLS record that carries application data
0x00. The EAP-TLS 1.3 server sends it once it has processed the client Finished and sent
its last handshake message. That record is the protected success result indication. Ze
sends it in the flight that round already carries, so the exchange keeps its round count.
The record is what makes a TLS 1.3 EAP-TLS client work. strongSwan derives no MSK without it, so the AUTH payload of RFC 7296 Section 2.16 cannot be computed and the SA never establishes. A TLS 1.2 exchange concludes with the bare EAP-Success it concluded with before, because the version test reads the negotiated version.
Ze issues no TLS session ticket. It builds one TLS configuration for each EAP session, and Go keys ticket encryption on that instance. No other session can redeem a ticket minted in one. EAP-TLS session resumption is therefore not available.
Denial-of-service protection
RFC 7296 Section 2.6 names two attacks on an IKE responder: state and CPU exhaustion from initiation requests with forged source addresses. The answer is a COOKIE. Ze answers an inbound IKE_SA_INIT with a COOKIE notification and commits no state, and the initiator must repeat its request with that cookie as the first payload before Ze creates an SA or takes the peer's half-open handshake slot.
cookie-threshold sets how many half-open IKE SAs Ze tolerates before it challenges.
It defaults to 0, which challenges every inbound initiation. That default costs a
genuine peer one extra round trip and closes a real availability hole: a responder peer
has one half-open slot, held for 30 seconds, so a single spoofed datagram bearing that
peer's source address would otherwise deny it service, and one datagram every 30 seconds
would deny it indefinitely. Raise the value to tolerate that many half-open handshakes
before challenging.
vpn {
ipsec {
cookie-threshold 0;
}
}
The cookie is an HMAC-SHA256 over the initiator's nonce, its source address and its SPI, under a secret Ze generates and rotates every 60 seconds. A cookie minted under the previous secret stays valid for one further interval, so a rotation never breaks an in-flight handshake. A cookie that does not match is ignored rather than rejected, as the RFC requires: the message is processed as though it carried no cookie, which for a pressured responder means issuing a fresh challenge.
Diffie-Hellman group correction
An initiator has to guess which Diffie-Hellman group the responder will pick. When the guess is wrong the responder answers INVALID_KE_PAYLOAD naming the group it accepts, and Ze retries the IKE_SA_INIT under that group. Without this, Ze could never establish with a peer that prefers a different group.
The retry re-offers the whole configured proposal set rather than narrowing it to the suite carrying the responder's group. RFC 7296 Section 1.2 requires that, because the rejection is unauthenticated and a narrowed re-offer would let an attacker who forges one notify choose the cipher. Ze also refuses a group it never proposed, which stops the same attacker choosing the group. Both COOKIE and INVALID_KE_PAYLOAD retries share one budget of three per connection attempt, so two peers can never oscillate between them.
Rekeying
Both Child SAs and IKE SAs are rekeyed with an on-wire CREATE_CHILD_SA exchange. This replaced an earlier local-only key roll that could silently desynchronise a live tunnel. A Child SA rekey carries N(REKEY_SA) with fresh nonces and traffic selectors. An IKE SA rekey performs a fresh Diffie-Hellman exchange and resets the message-ID counters.
Rekeying is make-before-break: the replacement SA is installed before the old one is deleted, so forwarding does not pause. Simultaneous rekeys are resolved by the RFC 7296 nonce rule. Section 2.8.1 states it as a recommendation: if redundant SAs are created, the endpoint that created the SA with the lowest of the four nonces SHOULD close that SA. Ze resolves the collision one step earlier and abandons its own pending exchange when its nonce is the lower one, so the redundant SA is never created and both ends converge on one SA. Ze rekeys at the ESP or IKE soft lifetime and retransmits a lost CREATE_CHILD_SA before tearing the tunnel down. Rekeys are counted by ze_ipsec_rekey_total{peer} and streamed as child-rekey events by monitor vpn ipsec.
Child SA and dataplane
Child SAs define traffic selectors and the ESP proposal. Ze programs XFRM policies and states through netlink. Route-based IPsec uses XFRM interfaces.
| Feature | Detail |
|---|---|
| ESP proposals | AES-GCM-16 128/256 and AES-CBC with HMAC-SHA-256/384/512 |
| Traffic selectors | IPv4 and IPv6 CIDR prefixes, an optional IP protocol, and an optional single port. See Traffic selectors and narrowing below |
| Encapsulation | Tunnel mode by default; mode transport negotiates transport mode per RFC 7296 Section 1.3.1 |
| Connection | connection-type initiate starts the exchange; connection-type respond waits for the peer |
| Replay protection | Anti-replay window, default 32 |
| Lifetime | Time-based and byte-based rekeying thresholds |
Both ESP wire forms
RFC 7296 Section 2.23 requires a device that supports NAT traversal to receive UDP-encapsulated ESP and bare ESP at any time. One Linux XFRM state serves one form. A state that carries the encapsulation template refuses bare ESP, and a state without one refuses encapsulated ESP. Ze serves the second form beside the kernel. A raw IPPROTO_ESP reader takes the datagram XFRM refused. It re-presents that datagram through port 4500, which carries UDP_ENCAP, so the kernel hands XFRM the form its template wants. One established Child SA therefore keeps carrying traffic when its peer changes ESP form mid-session, with no rekey and no delete.
The reader carries a per-socket inbound policy exemption. Linux applies the inbound IPsec policy check before it queues a packet to a raw socket. Ze's own Child SA policy refuses exactly the datagrams this reader exists to recover. The exemption reaches that one socket. XFRM still decrypts, still owns the replay window, and still applies the inbound policy to the re-presented datagram.
The second-form reader serves IPv4. An SA that asks for both forms over IPv6 is refused rather than installed with one form. The form Ze SENDS is a separate decision and follows the NAT verdict alone: Ze encapsulates when it detected a NAT.
Traffic selectors and narrowing
A peer's traffic-selector list states which traffic its Child SAs carry. It is also the
policy RFC 7296 Section 2.9 narrows a peer's proposal against.
vpn { ipsec { site-to-site { peer branch-1 {
traffic-selector 1 {
local { prefix 10.1.0.0/16; }
remote { prefix 10.2.0.0/16; }
}
traffic-selector 2 {
protocol 6
local { prefix 10.3.0.0/24; port 179; }
remote { prefix 10.4.0.0/24; }
}
} } } }
When the list is absent the peer accepts whatever the remote endpoint proposes. That is the behaviour of every configuration written before the list existed, so adding the list is what restricts a peer, never omitting it.
As responder, Ze narrows the initiator's proposed TSi and TSr to a subset the list allows,
and leads the answer with the initiator's first choices. A proposal it cannot narrow to a
non-empty subset draws a TS_UNACCEPTABLE notification. As initiator, Ze proposes the list
in order, because RFC 7296 reads the order as the preference order.
The selectors Ze puts on the wire are the selectors it programs. A proposal it cannot program exactly is narrowed FURTHER, never rounded outward: an address range that is not a prefix becomes the largest prefix inside it, and a port range that is neither all ports nor one port becomes its first port. A rekey is never narrowed below the scope in use.
| Port value | Meaning | RFC 7296 Section 3.13.1 encoding |
|---|---|---|
any (the default) |
every port | start 0, end 65535 |
1..65535 |
one port | start N, end N |
A port other than any needs protocol to name a protocol that defines ports, such as 6
for TCP or 17 for UDP. Section 3.13.1 requires the port fields to be 0 and 65535 whenever
the protocol is 0, so the combination is refused at commit rather than widened silently.
Opaque ports are refused for the same reason: no dataplane backend can express an exact
match on port 0, so accepting one would install an any-port policy.
Transport mode
mode transport asks the peer for transport mode with the USE_TRANSPORT_MODE
notification of RFC 7296 Section 1.3.1. Tunnel mode is the default, and it is the RFC's own
default.
vpn { ipsec { site-to-site { peer host-1 {
mode transport
transport-required true
traffic-selector 1 {
local { prefix 10.0.0.3/32; }
remote { prefix 10.0.0.4/32; }
}
} } } }
Transport mode constrains the selectors. RFC 7296 Section 2.23.1 requires exactly one IP
address in TSi and in TSr, so every prefix must be a single host, and a vti binding is
refused because an XFRM interface carries tunnel encapsulation. Several selectors are still
allowed when they share that one address, for example to negotiate several ports.
A peer that declines the request establishes the Child SA in tunnel mode. Set
transport-required true when that downgrade is unacceptable: Ze then deletes the SA
instead, which is what Section 1.3.1 asks of an initiator. It defaults to false, so a peer
without transport mode keeps a working tunnel.
The VPP dataplane backend does not implement transport mode. It refuses a transport-mode install with a clear error rather than programming a tunnel-mode entry and reporting success. The SA path and the policy path each refuse it on their own.
The VPP dataplane backend cannot be driven by IKE, and no configuration selects it.
It installs security associations, and it refuses every security policy the IKE engine
produces for them. VPP has no node-wide policy database: a policy lives in a security
policy database that acts only on the interfaces it is bound to, and nothing tells the
backend which interface to use. The backend is reached only through a private test
override, it is compiled in only with the ze_vpp build tag, and no test has sent ESP
through it. Use the XFRM backend, which is the default and the production path.
PKI certificate store
The pki { } block stores X.509 certificates, private keys, and CA certificates. Certificates are loaded from PEM files and validated at commit time. The PKI store also serves TLS certificates for the web UI and gRPC API.
Health monitoring reports certificate expiry as a warning at 30 days and an error after expiry. Prometheus exposes ze_pki_certificate_expiry_seconds and ze_pki_certificate_valid.
XFRM interfaces
XFRM interfaces provide route-based IPsec. Traffic routed through the XFRM interface is encrypted, and incoming traffic is decrypted before it appears on the interface. The generated configuration reference documents the interface surface.
Reading the kernel dataplane
Every other IPsec command reports what the IKE engine believes it installed. The show vpn
ipsec dataplane commands read the kernel instead. Three failures become visible: a state
the kernel refused, a state it expired, and a policy that outlived its owner.
| Command | What it reads |
|---|---|
show vpn ipsec dataplane sa [spi <spi>] |
The Security Association Database: SPI, addresses, if-id, mode, algorithms, replay window, byte and packet counters, and timestamps. spi <spi> selects one SA. SPI 0 is refused, because RFC 4303 Section 2.1 reserves it and a typo must not look like a full dump |
show vpn ipsec dataplane policy |
The Security Policy Database: selector prefixes and ports, direction, priority, upper-layer protocol, if-id, tunnel endpoints, and the peer that installed each policy. A policy Ze did not install reports its owner as unknown |
show vpn ipsec dataplane drift |
Each Child SA the engine counts as installed whose SPI the kernel does not hold. The command exits non-zero when it finds drift, so a script can test it |
RFC 4301 Section 4.4 keeps the SPD and the SAD separate, and so does this command tree.
A read that cannot happen is an error, never an empty table. No backend loaded, a backend that cannot enumerate, and a process without CAP_NET_ADMIN each get their own message. An empty table answers "is my tunnel programmed?" with "no", and that is the wrong answer when the truth is that nobody asked the kernel.
Drift is compared in ONE direction. An SPI the kernel holds that the engine does not name is not drift. RFC 7296 Section 2.8 keeps the old and the new Child SA alive until the old one is deleted. A rekey window therefore holds two SPIs.
show vpn ipsec sa and show vpn ipsec peer name <name> carry the kernel counters in each
child SA object: bytes-in, packets-in, bytes-out, and packets-out. They come from
the kernel because the IKE engine never sees ESP payload. Each is null rather than zero
when Ze cannot read the SAD, and counters-known says which answer you hold. Null says
nobody asked the kernel. Zero says the SA carried nothing.
The health registry folds the same comparison in. A peer whose Child SA the kernel does not
hold reports degraded and names the peer. A dataplane that cannot be read is not drift,
so it never turns the status green on a question nobody asked.
ze doctor reports doctor-ipsec-xfrm-unavailable as a warning when vpn ipsec is
configured and the kernel XFRM dataplane does not answer. The two causes need different
action: a kernel without CONFIG_XFRM_USER and CONFIG_INET_ESP, or a process without
CAP_NET_ADMIN. doctor-ipsec-udp-encap is an error, and it covers a NAT-T socket that
would not bind as well as one the kernel will not decapsulate through. Both leave a tunnel
that establishes and carries no traffic.
CLI
| Command | Description |
|---|---|
show vpn ipsec status |
Tunnel and peer status summary |
show vpn ipsec sa |
Active IKE and Child SAs, including algorithms, kernel byte and packet counters, rekey timers, SPIs, NAT detection, and initiator role |
show vpn ipsec peer name <name> |
Detail for one configured peer |
show vpn ipsec dataplane sa [spi <spi>] |
The SAs the kernel holds. See Reading the kernel dataplane |
show vpn ipsec dataplane policy |
The policies the kernel holds |
show vpn ipsec dataplane drift |
Child SAs the engine expects that the kernel does not hold. Exits non-zero on drift |
clear vpn ipsec sa [peer <name>] |
Tear down and re-establish SAs, optionally for one peer |
monitor vpn ipsec |
Stream sa-up, sa-down, child-up, child-down, and child-rekey lifecycle events |
show pki certificates |
List loaded certificates with expiry information |
show vpn ipsec sa also reports peer-window-size, the number of outstanding requests
the peer promised in its SET_WINDOW_SIZE notification. Zero means the peer sent none, which
RFC 7296 Section 2.3 reads as a window of one. Ze holds one request outstanding and accepts
exactly one request id.
clear vpn ipsec sa sends a best-effort encrypted IKE Delete before removing
local state. Initiator peers then re-establish immediately. If the UDP Delete is
lost, the normal DPD path still removes the stale remote SA.
Health and metrics
The IPsec component registers with the health registry. It reports healthy when all configured tunnels are established, degraded when some are down, and down when critical tunnels fail.
Prometheus exposes ze_ipsec_sa_count, ze_ipsec_tunnel_up{peer}, ze_ipsec_tunnel_degraded{peer}, and ze_ipsec_rekey_total{peer}.
ze_ipsec_error_notify_sent_total{type,protected} counts error notifications Ze sent, by
notify type and by whether the carrying message was encrypted.
ze_ipsec_error_notify_suppressed_total{reason} counts the ones a guard stopped, by the
name of that guard.
Three more counters report the COOKIE challenge described under Denial-of-service protection. ze_ipsec_cookie_challenges_total{peer} counts the challenges Ze issues, ze_ipsec_cookie_verify_failures_total{peer} counts inbound cookies that did not verify, and ze_ipsec_sa_init_retries_total{peer,cause} counts the IKE_SA_INIT retries Ze sends, labeled cookie or invalid-ke-payload. A rising verify-failure count is either an attacker probing the half-open slot or a secret rotation catching an in-flight challenge. A rising retry count on the cookie cause is the signature of the forged-notify flood RFC 7296 Section 2.6 describes.
ze_ipsec_tunnel_up reads 1 only when the IKE SA is established and the Child SA is
installed in the dataplane. A tunnel whose ESP install the kernel refused reads
ze_ipsec_tunnel_up 0 and ze_ipsec_tunnel_degraded 1. Such a tunnel has a live
control plane and carries no encrypted traffic. Alert on the degraded gauge, because
the two gauges together separate a lost session from a session with no ESP.
The daemon logs child-sa: dataplane refused the ESP state, tunnel is degraded and
carries no encrypted traffic when this happens.
Interop testing
The IKE implementation includes interop tests against strongSwan, from the Alpine 3.21 test
image. The infrastructure in test/ipsec-interop/ drives strongSwan containers as remote
IKE peers. Sixteen scenarios run today:
| Area | Scenarios |
|---|---|
| Authentication | PSK, EAP-MSCHAPv2, EAP-TLS, and EAP-TLS over TLS 1.3 |
| Ze as responder | PSK, EAP-MSCHAPv2, EAP-TLS over TLS 1.3, IKE SA rekey started by strongSwan, and a fresh IKE_SA_INIT accepted beside an established SA |
| Rekey and teardown | Child SA rekey with make-before-break, clear vpn ipsec sa and re-establishment, and a Delete sent while the one request window is held |
| Negotiation | The INVALID_KE_PAYLOAD retry and the COOKIE challenge |
| Dataplane | A live Child SA whose peer changes ESP form mid-session, and BGP routes exchanged with FRR over the tunnel |
There is no certificate-only (mode x509) scenario. The certificate paths are proven by
unit tests and by the EAP-TLS scenarios, which authenticate both ends with certificates.
Ze holds every gated MUST-level requirement extracted from RFC 7296 in
rfc/short/rfc7296.md. That is 222 of the summary's 227 rows, each proven in both
directions by RFC requirement: tagged tests. The remaining five rows are SHOULD-level and
ungated.
See also
- The generated configuration reference covers every IPsec and XFRM configuration leaf.
- Monitoring covers the health registry and operational visibility.
- Feature inventory shows the maturity of each IKE and IPsec capability.