From 47e6aaec0a2374c2fad7a830d3f121e378f8cbd3 Mon Sep 17 00:00:00 2001 From: riccardom Date: Fri, 7 Aug 2026 16:27:31 +0200 Subject: [PATCH] Addresses CI fixes --- client/internal/engine.go | 5 +++++ client/internal/peer/conn.go | 10 ++++++---- client/internal/pqkem_transport.go | 12 ++++++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 170864b23..1ece7df17 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -668,6 +668,11 @@ func (e *Engine) Start(netbirdConfig *mgmProto.NetbirdConfig, mgmtURL *url.URL) if pqkem.Enabled() { tr, pqErr := newPQTransport(e.config.WgAddr.IP) if pqErr != nil { + // In strict mode the peer must fail closed; silently continuing without the PQ + // exchange would hand out classic tunnels, so treat the bind failure as fatal. + if pqkem.Strict() { + return fmt.Errorf("pqkem: strict mode enabled but transport bind failed: %w", pqErr) + } log.Errorf("pqkem: transport bind failed, exchange disabled: %v", pqErr) } else { cbHandler := pqCallbackHandler{ diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index 06fb85382..1610f29fb 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -258,11 +258,13 @@ func NewConn(config ConnConfig, services ServiceDependencies) (*Conn, error) { } if config.PQ != nil && config.PQStrict { - if k, err := wgtypes.GenerateKey(); err != nil { - connLog.Errorf("pqkem: failed to generate strict-mode sentinel key, strict fail-closed disabled for this peer: %v", err) - } else { - conn.pqStrictSentinelKey = &k + // The sentinel is what makes strict mode fail closed; if we cannot generate it we + // must not fall back to a usable key, so fail creating the conn instead. + k, err := wgtypes.GenerateKey() + if err != nil { + return nil, fmt.Errorf("generate pqkem strict-mode sentinel key: %w", err) } + conn.pqStrictSentinelKey = &k } return conn, nil diff --git a/client/internal/pqkem_transport.go b/client/internal/pqkem_transport.go index b8bf5c020..09eb53720 100644 --- a/client/internal/pqkem_transport.go +++ b/client/internal/pqkem_transport.go @@ -30,11 +30,19 @@ func newPQTransport(overlayIP netip.Addr) (*pqTransport, error) { if !overlayIP.IsValid() { return nil, fmt.Errorf("invalid overlay IP for pqkem transport") } + // Unmap an IPv4-mapped IPv6 address so AsSlice() yields 4 bytes, and pick the UDP + // network matching the overlay family (a hardcoded "udp4" fails on a v6 overlay and + // on a mapped v4 whose AsSlice() is 16 bytes). + overlayIP = overlayIP.Unmap() + network := "udp6" + if overlayIP.Is4() { + network = "udp4" + } ip := net.IP(overlayIP.AsSlice()) - conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: ip, Port: DefaultPort}) + conn, err := net.ListenUDP(network, &net.UDPAddr{IP: ip, Port: DefaultPort}) if err != nil { log.Debugf("pqkem: default port %d unavailable on %s (%v), using an ephemeral port", DefaultPort, overlayIP, err) - conn, err = net.ListenUDP("udp4", &net.UDPAddr{IP: ip, Port: 0}) + conn, err = net.ListenUDP(network, &net.UDPAddr{IP: ip, Port: 0}) if err != nil { return nil, fmt.Errorf("bind pqkem udp on overlay %s: %w", overlayIP, err) }