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:

BindingDescription
radius.request.codeRADIUS 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:

MappingDescription
radius.reply.codeRADIUS reply code (access-accept, access-reject, etc.)
radius.reply.attr.<Attribute-Name>Value of a specific attribute from the reply
radius.request.codeEcho 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 CodeAction Result
Access-Acceptaccept
Accounting-Responseaccept
Disconnect-Ackaccept
CoA-Ackaccept
Access-Rejectreject
Disconnect-Nakreject
CoA-Nakreject
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

ParameterTypeDefaultDescription
secretstring(required)RADIUS shared secret
timeoutduration3sRequest timeout
retriesinteger0Number of retry attempts for timed out requests
statusbooleanfalseEnable Status-Server polling for health detection
connectionsinteger16Maximum number of connections to open
priorityinteger0Server priority for fallback selection (lower = higher priority)

Connect parameters

ParameterTypeDefaultDescription
protocolenum(required)Transport protocol: udp, tcp, or tls
ipIP address-Server IP address (use either ip or host)
hoststring-Server hostname (use either ip or host)
portinteger1812Destination port
bufferinteger-Socket buffer size in bytes

Backend parameters

ParameterTypeDefaultDescription
server-selectionenumfallbackServer selection strategy: round-robin, fallback, or no-fallback