Compare commits

..
Author SHA1 Message Date
claude-botandClaude Fable 5.1 6008d59c9f fix(reloader): restart audiobookshelf and immich-server on ConfigMap change
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Both consume their ConfigMap with envFrom, which Kubernetes does not
watch: a changed value applies in git and in the cluster while the
running container keeps the environment it started with. Paperless hit
exactly this in #739 and got the annotation in #741; these two were the
remaining envFrom consumers without one.

ftp-scanner, the third Deployment named in the issue, needs nothing: its
ConfigMap comes from a configMapGenerator that deliberately keeps the
content-hash suffix, so any change renames the ConfigMap, mutates the
pod template and rolls the pod on its own.

Reloader already watches all namespaces (kubernetes/infrastructure/reloader).

Fixes #742

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 06:46:12 +02:00
k.renner 5aa6c0609d Merge pull request 'fix(netbird-proxy-services): pin the digest the master build produced' (#818) from netbird-proxy-protocol-digest into master
ci/woodpecker/cron/e2e Pipeline failed
ci/woodpecker/cron/security-scan Pipeline was successful
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#818
2026-09-07 11:52:17 +00:00
claude-botandClaude Fable 5.1 0ebe113abb fix(netbird-proxy-services): pin the digest the master build produced
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
#817 pinned the digest from the branch build (pipeline 8452). Merging
#816 rebuilt the same date tag on master (pipeline 8457), Gitea kept
only the new manifest, and the pod has been in ImagePullBackOff since:
"failed to resolve reference ...@sha256:0d266b02...". With the Recreate
strategy no controller is running, so the services are unchanged.

The kaniko step of the master build pushed sha256:caffa308...; that is
the digest to pin. Comment states the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 13:47:22 +02:00
k.renner 1723f73b5d Merge pull request 'feat(netbird-proxy-services): roll the PROXY-protocol build (2/2)' (#817) from netbird-proxy-protocol-deploy into master
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#817
2026-09-07 11:34:52 +00:00
k.renner 8d8df3b635 Merge pull request 'feat(netbird): carry the WAN client address through the passthrough edge (1/2)' (#816) from netbird-proxy-protocol into master
ci/woodpecker/push/build-ci-images Pipeline was successful
Reviewed-on: k.renner/homelab#816
2026-09-07 11:34:43 +00:00
claude-botandClaude Fable 5.1 29c91769a7 feat(netbird-proxy-services): roll the PROXY-protocol build (2/2)
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Pins the controller image by digest. The registry tag is the build
date and pipeline 8452 rebuilt today's tag in place, so the tag alone
cannot carry a new build to the cluster; the digest can.

Merge only after the Traefik change from 1/2 has reconciled: from this
build on every service target sends a PROXY protocol v2 header, and
Traefik drops such connections unless websecure trusts the pod CIDRs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 13:32:06 +02:00
claude-botandClaude Fable 5.1 2c47c68a8b feat(netbird): carry the WAN client address through the passthrough edge
ci/woodpecker/push/build-ci-images Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
TLS-passthrough services hand Traefik a raw TCP stream, so every WAN
request now logs the routing peer's pod IP (10.244.4.64/.112 in the
access log) instead of the client. CrowdSec's detection parses exactly
that log; with the HTTP-mode services gone it would attribute every
external request to two pod IPs, and a re-armed bouncer would repeat the
2026-06-27 mass ban.

The controller now sets options.proxy_protocol on every target (PROXY
protocol v2, TCP/TLS modes only per NetBird) and treats a service
without it as out of date, so the eleven existing services are updated
on the next pass. Traefik's websecure entrypoint accepts the header from
the pod CIDRs, mirroring forwardedHeaders.trustedIPs. Traefik takes
connections with or without a header from a trusted IP, so mesh and LAN
traffic that carries none is unaffected and the Traefik change can land
before the controller image.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 13:29:48 +02:00
k.renner ceff817d88 Merge pull request 'fix(netbird-proxy-services): numeric runAsUser so runAsNonRoot can be verified' (#815) from netbird-proxy-services-uid into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#815
2026-09-07 10:48:04 +00:00
5 changed files with 31 additions and 11 deletions
@@ -6,7 +6,9 @@ For each host on such an Ingress the NetBird management gets one reverse-proxy
service in TLS-passthrough mode on :443, targeting Traefik's ClusterIP through a
subnet resource that covers the cluster's service CIDR. SNI and Host reach
Traefik untouched, the proxy terminates nothing, and no domain resource is
involved -- the routing peers stay ordinary IP routers.
involved -- the routing peers stay ordinary IP routers. The target sends a
PROXY protocol v2 header so Traefik (and CrowdSec behind its access log) sees
the WAN client address instead of the routing peer's pod IP.
Ownership is derived from NetBird itself, not from local state: a service is
managed iff its mode is "tls" and its target is this controller's subnet
@@ -194,9 +196,12 @@ def ensure_policy(nb, resource, source_group):
def service_body(host, resource, target_ip):
# proxy_protocol: PROXY v2 header carries the WAN client address to
# Traefik (proxyProtocol.trustedIPs on websecure); TCP/TLS modes only.
return {"name": host, "domain": host, "mode": "tls", "listen_port": LISTEN_PORT, "enabled": True,
"targets": [{"target_type": "subnet", "target_id": resource, "protocol": "tcp",
"host": target_ip, "port": 443, "enabled": True}]}
"host": target_ip, "port": 443, "enabled": True,
"options": {"proxy_protocol": True}}]}
def is_managed(svc, resource):
@@ -208,7 +213,8 @@ def target_matches(svc, target_ip):
return False
ts = svc.get("targets") or []
return len(ts) == 1 and ts[0].get("host") == target_ip and ts[0].get("port") == 443 \
and ts[0].get("protocol") == "tcp" and ts[0].get("enabled")
and ts[0].get("protocol") == "tcp" and ts[0].get("enabled") \
and (ts[0].get("options") or {}).get("proxy_protocol") is True
def reconcile():
@@ -21,6 +21,9 @@ metadata:
namespace: audiobookshelf
labels:
app.kubernetes.io/name: audiobookshelf
annotations:
# envFrom does not restart the pod when the ConfigMap changes; Reloader does.
configmap.reloader.stakater.com/reload: audiobookshelf-config
spec:
replicas: 1
strategy:
+3
View File
@@ -35,6 +35,9 @@ metadata:
namespace: immich
labels:
app.kubernetes.io/name: immich-server
annotations:
# envFrom does not restart the pod when the ConfigMap changes; Reloader does.
configmap.reloader.stakater.com/reload: immich-config
spec:
# Scaled to 0 alongside immich-machine-learning during single-node
# maintenance; see the note there. The CNPG Postgres cluster stays running,
@@ -76,7 +76,11 @@ spec:
type: RuntimeDefault
containers:
- name: controller
image: gitea.renner.uno/gitea_admin/netbird-proxy-services:2026-09-07
# Pinned by digest: the date tag is rebuilt on every push of the
# image directory (branch and master alike) and the registry keeps
# only the newest manifest per tag, so the digest must come from
# the MASTER build of the code change.
image: gitea.renner.uno/gitea_admin/netbird-proxy-services:2026-09-07@sha256:caffa308d802c6fcc6aea919ac94657c08b993651baa6b8d18de395bcc40523e
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
@@ -93,13 +93,17 @@ spec:
# Real client-IP visibility. The netbird-ingress routing peer
# re-originates every mesh + WAN-via-proxy connection from its own
# pod IP (rootless userspace WireGuard), so Traefik's TCP peer for
# that traffic is always a pod IP (e.g. fd10:244::2de1) — collapsing
# all external clients to one source. The NetBird reverse-proxy
# (HTTP mode, on the VPS) sets X-Forwarded-For with the real WAN
# client IP; trust the pod CIDRs so Traefik reads it instead. Without
# this, a CrowdSec bouncer bans the shared pod IP and 403s everyone
# (the 2026-06-27 IPv6 outage). Harmless no-op when no XFF is present
# (direct LAN/mesh) — those keep their RemoteAddr.
# that traffic is always a pod IP — collapsing all external clients
# to one source. The NetBird reverse-proxy services are TLS
# passthrough and carry the WAN client address in a PROXY protocol
# v2 header (options.proxy_protocol, set by netbird-proxy-services);
# X-Forwarded-For stays trusted for HTTP-mode services. Both are
# accepted from the pod CIDRs only; connections without a header
# (direct LAN/mesh) keep their RemoteAddr.
proxyProtocol:
trustedIPs:
- "10.244.0.0/16"
- "fd10:244::/56"
forwardedHeaders:
trustedIPs:
- "10.244.0.0/16"