Skip to main content

IP Interaction Map

This document maps every place in the codebase where an IP address is chosen for network communication.

Design Principle

All inter-node traffic flows through the WireGuard overlay network (10.10.0.0/16). This means Docker Swarm gossip/Raft, K3s API/kubelet, NFS mounts, and S3 endpoints are all encrypted in transit via WireGuard tunnels — never sent in the clear over public IPs.

Public IPs are only used where traffic must originate or terminate outside the WireGuard network: the WireGuard UDP endpoints themselves, DNS responses for developer machines, Cloudflare records, and agent registration.

Developer machine (not on WireGuard)

│ DNS query → dnsmasq returns PUBLIC IP
│ HTTP :80 → EasyHAProxy (binds 0.0.0.0)


┌──────────── Node A (public: 10.108.0.3, overlay: 10.10.0.1) ────────────┐
│ WireGuard wg-nimbus ◄──── UDP endpoint = PUBLIC IP │
│ │ │
│ │ overlay tunnel │
│ ▼ │
│ Docker Swarm / K3s / NFS ──── all traffic on OVERLAY IPs ──────────► │
└──────────────────────────────────────────────────────────────────────┬────┘

WireGuard tunnel
(encrypted UDP)

┌──────────── Node B (public: 10.108.0.4, overlay: 10.10.0.2) ───────┴────┐
│ WireGuard wg-nimbus ◄──── UDP endpoint = PUBLIC IP │
│ │ │
│ │ overlay tunnel │
│ ▼ │
│ Docker Swarm / K3s / NFS ──── all traffic on OVERLAY IPs │
└──────────────────────────────────────────────────────────────────────────┘

Legend

  • PUBLIC = node.IPAddress (e.g., 10.108.0.4) — the node's real/public IP
  • OVERLAY = nodeOverlayIP() / node.WireGuardIP (e.g., 10.10.0.1) — WireGuard tunnel IP

WireGuard Layer

WhereIP UsedWho ConnectsNotes
handlers.go:104 — WireGuard peer EndpointPUBLICOther nodes' WG clients (external UDP)Must be routable from the internet
handlers.go:106 — WireGuard peer AllowedIPOVERLAYInternal tunnel routingIP range accessible through the tunnel
handlers.go:119 — WireGuard config AddressOVERLAYNode's own interfaceAssigned overlay IP

Docker Swarm

All Docker Swarm communication (gossip protocol, Raft consensus, service mesh, overlay networking) travels over the WireGuard overlay. The swarm is initialized with --advertise-addr set to the overlay IP, so all inter-node swarm traffic is encrypted via WireGuard.

WhereIP UsedWho ConnectsNotes
swarm.go:113swarm init --advertise-addrOVERLAYSwarm nodes (gossip, Raft on :2377)All swarm mesh traffic uses this address
swarm.go:135swarm join manager IPOVERLAYWorker nodes joining swarmdocker swarm join overlay:2377
swarm.go:149,153setup_local_dns manager IPOVERLAYAgent nodes (DNS on swarm nodes)Nodes are on WireGuard, so overlay is reachable
swarm.go:322deploy_swarm_dnsmasq response IPPUBLICExternal host/developer machineHost is NOT on WireGuard, needs public IP

Docker Swarm — Exposed Ports

Published ports (-p) bind on 0.0.0.0 (all interfaces), so they are reachable via both public and overlay IPs.

WherePortWho ConnectsNotes
executor.go:440 — EasyHAProxy -p 80:800.0.0.0:80External clients via public IPHTTP load balancer
executor.go:441 — EasyHAProxy -p 443:4430.0.0.0:443External clients via public IPHTTPS load balancer
executor.go:470 — dnsmasq -p 5053:530.0.0.0:5053DNS queries (via iptables DNAT)Wildcard DNS for swarm domains

Kubernetes (K3s)

All K3s inter-node communication (kubelet, etcd, agent join) travels over the WireGuard overlay. --node-ip and --server are set to overlay IPs.

The kubeconfig endpoint (what kubectl connects to) is configurable via kubeconfig_endpoint in api.yaml:

  • "public" (default) — node's public IP, works from any machine
  • "overlay" — WireGuard IP, only works from other nodes
  • "<ip>" — a specific IP or hostname (e.g., a load balancer)

The K3s TLS certificate includes both the overlay IP and the kubeconfig endpoint IP via --tls-san, so the cert is valid for both internal and external access.

WhereIP UsedWho ConnectsNotes
kubernetes.go:80k3s server --node-ipOVERLAYWorkers (inter-node)K3s listens on overlay for cluster traffic
kubernetes.go:80k3s server --tls-sanOVERLAY + KUBECONFIGBothCert valid for both IPs
kubernetes.go:223k3s agent --server URLOVERLAYWorker to control planehttps://overlay:6443
kubernetes.go:227k3s agent --node-ipOVERLAYControl plane to workerkubelet address
nodes.go:504 — worker --node-ip (from init complete)OVERLAYControl plane to workerSame as above
executor.go — kubeconfig server addressKUBECONFIGkubectl (external)Configurable via kubeconfig_endpoint

K3s — Exposed Ports

WherePortWho ConnectsNotes
K3s API server:6443Workers + kubectlListens on all interfaces; cert covers both IPs
K3s EasyHAProxy (HelmChart):80, :443External clientsDeployed via Helm CRD, hostNetwork

NFS Volumes

NFS traffic between nodes uses the WireGuard overlay, so volume data is encrypted in transit.

WhereIP UsedWho ConnectsNotes
volume.go:39 — NFS server IP stored in DBOVERLAYOther nodes mounting NFSStored as vol.ServerIP
compute.go:133 — NFS mount in create_serviceOVERLAYDocker containers (cross-node)addr=overlay,rw,nolock,soft
compute.go:474 — NFS mount in instance_mount_addOVERLAYDocker containers (cross-node)Same as above

S3 (MinIO)

WhereIP UsedWho ConnectsNotes
s3.go:61 — S3 endpoint URLOVERLAYInternal agents/containershttp://overlay:9000

Cloudflare DNS

WhereIP UsedWho ConnectsNotes
compute.go:190 — Cloudflare A recordPUBLICInternet DNS queriesExternal-facing DNS, must be public

CLI DNS Setup (host machine)

The host machine running nimbus dns setup is not on the WireGuard network, so it must use the public IP to reach dnsmasq on the manager node.

WhereIP UsedWho ConnectsNotes
main.go:584resolvectl dns targetPUBLICHost's systemd-resolvedHost is not on WireGuard
main.go:591 — iptables DNAT 53->5053PUBLICHost DNS queriesRedirects to dnsmasq

Agent Registration

WhereIP UsedWho ConnectsNotes
main.go:315 — agent advertise_ip configPUBLICAgent to API serverHow the API knows the node's public IP

Summary

Traffic TypeIP TypeEncrypted?Examples
Node-to-nodeOVERLAYYes (WireGuard)Swarm gossip/Raft, K3s API/kubelet, NFS mounts, S3, agent-side DNS
External-facingPUBLICNo (plain)WG endpoints, dnsmasq responses, Cloudflare, CLI DNS, agent registration
Published ports0.0.0.0No (plain)EasyHAProxy :80/:443, dnsmasq :5053