wgkey as peer id

This commit is contained in:
crn4
2026-05-28 12:52:15 +02:00
parent 21cfec93d4
commit 596952265d
2 changed files with 30 additions and 48 deletions
+11 -9
View File
@@ -25,10 +25,9 @@ import (
// components struct round-trips through Calculate exactly the way the
// server-side typed components would.
//
// Synthetic ID scheme (underscore-separated, visually distinct from the xid
// format Calculate would put in log lines under the legacy path):
// ID scheme on the client side:
//
// Peers "p_<wire_index>" // envelope.peers is index-addressed
// Peers base64(wg_pub_key) // stable across snapshots
// Groups "g_<account_seq_id>"
// Policies "pol_<account_seq_id>" // 1 rule per policy
// Routes "r_<account_seq_id>"
@@ -74,16 +73,20 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents,
c.DNSSettings = &types.DNSSettings{}
}
// Phase 1: peers. The envelope's peers slice is index-addressed; we
// build a peerOrder lookup for downstream references. Peer.ID is
// synthesized from the peer's wire index — wire format ships no xid
// for peers (and never has).
// Phase 1: peers. The envelope's peers slice is index-addressed on the
// wire; we re-key by the peer's WireGuard public key (base64) so the
// in-memory components struct uses a stable identifier across
// snapshots. peerIDByIndex lets downstream phases resolve wire indexes
// back to that key.
peerIDByIndex := make([]string, len(full.Peers))
for idx, pc := range full.Peers {
if pc == nil {
return nil, fmt.Errorf("invalid envelope: peers[%d] is nil", idx)
}
peerID := synthPeerID(uint32(idx))
if len(pc.WgPubKey) == 0 {
return nil, fmt.Errorf("invalid envelope: peers[%d] missing wg_pub_key", idx)
}
peerID := encodeWgKeyBase64(pc.WgPubKey)
peer := decodePeerCompact(pc, peerID, full.AgentVersions)
c.Peers[peerID] = peer
peerIDByIndex[idx] = peerID
@@ -494,7 +497,6 @@ func synthID(prefix string, n uint32) string {
return string(buf)
}
func synthPeerID(idx uint32) string { return synthID("p_", idx) }
func synthGroupID(seq uint32) string { return synthID("g_", seq) }
func synthPolicyID(seq uint32) string { return synthID("pol_", seq) }
func synthRouteID(seq uint32) string { return synthID("r_", seq) }
+19 -39
View File
@@ -1,12 +1,10 @@
package networkmap
import (
"bytes"
"context"
"encoding/base64"
"fmt"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/shared/management/proto"
)
@@ -43,14 +41,18 @@ func EnvelopeToNetworkMap(ctx context.Context, env *proto.NetworkMapEnvelope, lo
return nil, fmt.Errorf("decode envelope: %w", err)
}
// Find the receiving peer in the decoded components by WG key so we can
// derive its capabilities and set components.PeerID for Calculate(). The
// envelope.peers list is index-addressed; we synthesized IDs as "p<idx>".
localPeerID, localPeer := findPeerByWgKey(components, localPeerKey)
// Find the receiving peer in the decoded components by WG key.
// c.Peers is keyed by canonical base64 of the raw 32-byte pub key
// (decoder re-encodes the bytes off the wire). The caller may pass a
// non-canonical encoding (some persisted production keys carry
// non-zero trailing padding bits that survived a legacy import), so
// round-trip through raw bytes once to canonicalize before lookup.
canonicalKey := canonicalizeWgKey(localPeerKey)
localPeer := components.Peers[canonicalKey]
if localPeer == nil {
return nil, fmt.Errorf("receiving peer (wg_key prefix %q) not found among %d decoded peers — components have no PeerID, Calculate would return empty", trimKey(localPeerKey), len(components.Peers))
}
components.PeerID = localPeerID
components.PeerID = canonicalKey
includeIPv6 := localPeer.SupportsIPv6() && localPeer.IPv6.IsValid()
useSourcePrefixes := localPeer.SupportsSourcePrefixes()
@@ -174,37 +176,15 @@ func trimKey(s string) string {
return s
}
// findPeerByWgKey locates the receiving peer in the decoded components by
// matching its WireGuard public key. Compares raw 32-byte decode output —
// not the base64 string — because production data has occasional non-canonical
// padding bits that round-trip through the envelope's `bytes wg_pub_key`
// field, canonicalising the encoding (semantically equivalent key, different
// string). Decodes `wgKey` once up front and reuses a stack buffer in the
// loop so an N-peer search is ~zero-alloc.
func findPeerByWgKey(c *types.NetworkMapComponents, wgKey string) (string, *nbpeer.Peer) {
const wgKeyRawLen = 32
var (
targetRaw [wgKeyRawLen]byte
haveRaw bool
)
if n, err := base64.StdEncoding.Decode(targetRaw[:], []byte(wgKey)); err == nil && n == wgKeyRawLen {
haveRaw = true
// canonicalizeWgKey normalises a base64-encoded WireGuard public key so it
// matches the canonical encoding emitted by the envelope decoder. Returns
// the input unchanged when it does not decode to 32 raw bytes (caller will
// hit a miss in the peer map and surface the error).
func canonicalizeWgKey(s string) string {
raw, err := base64.StdEncoding.DecodeString(s)
if err != nil || len(raw) != 32 {
return s
}
var peerRaw [wgKeyRawLen]byte
for id, p := range c.Peers {
if p == nil {
continue
}
if p.Key == wgKey {
return id, p
}
if !haveRaw {
continue
}
n, err := base64.StdEncoding.Decode(peerRaw[:], []byte(p.Key))
if err == nil && n == wgKeyRawLen && bytes.Equal(peerRaw[:], targetRaw[:]) {
return id, p
}
}
return "", nil
return base64.StdEncoding.EncodeToString(raw)
}