diff --git a/scripts/setup-ovn.sh b/scripts/setup-ovn.sh index cff918d98..b5b314171 100755 --- a/scripts/setup-ovn.sh +++ b/scripts/setup-ovn.sh @@ -597,11 +597,26 @@ else external_ids:ovn-encap-type="geneve" fi -# system-id is owned by the openvswitch-switch package (persisted in -# /etc/openvswitch/system-id.conf and re-applied on every boot). Read it back -# rather than overriding — overriding here would drift from the on-disk value -# and the next reboot would silently flip the chassis identity (mulga-999). -echo " system-id: $(sudo ovs-vsctl get open . external_ids:system-id)" +# Pin OVS system-id (= OVN chassis-id) to the node's hostname. Two reasons: +# 1. IPsec identity: ovs-monitor-ipsec uses chassis-id as the IKEv2 +# `@` peer identity. Our per-node IPsec peer cert +# (admin.GenerateIPSecPeerCert) carries the node name as CN + dnsName +# SAN — see spx admin init/join, which pass --node as the cert +# identity. Leaving chassis-id as the package-generated UUID would +# cause `received AUTHENTICATION_FAILED` because charon validates +# `@` against a cert dnsName=. +# 2. Ops legibility: `ovn-sbctl show` lists chassis by name, not UUID. +# Both the on-disk file (read by ovs-ctl on boot) and the running OVS DB +# value must be updated atomically — writing only one drifts on the next +# reboot. +NODE_SYSTEM_ID="$(hostname -s)" +if [ -z "$NODE_SYSTEM_ID" ]; then + echo " ERROR: hostname -s returned empty; cannot pin OVS system-id." >&2 + exit 1 +fi +echo "$NODE_SYSTEM_ID" | sudo tee /etc/openvswitch/system-id.conf >/dev/null +sudo ovs-vsctl set Open_vSwitch . external_ids:system-id="$NODE_SYSTEM_ID" +echo " system-id: $NODE_SYSTEM_ID (pinned to hostname for IPsec cert identity)" echo " ovn-remote: $OVN_REMOTE" echo " ovn-encap-ip: $ENCAP_IP" echo " ovn-encap-type: geneve" diff --git a/spinifex/admin/ipsec.go b/spinifex/admin/ipsec.go index 589433b84..ad330872e 100644 --- a/spinifex/admin/ipsec.go +++ b/spinifex/admin/ipsec.go @@ -48,6 +48,15 @@ func IPSecCertPaths(configDir string) (certPath, keyPath string) { // SANs discovered from the local machine's interfaces are not added here — // IPSec peer identity should be the explicit cluster identity only, not // every interface address, to keep peer-identity matching strict. +// +// Cross-cutting invariant: this dnsName SAN must equal the OVS chassis-id +// (Open_vSwitch.external_ids:system-id) on every node, because +// ovs-monitor-ipsec generates strongSwan configs with leftid=@ +// / rightid=@ and charon validates those names against the +// peer cert's dnsName SAN. setup-ovn.sh pins system-id to `hostname -s` +// for exactly this reason — see the comment in setup-ovn.sh near +// `system-id.conf`. Changing the cert identity here without updating +// setup-ovn.sh (or vice versa) breaks IKE with AUTHENTICATION_FAILED. // GenerateIPSecPeerCertIfEnabled is a no-op when enabled is false. Used by // admin init and admin join so the call site reads as a single guarded line // keyed off the cluster's network.ipsec_enabled flag.