yum upgrades for production use, this is the repository for you.
Active subscription is required.
Technical Briefing: RADIUS Authentication for NGINX
Problem Statement
NGINX ships with no built-in RADIUS support. Organizations that centralize credentials in FreeRADIUS, Microsoft NPS, Cisco ISE, or Aruba ClearPass have had no way to make NGINX defer to that authority—leaving per-tool htpasswd files as the practical alternative. The nginx-module-auth-radius package from the GetPageSpeed repository closes this gap by adding per-request RADIUS authentication (RFC 2865) to NGINX.
How It Works
The module hooks into the NGINX access phase in the same way auth_basic does:
- It returns
401withWWW-Authenticate: Basic realm="...". - It extracts credentials from the
Authorizationheader. - It then either passes the request, returns
401on Access-Reject, or returns503when all servers are unreachable—failing closed.
Implementation is non-blocking. Each configured server gets a pool of persistent UDP sockets, created once per worker. The pool size is controlled by the queue_size directive (default 10, maximum 255). Replies are validated against the RFC 2865 Response Authenticator, so spoofed or corrupted responses are discarded.
Two Key Limitations
- PAP only—no CHAP or MS-CHAP.
- No caching of authentication results—every protected request costs one RADIUS round trip.
Installation
RHEL-family
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-auth-radius
Then add the following at the very top of /etc/nginx/nginx.conf:
load_module modules/ngx_http_auth_radius_module.so;
Debian/Ubuntu
Set up the GetPageSpeed APT repo, then:
sudo apt-get install nginx-module-auth-radius
Module loading is handled automatically—no load_module directive is needed.
Configuration
There are five directives in total. radius_server declares a named server at the http level; the rest activate authentication inside a location. A radius_server block holds url, secret, nas_identifier, auth_timeout, and auth_retries.
A working example, tested on Rocky Linux 10 with nginx 1.30.4, uses radius_servers "corp_radius"; plus auth_radius "Restricted Area"; inside the location. Keep quotes around values containing special characters.
Verified Behaviors
Testing with curl confirmed:
- No credentials →
401with the Basic realm header - Valid credentials →
200 - Wrong password →
401 - All servers unreachable →
503
BlastRADIUS Compatibility Trap (CVE-2024-3596)
FreeRADIUS 3.2.5+ tracks whether a client has ever sent the Message-Authenticator attribute and then requires it on every subsequent packet. Since radtest always sends it, sanity-checking with radtest first locks the client and causes FreeRADIUS to silently drop the module’s requests.
Symptom: radtest works while NGINX returns 503 after exactly auth_timeout × auth_retries seconds, with nothing in the RADIUS log.
Fix: In /etc/raddb/clients.conf, set require_message_authenticator = false for the NGINX client, then restart FreeRADIUS.
Caveat: This loosens one BlastRADIUS mitigation for that client. Keep the NGINX–RADIUS path on localhost, a dedicated VLAN, or inside an IPsec/WireGuard tunnel.
Failover
Declare multiple servers and repeat radius_servers in priority order; the module advances on timeout or connection refusal. With the primary deliberately dead, authentication still returned 200 in under five milliseconds, because a refused UDP port fails over instantly—only genuine packet loss costs the full auth_timeout per retry. Keep auth_timeout and auth_retries low on the primary when a backup exists.
Health Checks
radius_health turns a location into a liveness probe: any RADIUS response (Accept or Reject) counts as healthy; only an unreachable server fails.
Caveat: Do not implement the endpoint with return 200, because return short-circuits the access phase and the probe never runs, reporting healthy forever. Use try_files with a small static file instead.
Performance Guidance
With no result cache, expect one round trip per request—fine on a LAN for admin panels and internal tools. For busy paths:
- Raise
queue_size(excess requests wait on a 100 ms retry timer) - Raise
worker_connections - Scope protection to paths like
location /admin/rather thanlocation /, so static assets bypass auth - Tighten
auth_timeout/auth_retries(defaults give 15 seconds per server)
SELinux on RHEL-family
setsebool -P httpd_can_network_connect on
Label non-standard ports, for example:
semanage port -m -t http_port_t -p tcp 8081
Troubleshooting
503with valid credentials and an empty RADIUS log—almost always the BlastRADIUS client lock.503after exactly 15 seconds—points to firewall rules for UDP 1812, wrong listen address, SELinux booleans, or a wrong shared secret. A bad Response Authenticator verification looks identical to packet loss;tcpdump -i any -n udp port 1812distinguishes them.unknown directive "radius_server"—the module isn’t loaded. Addload_moduleat the top ofnginx.conf, outside every block.- Files staged under
/tmpand moved into/etcmay carry wrong SELinux contexts. Runrestorecon -Rv /etc/nginx(or/etc/raddb).
Packaging
The package tracks GetPageSpeed NGINX builds, so the module binary always matches the installed nginx version. It ships in the GetPageSpeed Premium Repository alongside 140+ other NGINX modules. Source is dvershinin/ngx_http_auth_radius_module on GitHub.
Read the full article: NGINX RADIUS Authentication: No More htpasswd Files
