Compare commits

..
Author SHA1 Message Date
claude-botandClaude Opus 5.5 0806bdfab5 feat(dns): add dns.renner.uno/internal and netbird.renner.uno/public labels
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
Step 1 of 2 of the label split, owner decision 2026-10-22:
  dns.renner.uno/internal  -> UniFi (LAN) DNS via external-dns-unifi
  dns.renner.uno/public    -> Cloudflare DNS via external-dns-cloudflare
  netbird.renner.uno/public -> NetBird reverse-proxy service (netbird-service-sync)

Additive only; no consumer reads the new labels yet:
- dns.renner.uno/internal on every object external-dns-unifi publishes
  today (24 Ingresses incl. Grafana via chart values, 6 LoadBalancer
  Services = the 31 live UDM hostnames).
- netbird.renner.uno/public next to every dns.renner.uno/public label
  (20 Ingresses). netbird-service-sync already ORs both labels, so its
  desired set is unchanged.
authentik-gate-sync copies source labels to the generated *-gate Ingresses.

Step 2 flips external-dns-unifi to dns.renner.uno/internal=true,
netbird-service-sync to netbird.renner.uno/public only, and
external-dns-cloudflare to dns.renner.uno/public=true. It must merge only
after this change is applied, or external-dns-unifi (policy sync) would
delete LAN records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:43:33 +02:00
43 changed files with 70 additions and 23 deletions
@@ -157,29 +157,6 @@
ansible.builtin.pause:
seconds: 10
# The host's mesh client must not manage DNS: containers here resolve through
# the host stub, and the renner.uno nameserver group is unreachable from it.
- name: Check for the host NetBird client
ansible.builtin.stat:
path: /usr/bin/netbird
register: _netbird_client
- name: Read the host NetBird client config
ansible.builtin.command: netbird debug config
register: _netbird_client_config
changed_when: false
when: _netbird_client.stat.exists
# disable-dns is only applied on a fresh `up`, not to a connected client.
- name: Reconnect the host NetBird client with DNS management disabled
ansible.builtin.shell: |
set -eu
netbird down
netbird up --disable-dns
when:
- _netbird_client.stat.exists
- not (_netbird_client_config.stdout | from_json).disableDns
- name: Display deployment status
ansible.builtin.debug:
msg: |
@@ -7,6 +7,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -164,6 +164,7 @@ spec:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the
# UDM external-dns instance skips it, LAN uses the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
hosts:
@@ -27,6 +27,7 @@ metadata:
namespace: authentik
labels:
lb-pool: bgp
dns.renner.uno/internal: "true"
annotations:
# Pinned to .88 / fd99::88 (next free in the .80-.89 infra range
# alongside traefik .86, ha .87, syslog .84, syncthing .85). Pinning
+2
View File
@@ -3,6 +3,8 @@ kind: Ingress
metadata:
name: evcc
namespace: evcc
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
+1
View File
@@ -7,6 +7,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
+1
View File
@@ -7,6 +7,7 @@ metadata:
app.kubernetes.io/name: gitea-mcp
# Published on the NetBird proxy; LAN DNS stays on the Traefik VIP.
netbird.renner.uno/public: "true"
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
+1
View File
@@ -8,6 +8,7 @@ metadata:
# Published on the NetBird proxy; LAN DNS stays on the Traefik VIP so
# SSH (gitea-ssh) and node image pulls stay local.
netbird.renner.uno/public: "true"
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
# Long-running git fetch/push and container-registry blob uploads:
+4
View File
@@ -11,6 +11,8 @@ kind: Ingress
metadata:
name: hermes
namespace: hermes
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -38,6 +40,8 @@ kind: Ingress
metadata:
name: hermes-api
namespace: hermes
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -12,6 +12,8 @@ kind: Ingress
metadata:
name: shelly-manager
namespace: home-assistant
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: Shelly Managers
@@ -16,6 +16,7 @@ metadata:
labels:
app.kubernetes.io/name: home-assistant
lb-pool: bgp
dns.renner.uno/internal: "true"
annotations:
lbipam.cilium.io/ips: "10.2.99.87,fd99::87"
external-dns.kubernetes.io/hostname: ha.renner.uno
@@ -3,6 +3,8 @@ kind: Ingress
metadata:
name: esphome
namespace: home-assistant
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: ESPHome Managers
@@ -7,6 +7,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -13,6 +13,7 @@ metadata:
labels:
app.kubernetes.io/name: mosquitto
lb-pool: bgp
dns.renner.uno/internal: "true"
annotations:
lbipam.cilium.io/ips: "10.2.99.81,fd99::81"
# Publish an internal A record to UniFi DNS so workstations on the
@@ -10,6 +10,8 @@ kind: Ingress
metadata:
name: home-assistant-vscode
namespace: home-assistant
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: Home Assistant Config Editors
@@ -3,6 +3,8 @@ kind: Ingress
metadata:
name: zigbee2mqtt
namespace: home-assistant
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: Zigbee2MQTT Managers
@@ -104,6 +104,8 @@ kind: Ingress
metadata:
name: ical-proxy
namespace: apps
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
+3
View File
@@ -7,6 +7,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -37,6 +38,8 @@ kind: Ingress
metadata:
name: immich-photos
namespace: immich
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -19,6 +19,7 @@ metadata:
# external-dns instance skips anything carrying this, and LAN clients reach
# the same hostname through the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
# Manifest and service worker are fetched without credentials; neither
@@ -35,6 +35,8 @@ kind: Ingress
metadata:
name: wiki-embed
namespace: ai
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
traefik.ingress.kubernetes.io/router.middlewares: ai-wiki-embed-basic-auth@kubernetescrd
+3
View File
@@ -16,6 +16,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -125,6 +126,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -155,6 +157,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
+2
View File
@@ -317,6 +317,7 @@ metadata:
namespace: matrix
labels:
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
traefik.ingress.kubernetes.io/router.middlewares: matrix-lk-jwt-strip@kubernetescrd
@@ -369,6 +370,7 @@ metadata:
namespace: matrix
labels:
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
@@ -9,6 +9,8 @@ kind: Ingress
metadata:
name: alertmanager
namespace: monitoring
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: Monitoring Access
@@ -229,6 +229,7 @@ metadata:
namespace: monitoring
labels:
lb-pool: bgp
dns.renner.uno/internal: "true"
annotations:
io.cilium/lb-ipam-ips: "10.2.99.84,fd99::84"
external-dns.kubernetes.io/hostname: syslog.renner.uno
@@ -132,6 +132,8 @@ spec:
ingress:
enabled: true
ingressClassName: traefik
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
hosts:
@@ -3,6 +3,8 @@ kind: Ingress
metadata:
name: loki
namespace: monitoring
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
# nginx's auth-type/auth-secret/auth-realm annotation triple becomes
@@ -9,6 +9,8 @@ kind: Ingress
metadata:
name: prometheus
namespace: monitoring
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: Monitoring Access
@@ -7,6 +7,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
+2
View File
@@ -6,6 +6,7 @@ metadata:
namespace: navidrome
labels:
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -45,6 +46,7 @@ metadata:
namespace: navidrome
labels:
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
traefik.ingress.kubernetes.io/router.middlewares: navidrome-navidrome-strip-identity@kubernetescrd
spec:
+1
View File
@@ -194,6 +194,7 @@ metadata:
namespace: navidrome
labels:
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: Music Librarians
@@ -27,6 +27,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
traefik.ingress.kubernetes.io/router.middlewares: paperless-docs-to-paperless-redirect@kubernetescrd
@@ -113,6 +113,7 @@ metadata:
namespace: paperless
labels:
lb-pool: bgp
dns.renner.uno/internal: "true"
annotations:
io.cilium/lb-ipam-ips: "10.2.99.82,fd99::82"
# LAN-only DNS via the UniFi external-dns instance. No cloudflare
+1
View File
@@ -357,6 +357,7 @@ metadata:
# Registered publicly (Cloudflare wildcard -> NetBird proxy); the UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: Paperless Users
+1
View File
@@ -143,6 +143,7 @@ metadata:
namespace: paperless
labels:
lb-pool: bgp
dns.renner.uno/internal: "true"
annotations:
io.cilium/lb-ipam-ips: "10.2.99.85,fd99::85"
external-dns.kubernetes.io/hostname: syncthing.paperless.renner.uno
+1
View File
@@ -6,6 +6,7 @@ metadata:
namespace: review-bot
labels:
app.kubernetes.io/name: review-bot
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -28,6 +28,8 @@ kind: Ingress
metadata:
name: terminus-device
namespace: trmnl
labels:
dns.renner.uno/internal: "true"
# No external-dns hostname annotation: external-dns-unifi runs with
# --source=ingress, so it registers the hosts in spec.rules on its own.
# Same as the terminus Ingress next door.
+2
View File
@@ -7,6 +7,8 @@ kind: Ingress
metadata:
name: terminus
namespace: trmnl
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
+4
View File
@@ -6,6 +6,8 @@ kind: Ingress
metadata:
name: wiki
namespace: ai
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -43,6 +45,8 @@ kind: Ingress
metadata:
name: wiki-api
namespace: ai
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
traefik.ingress.kubernetes.io/router.middlewares: ai-wiki-api-basic-auth@kubernetescrd
+1
View File
@@ -5,6 +5,7 @@ metadata:
namespace: woodpecker
labels:
app.kubernetes.io/name: woodpecker
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
# Live log streams (SSE/WebSocket) hold the connection open for the
+2
View File
@@ -16,6 +16,7 @@ metadata:
namespace: wordpress
labels:
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
@@ -54,6 +55,7 @@ metadata:
# so an unlabelled Ingress on this host would gain a UDM A-record pointing
# LAN clients at the Traefik VIP.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
auth.renner.uno/group: WordPress Authors
spec:
@@ -9,6 +9,8 @@ kind: Ingress
metadata:
name: zone-configurator
namespace: home-assistant
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
auth.renner.uno/group: Zone Configurators
@@ -46,6 +46,7 @@ metadata:
# Public DNS via Cloudflare; the proxies resolve it from outside. The UDM
# external-dns instance skips it, LAN reaches it via the hairpin NAT.
dns.renner.uno/public: "true"
netbird.renner.uno/public: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
# NOT behind the Authentik gate: the client is a daemon with an API key,
@@ -3,6 +3,8 @@ kind: Ingress
metadata:
name: openbao
namespace: openbao
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec: