From 3275dd23a56f2934a65aa6f486478800b350162b Mon Sep 17 00:00:00 2001 From: Calin Teodor Date: Sun, 7 Jun 2026 10:45:06 +0300 Subject: [PATCH 1/3] feat(tunnel): SetReplyWhitelist proxy for keyexchange Manager (PILOT-344 wiring) --- pkg/daemon/tunnel.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/daemon/tunnel.go b/pkg/daemon/tunnel.go index 346b0978..eb1ca419 100644 --- a/pkg/daemon/tunnel.go +++ b/pkg/daemon/tunnel.go @@ -559,6 +559,13 @@ func (tm *TunnelManager) SetRekeyWhitelist(nodeIDs []uint32) { tm.rekeyWhitelist.Store(&wm) } +// SetReplyWhitelist is the cmd/daemon-facing proxy that forwards to the +// embedded keyexchange.Manager (PILOT-344). Trusted peers bypass the +// 1-second asymmetric-recovery reply gate. +func (tm *TunnelManager) SetReplyWhitelist(nodeIDs []uint32) { + tm.kx.SetReplyWhitelist(nodeIDs) +} + // SetBeaconAddr configures the beacon address for NAT hole-punching and relay. // Thin shim over routing.Manager.SetBeaconAddr. func (tm *TunnelManager) SetBeaconAddr(addr string) error { From b9c1aaaf5d3e6834d8fd713c322584f0689d8f59 Mon Sep 17 00:00:00 2001 From: Calin Teodor Date: Sun, 7 Jun 2026 10:45:07 +0300 Subject: [PATCH 2/3] feat(daemon): SetReplyWhitelist + SetRekeyWhitelist proxies (PILOT-344, PILOT-345) --- pkg/daemon/daemon.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index a5e838de..fef4d92b 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -563,6 +563,23 @@ func (d *Daemon) SetSYNWhitelist(nodeIDs []uint32) { d.synWhitelist.Store(&m) } +// SetReplyWhitelist proxies to the embedded keyexchange.Manager (PILOT-344). +// Trusted peers bypass the 1-second asymmetric-recovery reply gate. +func (d *Daemon) SetReplyWhitelist(nodeIDs []uint32) { + if d.tunnels != nil { + d.tunnels.SetReplyWhitelist(nodeIDs) + } +} + +// SetRekeyWhitelist proxies to the TunnelManager (PILOT-345). Trusted +// peers bypass the 3-second rekey-request interval and the 4096 +// requester-map cap. +func (d *Daemon) SetRekeyWhitelist(nodeIDs []uint32) { + if d.tunnels != nil { + d.tunnels.SetRekeyWhitelist(nodeIDs) + } +} + func (d *Daemon) Start() error { // Warm the hostname cache from disk before the IPC server comes up, // so the first send-message after restart hits the cache instead of From a581ecbb019bad74ffb4d16b06b3455039fefa2c Mon Sep 17 00:00:00 2001 From: Calin Teodor Date: Sun, 7 Jun 2026 10:45:09 +0300 Subject: [PATCH 3/3] feat(cmd): wire -syn/reply/rekey-whitelist CLI flags + env (PILOT-343/344/345) --- cmd/daemon/main.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/cmd/daemon/main.go b/cmd/daemon/main.go index eb97d890..40660efa 100644 --- a/cmd/daemon/main.go +++ b/cmd/daemon/main.go @@ -67,6 +67,13 @@ func main() { synRate := flag.Int("syn-rate-limit", 0, "max SYN packets per second (default 100)") maxConnsPerPort := flag.Int("max-conns-per-port", 0, "max connections per port (default 1024)") maxConnsTotal := flag.Int("max-conns-total", 0, "max total connections (default 4096)") + // PILOT-343/344/345 rate-limit whitelists. Comma-separated trusted-peer + // node IDs (decimal uint32). Env fallbacks let deployments configure + // without editing flag strings. Empty default preserves backwards + // compatibility — fleets that don't set them behave exactly as before. + synWhitelist := flag.String("syn-whitelist", "", "PILOT-343: comma-separated trusted source node IDs that bypass the SYN rate limit. Env: PILOT_SYN_WHITELIST.") + replyWhitelist := flag.String("reply-whitelist", "", "PILOT-344: comma-separated trusted peer node IDs that bypass the keyexchange reply interval gate. Env: PILOT_REPLY_WHITELIST.") + rekeyWhitelist := flag.String("rekey-whitelist", "", "PILOT-345: comma-separated trusted peer node IDs that bypass the tunnel-rekey interval and 4096 cap. Env: PILOT_REKEY_WHITELIST.") timeWait := flag.Duration("time-wait", 0, "TIME_WAIT duration (default 10s)") public := flag.Bool("public", false, "make this node's endpoint publicly visible (default: private)") relayOnly := flag.Bool("relay-only", false, "hide real_addr from peers; reach this node only via beacon-relay path. Privacy stance: peers cannot enumerate this daemon's public IP. Trade-off: relay adds one beacon hop. Default false (current direct-first behavior).") @@ -260,6 +267,15 @@ func main() { if err := rt.StartPlugins(context.Background()); err != nil { log.Fatalf("plugin startup: %v", err) } + + // PILOT-343/344/345: apply rate-limit whitelists BEFORE Start so the + // first inbound SYN/PILA/encrypted-frame already sees them. Each + // helper tolerates empty/garbage input — bad tokens log a warning + // and are dropped so a typo in env doesn't fail-fast the daemon. + applyNodeIDWhitelist("syn", *synWhitelist, "PILOT_SYN_WHITELIST", d.SetSYNWhitelist) + applyNodeIDWhitelist("reply", *replyWhitelist, "PILOT_REPLY_WHITELIST", d.SetReplyWhitelist) + applyNodeIDWhitelist("rekey", *rekeyWhitelist, "PILOT_REKEY_WHITELIST", d.SetRekeyWhitelist) + if err := d.Start(); err != nil { log.Fatalf("daemon start: %v", err) } @@ -317,3 +333,43 @@ func parseNetworkIDs(s string) []uint16 { } return ids } + +// parseNodeIDs parses a comma-separated string of uint32 node IDs. +// Garbage tokens are logged and skipped — a typo in env shouldn't +// fail-fast the daemon. +func parseNodeIDs(s string) []uint32 { + if s == "" { + return nil + } + var ids []uint32 + for _, p := range strings.Split(s, ",") { + p = strings.TrimSpace(p) + if p == "" { + continue + } + n, err := strconv.ParseUint(p, 10, 32) + if err != nil { + log.Printf("warning: invalid node ID %q: %v", p, err) + continue + } + ids = append(ids, uint32(n)) + } + return ids +} + +// applyNodeIDWhitelist resolves flag, falls back to env, parses the +// comma-separated list, and applies via the provided setter. Logs one +// line on success showing the count so deployments can sanity-check +// what landed. +func applyNodeIDWhitelist(name, flagVal, envName string, set func([]uint32)) { + raw := flagVal + if raw == "" { + raw = os.Getenv(envName) + } + if raw == "" { + return + } + ids := parseNodeIDs(raw) + set(ids) + log.Printf("%s-rate-limit whitelist configured: %d node(s)", name, len(ids)) +}