feat(ingress): NetBird mesh ingress (routing peer) alongside cloudflared #27

Merged
k.renner merged 2 commits from worktree-netbird-mesh-ingress into master 2026-06-25 17:49:19 +00:00
Owner

What

Adds a second, parallel ingress path over the NetBird overlay mesh (100.96.0.0/16), in addition to the existing Cloudflare path. Iteration 1 goal: make services reachable from mesh peers.

A new in-cluster NetBird routing peer (kubernetes/infrastructure/netbird-ingress/) enrolls with the self-hosted management server netbird.renner.uno and advertises the Traefik VIP into the mesh. Because mesh DNS (10.2.40.53) already resolves <app>.renner.uno to that VIP, every Traefik-fronted service becomes mesh-reachable with zero per-service work — existing Ingress objects, TLS certs and DNS are untouched.

mesh peer (100.96.x.x) --wg--> netbird-ingress pod --(masq + fwd)--> Traefik VIP --> service

Security

  • Modeled on the cloudflared Deployment; held to the same in-cluster firewall discipline — a dedicated netbird-ingress-egress CiliumNetworkPolicy restricts egress to Traefik (label selector) + DNS + the NetBird control/data plane only. No hardcoded IPs in policy.
  • Not privileged (unlike the upstream NetBird operator). NET_ADMIN/SYS_ADMIN/SYS_RESOURCE are the irreducible minimum for an L3 WireGuard routing peer.

Required one-time NetBird-side setup (not zero-touch)

NetBird stores routes/policies in its DB (dashboard/API, not Ansible-templated). Before this works:

  1. Create a reusable + ephemeral setup key on netbird.renner.uno (auto-group k8s-ingress-router).
  2. Replace the placeholder in secret.sops.yaml with the real key (sops kubernetes/infrastructure/netbird-ingress/secret.sops.yaml).
  3. Add a Network Route for the Traefik VIP via the netbird-ingress peer, distributed to the mesh groups that should reach cluster services.
  4. Add an access policy for those groups to the route.

Validate before declaring done (staged)

  • Pod Ready, netbird status Connected, peer visible in dashboard.
  • From one mesh peer (LAN disconnected): curl -v https://audiobooks.renner.uno to TLS + 200.
  • Key risk: the pod forwards to a LoadBalancer VIP — confirm Cilium DNATs pod-forwarded traffic onto a Traefik backend. If forwarding to the LB IP fails, fall back to the Traefik ClusterIP.
  • Confirm no regression on the Cloudflare path and no Home Assistant ip_ban trip from the masqueraded peer IP.

Notes: NetBird overlay is IPv4-only (100.96.0.0/16), so the mesh data path is v4; the IPv6 Traefik VIP is unused on this path.

🤖 Generated with Claude Code

## What Adds a **second, parallel ingress path over the NetBird overlay mesh** (`100.96.0.0/16`), in addition to the existing Cloudflare path. Iteration 1 goal: make services reachable from mesh peers. A new in-cluster **NetBird routing peer** (`kubernetes/infrastructure/netbird-ingress/`) enrolls with the self-hosted management server `netbird.renner.uno` and advertises the Traefik VIP into the mesh. Because mesh DNS (`10.2.40.53`) already resolves `<app>.renner.uno` to that VIP, **every Traefik-fronted service becomes mesh-reachable with zero per-service work** — existing Ingress objects, TLS certs and DNS are untouched. ``` mesh peer (100.96.x.x) --wg--> netbird-ingress pod --(masq + fwd)--> Traefik VIP --> service ``` ## Security - Modeled on the `cloudflared` Deployment; held to the **same in-cluster firewall discipline** — a dedicated `netbird-ingress-egress` CiliumNetworkPolicy restricts egress to **Traefik (label selector) + DNS + the NetBird control/data plane only**. No hardcoded IPs in policy. - **Not** `privileged` (unlike the upstream NetBird operator). `NET_ADMIN`/`SYS_ADMIN`/`SYS_RESOURCE` are the irreducible minimum for an L3 WireGuard routing peer. ## Required one-time NetBird-side setup (not zero-touch) NetBird stores routes/policies in its DB (dashboard/API, not Ansible-templated). Before this works: 1. Create a **reusable + ephemeral** setup key on `netbird.renner.uno` (auto-group `k8s-ingress-router`). 2. Replace the placeholder in `secret.sops.yaml` with the real key (`sops kubernetes/infrastructure/netbird-ingress/secret.sops.yaml`). 3. Add a **Network Route** for the Traefik VIP via the `netbird-ingress` peer, distributed to the mesh groups that should reach cluster services. 4. Add an **access policy** for those groups to the route. ## Validate before declaring done (staged) - Pod `Ready`, `netbird status` Connected, peer visible in dashboard. - From **one** mesh peer (LAN disconnected): `curl -v https://audiobooks.renner.uno` to TLS + 200. - **Key risk:** the pod *forwards* to a LoadBalancer VIP — confirm Cilium DNATs pod-forwarded traffic onto a Traefik backend. If forwarding to the LB IP fails, fall back to the Traefik ClusterIP. - Confirm no regression on the Cloudflare path and no Home Assistant `ip_ban` trip from the masqueraded peer IP. Notes: NetBird overlay is IPv4-only (`100.96.0.0/16`), so the mesh data path is v4; the IPv6 Traefik VIP is unused on this path. 🤖 Generated with Claude Code
k.renner added 1 commit 2026-06-25 17:40:04 +00:00
Adds a second, parallel ingress path over the NetBird overlay mesh
(100.96.0.0/16), in addition to the existing Cloudflare path. A new
in-cluster NetBird routing peer enrolls with the self-hosted management
server (netbird.renner.uno) and advertises the Traefik VIP (10.2.99.86)
into the mesh, so every Traefik-fronted service becomes reachable from
mesh peers with no per-service work — mesh DNS (10.2.40.53) already
resolves <app>.renner.uno to that VIP; the only gap was that the
bgp-pool CIDR wasn't routable over the overlay.

Modeled on the cloudflared Deployment and held to the same in-cluster
firewall discipline: a dedicated CiliumNetworkPolicy
(netbird-ingress-egress) restricts egress to traefik + DNS + the NetBird
control/data plane only. Unlike the upstream NetBird operator the pod is
NOT privileged; the NET_ADMIN/SYS_ADMIN/SYS_RESOURCE caps are the
irreducible minimum for an L3 WireGuard routing peer.

- kubernetes/infrastructure/netbird-ingress/ (deployment, sops secret, kustomization)
- image-automation policy pinned to bare-numeric semver (excludes -rootless/-rc)
- netbird-ingress-egress CNP in network-policies/ingress-isolation.yaml

Requires one-time NetBird-side setup (dashboard/API): create a reusable
+ ephemeral setup key (auto-group k8s-ingress-router), replace the
placeholder in secret.sops.yaml, add a network route for 10.2.99.86/32
via that peer, and an access policy for the consuming mesh groups.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner added 1 commit 2026-06-25 17:47:52 +00:00
Replaces the placeholder in secret.sops.yaml with the reusable+ephemeral
setup key created on netbird.renner.uno (auto-group k8s-ingress-router).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit 87b4c0145a into master 2026-06-25 17:49:19 +00:00
Sign in to join this conversation.