radius
This backend sends RADIUS requests to external RADIUS servers. It supports two modes of operation:
- Proxying mode: Forwards the incoming RADIUS request to another RADIUS server. The proxy request and reply are derived from the original request context.
- Query mode: Sends independent RADIUS requests that you construct within the execution context. This allows issuing RADIUS requests from any protocol handler, not just RADIUS.
Server configuration
Both modes share the same server configuration. Define one or more target servers within the backend block.
Basic server configuration
backends {
radius "RADIUS_BACKEND" {
# Server selection strategy (optional)
# round-robin = distribute requests across healthy servers
# fallback = try next healthy server if first does not respond
# no-fallback = do not try next server if first does not respond
server-selection round-robin;
server "radius1.example.org" {
# RADIUS shared secret (required)
secret "ExampleSecret";
# Request timeout (supports duration units: ms, s, m, h)
timeout 7s;
# Number of times to retry a timed out request
retries 0;
# Enable Status-Server polling for health detection
status false;
# Maximum number of connections to open (optional)
#connections 16;
connect {
# Transport protocol: udp, tcp, or tls
protocol udp;
# Server IP address
ip 203.0.113.111;
# Alternatively, use hostname instead of ip
#host "radius1.example.org";
# Destination port
port 1812;
# Optional: socket buffer size in bytes
#buffer 1048576;
}
}
# Additional servers for redundancy
server "radius2.example.org" {
secret "ExampleSecret";
timeout 7s;
retries 0;
status false;
connect {
protocol udp;
ip 203.0.113.112;
port 1812;
}
}
}
}
RadSec (RADIUS over TLS) server configuration
For TLS connections, add a tls block inside the connect block:
backends {
radius "RADSEC_BACKEND" {
server "radsec.example.org" {
secret "radsec";
timeout 60s;
retries 2;
status true;
connect {
protocol tls;
host "radsec.example.org";
port 2083;
tls {
# Client certificate for mutual TLS
certificate "RADSEC_CLIENT_CERT";
certificate_key "RADSEC_CLIENT_KEY";
# CA certificate to validate server
server_ca_certificate "RADSEC_SERVER_CA";
# Optional: custom certificate verification
verification {
if any {
cert.subject_alt.dns != "radsec.example.org";
} then {
reject;
} else {
accept;
}
}
}
}
}
}
}
Proxying mode
In proxying mode, the backend forwards the incoming RADIUS request to the configured server. Use this mode when you want to relay requests to another RADIUS server.
Calling the proxy backend
Call the backend directly by name in a handler pipeline:
aaa {
policy "DEFAULT" {
handler "PROXY" {
@execute {
backend "RADIUS_BACKEND";
}
}
}
}
Modifying requests and replies
Use @pre-proxying and @post-proxying blocks within the backend configuration to filter or modify attributes:
backends {
radius "RADIUS_PROXY" {
# Modify request before sending to upstream server
@pre-proxying {
# Remove attributes from the proxy request
filter {
Tunnel-Type;
Tunnel-Medium-Type;
Tunnel-Private-Group-ID;
}
# Add or modify attributes
modify {
radiusproxy.request.attr.Operator-Name = "4EXAMPLE_COM:FI";
}
# Copy specific attributes from the original request
copy {
User-Name;
User-Password;
}
}
# Modify reply before returning to client
@post-proxying {
filter {
Tunnel-Type;
Tunnel-Medium-Type;
Tunnel-Private-Group-ID;
}
copy {
Reply-Message;
}
}
server "radius1.example.org" {
# ... server configuration ...
}
}
}
Query mode
In query mode, you define named queries that construct independent RADIUS requests. This allows you to:
- Send RADIUS requests from any protocol handler (HTTP, TACACS+, etc.)
- Issue multiple RADIUS requests within a single execution context
- Build custom RADIUS requests with specific attributes
Defining queries
Add query blocks within the backend configuration:
backends {
radius "RADSEC_BACKEND" {
server "radius.example.org" {
# ... server configuration ...
}
query "AUTHENTICATE_USER" {
# Set request attributes before sending
bindings {
radius.request.code = "access-request";
radius.request.attr.User-Name = vars.username;
radius.request.attr.User-Password = vars.password;
}
# Extract values from the reply
mapping {
vars.reply_code = "%{radius.reply.code}";
vars.filter_id = "%{radius.reply.attr.Filter-Id}";
}
}
}
}
Query bindings
The bindings block sets values on the RADIUS request before sending:
| Binding | Description |
|---|---|
radius.request.code | RADIUS packet code (access-request, accounting-request, etc.) |
radius.request.attr.<Attribute-Name> | Set a specific RADIUS attribute value |
Values can be literal strings or references to context variables.
The left side is the new request context being constructed, while the right side provides the values from the current execution context.
Query mapping
The mapping block extracts values from the RADIUS reply into the current context:
| Mapping | Description |
|---|---|
radius.reply.code | RADIUS reply code (access-accept, access-reject, etc.) |
radius.reply.attr.<Attribute-Name> | Value of a specific attribute from the reply |
radius.request.code | Echo of the sent request code |
radius.request.attr.<Attribute-Name> | Echo of a sent request attribute |
Query results
The RADIUS backend query action returns a result based on the RADIUS reply code received from the upstream server:
| RADIUS Reply Code | Action Result |
|---|---|
| Access-Accept | accept |
| Accounting-Response | accept |
| Disconnect-Ack | accept |
| CoA-Ack | accept |
| Access-Reject | reject |
| Disconnect-Nak | reject |
| CoA-Nak | reject |
| No response (timeout) | error |
Calling queries from handlers
Use the backend block with name and query parameters:
aaa {
policy "DEFAULT" {
handler "AUTHENTICATION" {
@execute {
# Set up variables for the query
modify {
vars.username = "alice";
vars.password = "secret123";
}
# Execute the RADIUS query
backend {
name "RADSEC_BACKEND";
query "AUTHENTICATE_USER";
}
# Use the results
if all {
vars.reply_code == "access-accept";
} then {
accept;
} else {
reject;
}
}
}
}
}
Query from HTTP handler
RADIUS queries can be issued from any protocol. Here is an example using an HTTP server:
servers {
http "HTTP_SERVER" {
listen {
protocol tcp;
ip 0.0.0.0;
port 4000;
}
clients "HTTP_CLIENTS";
policy "DEFAULT";
}
}
backends {
radius "RADIUS_AUTH" {
server "radius.example.org" {
# ... server configuration ...
}
query "CHECK_USER" {
bindings {
radius.request.code = "access-request";
radius.request.attr.User-Name = vars.http_user;
radius.request.attr.User-Password = vars.http_password;
}
mapping {
vars.auth_result = "%{radius.reply.code}";
}
}
}
}
aaa {
policy "DEFAULT" {
handler "HTTP_AUTH" {
@execute {
modify {
vars.http_user = aaa.identity;
vars.http_password = aaa.password;
}
backend {
name "RADIUS_AUTH";
query "CHECK_USER";
}
if all {
vars.auth_result == "Access-Accept";
} then {
accept;
} else {
reject;
}
}
}
}
}
Server parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
secret | string | (required) | RADIUS shared secret |
timeout | duration | 3s | Request timeout |
retries | integer | 0 | Number of retry attempts for timed out requests |
status | boolean | false | Enable Status-Server polling for health detection |
connections | integer | 16 | Maximum number of connections to open |
priority | integer | 0 | Server priority for fallback selection (lower = higher priority) |
Connect parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
protocol | enum | (required) | Transport protocol: udp, tcp, or tls |
ip | IP address | - | Server IP address (use either ip or host) |
host | string | - | Server hostname (use either ip or host) |
port | integer | 1812 | Destination port |
buffer | integer | - | Socket buffer size in bytes |
Backend parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
server-selection | enum | fallback | Server selection strategy: round-robin, fallback, or no-fallback |