Files
homelab/tests/radius
claude-botandClaude Opus 5.5 bc698ea22d
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
feat(radius): forward non-anonymous EAP logins to the UDM RADIUS
Outer identity anonymous[@realm] (any case) keeps the local EAP-TTLS ->
authentik path. Any other outer identity is a UniFi Endpoint account: the
outer server proxies every EAP round to new home server udm (10.2.47.1:1812,
UDM_RADIUS_SECRET) without local EAP processing; post-proxy/post-auth leave
UDM replies untouched. Endpoint account names look like ordinary usernames,
so routing keys on the anonymous identity, not a name pattern.

home_server udm: response_window 5 < proxy_dedup_window 10,
require_message_authenticator yes (RFC 3579, EAP replies), status_check
request with a user name that cannot exist.

CNP freeradius-udm-egress allows only 10.2.47.1/32 UDP 1812.

Harness: mock_udm.py on a second internal bridge at the udm address from
proxy.conf. Asserts both EAP rounds (State included) reach it, its Accept
reaches the NAS with Tunnel-* VLAN 200 and MS-MPPE keys intact, its Reject
passes, and anonymous logins and accounting never reach it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 13:13:07 +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, on a second internal bridge radtest-udm (that address's /24, no host address). 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.