The Authentik gate added in #138 only guards the Traefik ingress path.
shelly-manager also has a Multus macvlan (net1) on the IoT VLAN for mDNS
discovery, it binds 0.0.0.0 (image Dockerfile default
ASPNETCORE_URLS=http://0.0.0.0:8080), and macvlan traffic bypasses Cilium
policy entirely. So the unauthenticated admin UI was reachable directly on
the IoT VLAN, past the gate.
Verified 2026-07-10 from edge-1 (on v30iot):
GET http://10.2.30.122:8080/ -> 200, full Shelly Manager UI
An untrusted IoT client could rewrite Shelly device config, push scripts,
and read stored device admin passwords — exactly what the gate was meant
to prevent. The macvlan predates the gate; #138 closed the front door and
left this side door open.
Pin Kestrel to the pod's eth0 address via the downward API
(ASPNETCORE_URLS=http://$(POD_IP):8080, POD_IP=status.podIP) so :8080 is
served only on the cluster pod CIDR, not on net1. Everything that should
reach the app still does — the IPv4 ClusterIP Service, the Authentik
outpost through it, and the kubelet httpGet probes all target the pod IP.
The Service is single-stack IPv4, so no IPv6 consumer is lost.
mDNS discovery is unaffected: the resolver is a separate ZeroconfResolver
socket set that binds its own multicast sockets on net1, independent of
Kestrel (src/ShellyManager.Web/Shelly/MdnsResolver.cs). net1 keeps L2
presence for `_shelly._tcp.local.` browsing; it just no longer carries the
HTTP server.
Also corrects the stale "static IP" NAD comment — net1 is a DHCP lease
since #135.
Verified: kubectl kustomize builds; rendered env has POD_IP (downward API)
and ASPNETCORE_URLS=http://$(POD_IP):8080; probes still httpGet the named
port, which resolves to the pod IP Kestrel now binds. Post-merge checks to
run: discovery still lists all devices, GET to net1:8080 from the IoT VLAN
now fails, gate still serves via Traefik, pod stays Ready.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>