From 8e82b428f5347bc447d5b965f82309887bb1795a Mon Sep 17 00:00:00 2001 From: Konstantin Renner Date: Fri, 26 Jun 2026 06:43:22 +0200 Subject: [PATCH] poc(audiobookshelf): expose pod directly to NetBird mesh via sidecar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../apps/audiobookshelf/deployment.yaml | 39 +++++++++++++++++++ .../apps/audiobookshelf/kustomization.yaml | 1 + .../audiobookshelf/netbird-secret.sops.yaml | 23 +++++++++++ 3 files changed, 63 insertions(+) create mode 100644 kubernetes/apps/audiobookshelf/netbird-secret.sops.yaml diff --git a/kubernetes/apps/audiobookshelf/deployment.yaml b/kubernetes/apps/audiobookshelf/deployment.yaml index 745dbe35..e87f25c7 100644 --- a/kubernetes/apps/audiobookshelf/deployment.yaml +++ b/kubernetes/apps/audiobookshelf/deployment.yaml @@ -110,7 +110,46 @@ spec: capabilities: drop: ["ALL"] add: ["CHOWN", "DAC_OVERRIDE", "FOWNER", "SETGID", "SETUID"] + # PROOF-OF-CONCEPT: expose this pod DIRECTLY to the NetBird mesh. + # The sidecar shares this pod's network namespace, so it brings up + # wt0 here and 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 this + # netns, so there is NO L3 forwarding and Cilium socket-LB is never + # in the path (the failure mode the standalone routing peer hit). + # NOT privileged; NET_ADMIN/SYS_ADMIN/SYS_RESOURCE is the minimum + # for the WireGuard interface. Reuses the existing reusable setup + # key; peer named via NB_HOSTNAME. + - name: netbird + image: netbirdio/netbird:0.73.2 # {"$imagepolicy": "flux-system:netbird-ingress"} + env: + - name: NB_SETUP_KEY + valueFrom: + secretKeyRef: + name: netbird-sidecar-setup-key + key: setup-key + - name: NB_MANAGEMENT_URL + value: "https://netbird.renner.uno" + - name: NB_HOSTNAME + value: "audiobookshelf" + - name: NB_LOG_LEVEL + value: "info" + securityContext: + runAsNonRoot: false + capabilities: + add: ["NET_ADMIN", "SYS_RESOURCE", "SYS_ADMIN"] + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + memory: 256Mi + volumeMounts: + - name: netbird-state + mountPath: /var/lib/netbird volumes: + - name: netbird-state + emptyDir: {} - name: config persistentVolumeClaim: claimName: audiobookshelf-config diff --git a/kubernetes/apps/audiobookshelf/kustomization.yaml b/kubernetes/apps/audiobookshelf/kustomization.yaml index ab78bd62..27fdc5d2 100644 --- a/kubernetes/apps/audiobookshelf/kustomization.yaml +++ b/kubernetes/apps/audiobookshelf/kustomization.yaml @@ -1,5 +1,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: + - netbird-secret.sops.yaml - deployment.yaml - ingress.yaml diff --git a/kubernetes/apps/audiobookshelf/netbird-secret.sops.yaml b/kubernetes/apps/audiobookshelf/netbird-secret.sops.yaml new file mode 100644 index 00000000..5c25eb41 --- /dev/null +++ b/kubernetes/apps/audiobookshelf/netbird-secret.sops.yaml @@ -0,0 +1,23 @@ +apiVersion: ENC[AES256_GCM,data:vqI=,iv:kN/dA9gt/tg51rdRW03mwhAubbaM6DtlH3+RUAUUONs=,tag:dLLfc+1OsvaWzy0o1z6aAw==,type:str] +kind: ENC[AES256_GCM,data:Jr3WCGzm,iv:DB4vYFhn0n6vaKK2BbJvYN7USuDWDxyXq9vUesQHycg=,tag:zTMxG/UeZgT13ekUBPEpjA==,type:str] +metadata: + name: ENC[AES256_GCM,data:L7xNmIbARsy82a4Vtm5Aez7u6+HtzfhnCw==,iv:VxxN8U53qarYotB7MJGdtLc7PcRGpkKJ6vl5v8w6H8I=,tag:/exbkZO1wwGyAIiGeAXITQ==,type:str] + namespace: ENC[AES256_GCM,data:ww+0DmtuT1rsZsgU/qk=,iv:Vm86ObK2Zuhpq/vfHGk9VNo7Q6/Q//fqBf4szoloFtk=,tag:ey6GrZQZ42j1+OEiaxaEYQ==,type:str] +type: ENC[AES256_GCM,data:anWESb9n,iv:zPccE2572d/RSXND6PopwGGbhQ77NdhL2ANBkigMUSg=,tag:GXnENaRnwvDuCq9ZnrX9ww==,type:str] +stringData: + setup-key: ENC[AES256_GCM,data:t4XfNeA97RBpB9gM82s00ZB8aSctfEWL985O9tNvt2kujDXL,iv:QOJGzOrF6C0aeBmBOdS6Egtw/rfHK6Nh9m14DA7FrqM=,tag:P9JmWpsxlpOx2GKVp85ZBg==,type:str] +sops: + age: + - recipient: age1yqjvqkwwwxdj7mr546zfs5ckzuwndp69ya2n2drd4vypwns8w4ys04qfy3 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2RTdFTlV2NW0rZmxVQ2Fm + Q2U2VTgzU2RWQzRuOHAvbFM4RG1BK1g4WXh3CjIzbG5sTXdSR2FTVWNNa0Y1VEdP + MUZRWmkzeU1OWCtwNm11bEFjZlhJS0kKLS0tIEZPSW91bEVIRUJrWUpTRHRHRzYr + UGRZbFU1ZnVSSE5aRHN6T3BDOUl5NTQKHiiHyoMMHrSyDeVCY2Lw7q/GWNrok+PS + Yt3YVdGt2oaqPjSOPRsyLfzRljn9fVwVpiTLIH3ohHABKfm4FK/tPg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-06-26T04:41:35Z" + mac: ENC[AES256_GCM,data:HeOfc8RshmFGZQThYfuM6nUBI1pWtzP4s48Wjjq5uY1aAtbIkxDeDR+wXNLHq7AdxtSO8vA+SloH0WcBTAu9yZ2pgPZoXnRo5x6a0QAuczXHaFsMp4mK619rr5qFyT9yytEk6+XQW5AhziryW01GzpcOV59tDze2o9BTtL+9Rvk=,iv:ejCqFz+3xOMENid9ZTkgexpFul6yqHRuBYGPSsTHnWY=,tag:a/x+4ox88DBYMdC5Py5vJQ==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 -- 2.54.0