cilium/cilium · critical
IPSec requires a valid BootID
Error message
IPSec requires a valid BootID
What it means
During InitLocalNode, after populating the local node from config and Kubernetes, the agent reads the node's BootID (derived from the machine boot / system information). If IPSec (transparent encryption) is enabled but the BootID is empty, initialization aborts, because IPSec key material is keyed per boot and a missing BootID would make SPI/XFRM state unsafe.
Source
Thrown at pkg/node/sync/local_node_sync.go:86
old node.LocalNode
}
func (ini *localNodeSynchronizer) InitLocalNode(ctx context.Context, n *node.LocalNode) error {
n.Source = source.Local
if err := ini.initFromConfig(n); err != nil {
return err
}
n.Local.UnderlayProtocol = ini.TunnelConfig.UnderlayProtocol()
if err := ini.initFromK8s(ctx, n); err != nil {
return err
}
n.BootID = node.GetBootID(ini.Logger)
if ini.IPsecConfig.Enabled() && n.BootID == "" {
return fmt.Errorf("IPSec requires a valid BootID")
}
for _, fn := range ini.ExtraInitFuncs {
if err := fn(ctx, n); err != nil {
return err
}
}
return nil
}
func (ini *localNodeSynchronizer) SyncLocalNode(ctx context.Context, store *node.LocalNodeStore) {
if ini.K8sLocalNode == nil {
return
}
for ev := range ini.K8sLocalNode.Events(ctx) {
if ev.Kind == resource.Upsert {View on GitHub (pinned to ac7b90affa)
Solutions
- Verify /proc/sys/kernel/random/boot_id exists and is readable inside the agent's environment; mount it if the runtime hides it.
- Check node.GetBootID() fallback paths and logs to see why it returned empty (e.g. missing procfs mount).
- Run the agent with proper privileges/host mounts (hostPID/proc access) so kernel files are visible.
- If IPsec is not actually needed, disable transparent encryption (--enable-ipsec=false) to bypass the requirement.
Example fix
// before: container without boot_id visible // docker run cilium/... (no /proc/sys/kernel/random/boot_id) // after: expose host proc docker run -v /proc/sys/kernel/random/boot_id:/proc/sys/kernel/random/boot_id:ro cilium/...
Defensive patterns
Strategy: validation
Validate before calling
bootID, err := os.ReadFile("/proc/sys/kernel/random/boot_id")
if err != nil || len(strings.TrimSpace(string(bootID))) == 0 {
return errors.New("boot id unavailable: IPsec cannot be enabled")
}
if ipsecEnabled && node.GetBootID(logger) == "" {
return errors.New("IPSec requires a valid BootID")
} Type guard
func hasBootID(n *node.LocalNode) bool {
return n.BootID != ""
} Try / catch
if err := synchronizer.InitLocalNode(ctx, n); err != nil {
if strings.Contains(err.Error(), "BootID") {
log.Error("IPsec enabled but boot ID unavailable; aborting with encryption disabled rather than unsafe state")
}
return err
} Prevention
- Mount /proc/sys/kernel/random/boot_id into agent containers.
- Pre-check the environment for boot ID availability before enabling IPsec.
- Pin to runtimes/VMs that expose a stable boot ID.
- Gate --enable-ipsec on an environment readiness check in deployment tooling.
When it happens
Trigger: IPsecConfig.Enabled() is true (encryption enabled, e.g. --enable-ipsec=true) and node.GetBootID() returns an empty string during local node initialization, causing InitLocalNode (the public LocalNodeSynchronizer entry point) to return this error and agent startup to fail.
Common situations: Containers or VMs lacking /proc/sys/kernel/random/boot_id or equivalent boot identification; running in environments (some sandboxed/container runtimes) where the boot ID file is unreadable; unusual platforms where GetBootID cannot determine a stable boot identifier.
Related errors
- ${binaryName} doesn't support --${ipam}=${ipamFlagValue}
- an unsupported XfrmStateAlgo combination has been found
- BIG TCP is not supported with encryption enabled
- auth-algo has invalid value: %s
- IPSec doesnt support strict ingress encryption.
AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31).
Data as JSON: /api/errors/028feedf8006f949.
Report an issue: GitHub.