poc(audiobookshelf): expose pod directly to the NetBird mesh via sidecar #29

Merged
k.renner merged 1 commits from netbird-direct-audiobookshelf into master 2026-06-26 04:44:34 +00:00
Owner

Proof-of-concept: expose a service directly on the NetBird mesh

The standalone routing-peer approach can't work: Cilium socketLB only translates a pod's own socket connections, not traffic a peer forwards for mesh clients — so forwarded packets to any ClusterIP/LB VIP are blackholed (confirmed via conntrack: the flow stays [UNREPLIED]). A central terminating proxy would work but is extra machinery to maintain.

This PR validates the third option — the pod itself becomes a mesh peer:

  • A non-privileged netbird sidecar shares the audiobookshelf pod's network namespace and brings up wt0 there. The pod enrolls as audiobookshelf.nb.renner.uno.
  • Mesh clients connect straight to the pod's mesh IP:80 — traffic is delivered locally inside the netns. No L3 forwarding, socketLB never in the path.
  • NET_ADMIN/SYS_ADMIN/SYS_RESOURCE only (the WireGuard minimum); not privileged (the operator's default sidecar is privileged — we override that).
  • Reuses the existing reusable setup key (new sops secret in the audiobookshelf namespace). The existing audiobookshelf CNP already permits the sidecar's egress (world + kube-dns), so no policy change.

Validate (from a mesh peer)

dig audiobookshelf.nb.renner.uno          # → the pod's 100.96.x.x mesh IP
curl http://audiobookshelf.nb.renner.uno  # → audiobookshelf directly (no Traefik, no VIP)

A NetBird access policy must allow the client's group to reach this peer's group.

Scope / caveats

  • Names are under *.nb.renner.uno (NetBird's zone), not the canonical audiobooks.renner.uno. Mapping the canonical names is a separate optional CNAME layer.
  • Direct exposure = HTTP straight to the pod (no Traefik/TLS in front). Fine for the PoC; TLS/ingress semantics are a later decision.
  • One sidecar per exposed pod. If this holds up, the choice is manual sidecars vs. the NetBird operator for label-driven injection.

If validated, the now-defunct standalone netbird-ingress routing peer should be removed.

🤖 Generated with Claude Code

## Proof-of-concept: expose a service **directly** on the NetBird mesh The standalone routing-peer approach can't work: Cilium **socketLB only translates a pod's own socket connections**, not traffic a peer *forwards* for mesh clients — so forwarded packets to any ClusterIP/LB VIP are blackholed (confirmed via conntrack: the flow stays `[UNREPLIED]`). A central terminating proxy would work but is extra machinery to maintain. This PR validates the third option — **the pod itself becomes a mesh peer**: - A **non-privileged** netbird sidecar shares the audiobookshelf pod's network namespace and brings up `wt0` there. The pod enrolls as `audiobookshelf.nb.renner.uno`. - Mesh clients connect **straight to the pod's mesh IP:80** — traffic is delivered locally inside the netns. **No L3 forwarding, socketLB never in the path.** - `NET_ADMIN`/`SYS_ADMIN`/`SYS_RESOURCE` only (the WireGuard minimum); **not** privileged (the operator's default sidecar *is* privileged — we override that). - Reuses the existing reusable setup key (new sops secret in the `audiobookshelf` namespace). The existing audiobookshelf CNP already permits the sidecar's egress (`world` + kube-dns), so no policy change. ### Validate (from a mesh peer) ``` dig audiobookshelf.nb.renner.uno # → the pod's 100.96.x.x mesh IP curl http://audiobookshelf.nb.renner.uno # → audiobookshelf directly (no Traefik, no VIP) ``` A NetBird **access policy** must allow the client's group to reach this peer's group. ### Scope / caveats - Names are under `*.nb.renner.uno` (NetBird's zone), not the canonical `audiobooks.renner.uno`. Mapping the canonical names is a separate optional CNAME layer. - Direct exposure = HTTP straight to the pod (no Traefik/TLS in front). Fine for the PoC; TLS/ingress semantics are a later decision. - One sidecar per exposed pod. If this holds up, the choice is manual sidecars vs. the NetBird operator for label-driven injection. If validated, the now-defunct standalone `netbird-ingress` routing peer should be removed. 🤖 Generated with Claude Code
k.renner added 1 commit 2026-06-26 04:43:50 +00:00
Proof-of-concept for "direct mesh exposure" — the alternative to the
standalone routing peer, which can't deliver mesh traffic to cluster
LoadBalancer/ClusterIP VIPs (Cilium socket-LB only translates a pod's
OWN socket connections, not traffic a peer forwards on behalf of mesh
clients; confirmed via conntrack on netbird-ingress).

Here a non-privileged netbird sidecar shares the audiobookshelf pod's
network namespace, so the pod becomes its own mesh peer
(audiobookshelf.nb.renner.uno). Mesh clients connect straight to the
pod's mesh IP:80 — traffic is delivered locally inside the netns, so
there is no L3 forwarding and socket-LB is never in the path.

- non-privileged; NET_ADMIN/SYS_ADMIN/SYS_RESOURCE (WireGuard minimum)
- reuses the existing reusable setup key (new sops secret in this ns)
- existing audiobookshelf CNP already permits the sidecar's egress
  (world + kube-dns), so no network-policy change needed

Validation: from a mesh peer, `dig audiobookshelf.nb.renner.uno` then
`curl http://audiobookshelf.nb.renner.uno` — expect the app directly,
no Traefik, no VIP. A NetBird access policy must allow the client's
group to reach this peer's group.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit ba996c6073 into master 2026-06-26 04:44:34 +00:00
Sign in to join this conversation.