Files
claude-botandClaude Opus 5.5 05d2fcac71
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
feat(radius): proxy UniFi Endpoint logins to the UDM over VLAN 11
The UDM RADIUS rejects the pod CIDR as an unknown client; it accepts
10.2.11.0/24. FreeRADIUS pods get net1 on VLAN 11 "(11) RADIUS" and the
udm home server moves to 10.2.11.1.

- Talos k8s-{1,2,3}: VLANConfig radius0 (VLAN 11 on uplink), no
  addresses, IPv6 disabled on the host link.
- Cilium bpf.vlanBypass gains 11: radius0 is address-less like ens19, so
  its tagged frames meet the VLAN filter on uplink (#592).
- NAD freeradius-udm: macvlan on radius0, DHCP IPAM requesting only
  subnet-mask (skipDefault), so the lease has no router and net1 holds
  only the connected /24. No sbr: it moves that route into a source
  table, and the unbound proxy socket would then reach the UDM via eth0
  from the pod IP. tuning disables IPv6 on net1 (VLAN 11 sends RAs).
- NAS listeners bound to eth0, so nothing listens on net1.
- CNP freeradius-udm-egress removed: net1 traffic bypasses Cilium.
- Harness: FreeRADIUS interfaces named eth0/net1 explicitly; Docker
  attaches networks in no fixed order, and the eth0 bind exposed it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 15:02:37 +01:00
..

FreeRADIUS functional test

tests/radius/run.sh

Runs the production FreeRADIUS config from kubernetes/apps/authentik/freeradius-config.yaml (rendered through the projected volume in freeradius.yaml, same image, args and secret env names) in Docker against a mock authentik outpost and a mock UDM RADIUS, and drives it with eapol_test (EAP-TTLS, inner PAP) as a NAS in the test client ranges 10.2.50.0/24 and fd02:50::/64. The v6 cases (v6-*) hit the IPv6 listeners and client test6. Cases use outer identity anonymous unless noted.

Case Expected
vlan-tunnel (outpost sends Tunnel-* and Cisco-AV-Pair) SUCCESS, Tunnel-* VLAN 50, no Cisco-AV-Pair
vlan-avpair (outpost sends only Cisco-AV-Pair = "dot1x-vlan=50") SUCCESS, Tunnel-* VLAN 50, no Cisco-AV-Pair
novlan (Accept without VLAN) FAILURE (Access-Reject)
wrong password / unknown user (Reject without Message-Authenticator) FAILURE (Access-Reject) within 1 s
Accounting-Request answered locally, nothing sent to the outpost or the UDM
anon-realm (outer identity Anonymous@Example.Org) local TTLS, as vlan-tunnel
udm-accept, v6-udm-accept (outer identity = account name) both EAP rounds reach the mock UDM (second with its State); its Access-Accept reaches the NAS with Tunnel-* VLAN 200 and MS-MPPE keys unchanged
udm-reject the mock UDM's Access-Reject reaches the NAS within 1 s
all anonymous cases never reach the mock UDM
recovery outpost stalls until marked dead, then returns: login succeeds within 20 s

Pieces:

  • mock_outpost.py: stand-in outpost on UDP 1812, reachable under the outpost Service FQDN through a Docker network alias.
  • mock_udm.py: stand-in UDM RADIUS at the udm home server address from proxy.conf (10.2.11.1), on a second internal bridge radtest-udm (that address's /24, no host address). FreeRADIUS gets a second interface there, as net1 in the pod: only the connected /24, no default route, and the proxy socket picks it by route lookup. The macvlan/DHCP attachment itself is not tested. Answers round 1 with Challenge + State + EAP-TTLS Start (or Reject for endpoint-reject), round 2 with Accept + EAP-Success; terminates no EAP.
  • eapol.Dockerfile: eapol_test from Debian eapoltest.
  • Docker network radtest: internal bridge on 10.2.50.0/24 and fd02:50::/64 with no host address (inhibit_ipv4, gateway_mode_ipv6=isolated), so the workstation LAN routes are untouched.

Requires Docker, openssl and yq (the jq wrapper). Secrets, password and certificates are generated dummies. RADTEST_DEBUG=1 runs radiusd with -xx and prints all logs. Containers, network and temp files are removed on exit.

live.sh logs in against the deployed FreeRADIUS instead; usage in docs/radius/dot1x.md ("Live login check").