From f8c25d9c23097a30e1c4eea534bb541b11235e30 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 7 Feb 2026 22:25:54 +0000 Subject: [PATCH] [ci skip] Add skill: traefik-udp-cross-namespace Extracted from debugging DNS forwarding through Traefik v3. Documents two non-obvious requirements for custom UDP entrypoints in the Helm chart: expose.default=true (port not added to Service by default) and allowCrossNamespace=true (IngressRouteUDP cross-namespace refs blocked by default). Both issues compound silently. --- .../traefik-udp-cross-namespace/SKILL.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 .claude/skills/traefik-udp-cross-namespace/SKILL.md diff --git a/.claude/skills/traefik-udp-cross-namespace/SKILL.md b/.claude/skills/traefik-udp-cross-namespace/SKILL.md new file mode 100644 index 00000000..e9713139 --- /dev/null +++ b/.claude/skills/traefik-udp-cross-namespace/SKILL.md @@ -0,0 +1,137 @@ +--- +name: traefik-udp-cross-namespace +description: | + Fix Traefik v3 (Helm chart v39+) UDP entrypoints not working for cross-namespace + IngressRouteUDP resources. Use when: (1) Traefik pod listens on a UDP port internally + but the LoadBalancer service doesn't expose it, (2) IngressRouteUDP logs show + "udp service / is not in the parent resource namespace" error, + (3) DNS or other UDP traffic through Traefik times out despite correct IngressRouteUDP + config, (4) Custom entrypoints added to Traefik Helm values don't appear in the + Service ports. Requires two fixes: expose the port AND enable cross-namespace CRD refs. +author: Claude Code +version: 1.0.0 +date: 2026-02-07 +--- + +# Traefik v3 UDP Entrypoint + Cross-Namespace Routing + +## Problem +Adding a custom UDP entrypoint (e.g., DNS on port 53) to Traefik v3 via Helm chart values +doesn't work out of the box. Traffic times out even though the Traefik pod listens on the +port internally. Two separate issues compound: + +1. The Helm chart defaults `expose` to `false` for custom entrypoints — the port is never + added to the LoadBalancer Service +2. `allowCrossNamespace` defaults to `false` — IngressRouteUDP in namespace A can't + reference a Service in namespace B + +## Context / Trigger Conditions +- Traefik Helm chart v39.0.0+ (Traefik v3.x) +- Custom UDP entrypoint defined in `ports` values +- `IngressRouteUDP` referencing a service in a different namespace +- Symptoms: + - `kubectl get svc traefik` doesn't show your custom UDP port + - UDP traffic to the LoadBalancer IP times out + - Traefik logs show: `"udp service / is not in the parent resource namespace "` + - `netstat -ulnp` inside Traefik pod confirms it IS listening on the port + +## Solution + +### Fix 1: Expose the UDP port on the Service + +In the Helm values, add `expose = { default = true }` to the entrypoint: + +```hcl +# Terraform HCL +ports = { + dns-udp = { + port = 5353 + exposedPort = 53 + protocol = "UDP" + expose = { default = true } # <-- Required for custom entrypoints + } +} +``` + +```yaml +# Helm values YAML equivalent +ports: + dns-udp: + port: 5353 + exposedPort: 53 + protocol: UDP + expose: + default: true +``` + +Note: The built-in `web` and `websecure` entrypoints have `expose.default = true` by +default, but custom entrypoints do NOT. + +### Fix 2: Enable cross-namespace CRD references + +In the Helm values, add `allowCrossNamespace = true` to the kubernetesCRD provider: + +```hcl +# Terraform HCL +providers = { + kubernetesCRD = { + enabled = true + allowCrossNamespace = true # <-- Required for cross-namespace IngressRouteUDP + } +} +``` + +```yaml +# Helm values YAML +providers: + kubernetesCRD: + enabled: true + allowCrossNamespace: true +``` + +This is required whenever an `IngressRouteUDP` (or `IngressRouteTCP`, `IngressRoute`) +references a Kubernetes Service in a different namespace. + +## Verification + +```bash +# 1. Verify the port appears in the Service +kubectl get svc -n traefik traefik -o jsonpath='{.spec.ports[*].name}' +# Should include your custom entrypoint name (e.g., "dns-udp") + +# 2. Check Traefik logs for cross-namespace errors +kubectl logs -n traefik -l app.kubernetes.io/name=traefik | grep "not in the parent resource namespace" +# Should return nothing after the fix + +# 3. Test the UDP service +dig @ example.com +``` + +## Example + +DNS forwarding through Traefik to Technitium DNS: +- IngressRouteUDP in `traefik` namespace routes `dns-udp` entrypoint to + `technitium-dns:53` in `technitium` namespace +- Without Fix 1: port 53 never exposed on LoadBalancer — traffic can't reach Traefik +- Without Fix 2: Traefik rejects the route — logs error every ~60 seconds +- With both fixes: DNS queries to LoadBalancer IP:53 → Traefik → Technitium + +## Notes + +1. **Debugging order matters**: Fix 1 (expose) must come first. Without the port on the + Service, you can't even test if the routing works. Fix 2 (cross-namespace) errors only + appear in Traefik logs, not as user-visible failures. +2. **`allowCrossNamespace` is a security consideration**: It allows any IngressRoute CRD + to reference services in any namespace. If this is too broad, consider using + `TraefikService` middleware or moving the IngressRouteUDP to the target namespace. +3. **Rolling update**: Changing `allowCrossNamespace` triggers a Traefik pod restart + (new CLI args). Changing `expose` only updates the Service (no pod restart needed). +4. **This applies to TCP too**: `IngressRouteTCP` with cross-namespace services needs the + same `allowCrossNamespace` setting. + +## References +- Traefik Helm chart ports configuration: https://github.com/traefik/traefik-helm-chart +- Traefik v3 providers documentation: https://doc.traefik.io/traefik/providers/kubernetes-crd/ + +## See also +- `traefik-http3-quic` — related Traefik Helm chart configuration skill