2026-03-02
v10.32.2
Summary
Add radius.request.identifier and radius.reply.identifier accessor support for reading and writing RADIUS packet identifiers
Add --identifier flag to radiator-client for setting custom packet identifiers
Fixed
stopaction not halting pipeline execution when used inside sub-pipelines such asif-then blocksAdd named HTTP status code constants (e.g.
http.TOO_MANY_REQUESTS,http.INTERNAL_SERVER_ERROR) for use in pipelinesAdd machine-readable error codes to caught pipeline errors via
aaa.caught_error.codeand compile-time-validatederrors.*constantsAdd ability to read all RADIUS attributes as a JSON array using bare
radius.request.attrandradius.reply.attraccessorsImplement Replace operator for radius proxy attribute modification
Add RADIUS attribute bulk copy and numeric packet code setter for proxy queries
Use
attrsinstead ofattrfor bulk RADIUS attribute operations to avoid confusing syntaxAllow handlers to override the RADIUS reply packet code by preserving explicitly set values
The
verificationblock in TLS configuration now uses the@verificationprefix; the old syntax is deprecated and emits a warningAdd select() and remove() filters for filtering multivalue data and RADIUS attribute collections
Add
nas_identifierconfiguration option to RADIUS backend server blocks for including NAS-Identifier in Status-Server packetsPipeline blocks inside EAP methods (
authentication,post-authentication,pre-authentication,authorization) now use the@prefix; the old syntax is deprecated and emits a warningAdd
assertpipeline action for verifying expression values and error conditions during pipeline execution. See assert for full documentation.Simplify the configuration editor syntax highlighting for radconf files to use a generic grammar instead of dynamically loaded keywords
Fixed incorrect error-level logging for unauthorized HTTP management API requests
Add rate limiting support via Lua scripts and cache-based counters, with GCRA and counter-based algorithms for per-user, per-device, and per-gateway throttling. See Rate Limiting and Rate Limiting Algorithms for full documentation.
radiator-client --expect-response-codenow accepts packet type names (e.g.Access-Reject) in addition to numeric codesLua print() now routes output to the structured application log instead of stdout
Add
conditionspipeline action conditions for full documentation.Add
format-packetsubcommand and improve packet display in radiator-clientFixed backend server priority field being ignored - servers are now selected by priority (0 = highest) first, then alphabetically
The Management UI log view is now faster and more reliable for large datasets, with easier filtering, infinite-scroll loading, streaming logs, and JSON/CSV exports. This makes it easier to find and share relevant logs with less manual work.
Fixed light/dark theme detection when following the system theme preference
Fixed RADIUS proxy attribute ordering so Message-Authenticator is always the first attribute in proxied requests, even when pre-proxying modifies attributes without a copy block
Added a button to clear search highlights in the documentation viewer
Improved toolbar layout on small screens by hiding the page title
Fix RADIUS attributes being duplicated when using
modifyin@pre-proxyingwithout acopyblockAdded syntax highlighting for radconf code fences in Markdown documentation viewed in the Management UI
Add Reverse CoA support
RADIUS Packet Identifier Accessor Support
New execution context variables radius.request.identifier and
radius.reply.identifier allow reading and writing the RADIUS packet
identifier field (0–255) in configuration policies.
The identifier is a byte-sized field in the RADIUS packet header used to match requests with their corresponding responses. The server automatically echoes the request identifier in the reply packet.
The Execution Context reference has
been updated with the new radius.request.identifier and
radius.reply.identifier variables.
radiator-client --identifier Flag
The radiator-client tool now supports a --identifier option to set a
custom RADIUS packet identifier (0–255) on outgoing requests. When not
specified, the identifier defaults to 0.
radiator-client --identifier 42 -s 127.0.0.1 --secret mysecret \
--user mikem --password fred
Named HTTP status code constants
The http namespace now provides named constants for all standard HTTP status
codes. Use these instead of raw numeric values for clearer, more maintainable
pipeline configurations.
# Before
modify http.status = 429;
# After
modify http.status = http.TOO_MANY_REQUESTS;
All standard 1xx through 5xx status codes are available. Constants are read-only and resolve to their numeric values at runtime.
See Execution Context for the full list of available constants.
Structured error codes for pipeline error handling
Errors caught by the try action now carry an optional
machine-readable error code accessible via aaa.caught_error.code. This
enables pipelines to branch on specific error conditions without parsing the
human-readable error message string.
Error code constants are available in the read-only errors namespace and
validated at configuration load time, preventing typos from going unnoticed.
The first supported error code is errors.RADIUS_IDENTIFIERS_EXHAUSTED, which
is set when all 256 RADIUS packet identifiers are in use on a connection and a
new proxied request cannot be sent.
try backend "REVERSE_RADSEC";
if all {
aaa.caught_error.code == errors.RADIUS_IDENTIFIERS_EXHAUSTED;
} then {
modify http.status = http.TOO_MANY_REQUESTS;
stop;
} else if all {
aaa.caught_error != none;
} then {
reject "operation failed: %{aaa.caught_error}";
}
When aaa.caught_error is cleared, aaa.caught_error.code is also cleared
automatically.
See Execution Context for the full list of available error code constants.
Log All RADIUS Attributes into a Single Field
The bare radius.request.attr and radius.reply.attr accessors (without a
specific attribute name) now return all dictionary attributes as a JSON array of
objects with name and value fields. Enum-typed attribute values are
automatically resolved to their dictionary names (e.g. "framed-user" instead
of 2).
This is useful for logging all RADIUS attributes in a single structured log field:
log "AUTHENTICATION" {
json {
"request_attrs" radius.request.attr;
"reply_attrs" radius.reply.attr;
}
}
The resulting JSON array looks like:
[{"name": "user-name", "value": "mikem"}, {"name": "service-type", "value": "framed-user"}]
See Execution Context for more details.
The = (replace) operator now works for radiusproxy.request.attr.* and
radiusproxy.reply.attr.* in @pre-proxying and @post-proxying blocks.
Previously only append (+=) and insert-unless (?=) were functional.
Example
backends {
radius "UPSTREAM" {
@pre-proxying {
copy {
User-Name;
User-Password;
}
}
}
}
RADIUS attribute bulk copy in backend queries
The bare radius.request.attr and radius.reply.attr accessors (without a
specific attribute name) now copy all attributes in bulk between contexts.
Encrypted attributes such as User-Password are automatically re-encrypted
with the destination shared secret so they survive proxying across different
RADIUS hops. Message-Authenticator is excluded from bulk copies because it
must be recomputed per context.
This makes it possible to implement RADIUS proxying through query mode:
backends {
radius "RADSEC_BACKEND" {
server "UPSTREAM" {
# ... server configuration ...
}
query "PROXY_REQUEST" {
bindings {
radius.request.code = radius.request.code;
radius.request.attrs = radius.request.attrs;
}
mapping {
radius.reply.code = radius.reply.code;
radius.reply.attr = radius.reply.attr;
}
}
}
}
See RADIUS backend query mode for details.
Numeric values for radius.*.code
radius.request.code and radius.reply.code now accept numeric integer values
in addition to string names (e.g. "access-request"). This is needed for query
bindings where the packet code is passed through as an integer, such as
radius.request.code = radius.request.code.
Use attrs for bulk attribute operations
Previously bulk attribute copy used the attr path, which led to confusing
configuration lines like:
radius.reply.attr = radius.reply.attr;
This looks like a no-op assigning a single attribute to itself. The attrs
(plural) path now makes the bulk nature of the operation explicit:
radius.reply.attrs = radius.reply.attrs;
Both attr and attrs still work interchangeably, but attrs is now the
recommended form for bulk operations.
Documentation
- Updated execution-context.md to use
attrsfor bulk attribute access - Updated backends.radius.md query proxy examples to use
attrs
Preserve handler-set reply packet codes
Previously, the reply packet code was always determined by the handler result
(accept, reject, challenge), which made it impossible for handlers to
send a custom response code. Now, if a handler explicitly sets
radius.reply.code before the result is finalized, that code is sent as-is
instead of being replaced by the default code for the handler result.
When proxying RADIUS requests in query mode, the backend's reply code can be mapped back to the reply. Previously, the mapped code was overwritten by the proxy handler's own result. Now the backend's reply code is faithfully relayed to the client:
query "PROXY_REQUEST" {
bindings {
radius.request.code = radius.request.code;
radius.request.attrs = radius.request.attrs;
}
mapping {
radius.reply.code = radius.reply.code;
radius.reply.attrs = radius.reply.attrs;
}
}
Documentation
- Updated execution-context.md
radius.reply.codedescription to clarify the override behavior
@verification prefix for TLS verification block
The verification block inside tls configuration now uses the @verification prefix,
consistent with other block-level statements. The old verification syntax is still
accepted but will emit a deprecation warning.
Before
tls {
verification {
accept;
}
}
After
tls {
@verification {
accept;
}
}
Documentation
- Updated tls.@verification reference docs
- Updated servers.tls parameter table
- Updated backends.radius RadSec example
- Updated backend-load-balancing PostgreSQL TLS examples
Add select() and remove() filters
New filters for filtering multivalue data and RADIUS attribute collections. Supports matching by attribute name, attribute number, and regex patterns.
Documentation
NAS-Identifier Support for RADIUS Backend Status-Server
A new optional nas_identifier configuration statement is available in the RADIUS backend server block. When set, Radiator includes the NAS-Identifier attribute in outgoing Status-Server health check packets. On the receiving server side, the value is extracted from incoming Status-Server packets and exposed through the server connections Management API.
@ prefix for pipeline blocks inside EAP methods
Pipeline blocks inside EAP method configurations (eap-tls, eap-ttls, eap-peap,
eap-md5, eap-otp, eap-gtc, eap-sim, eap-aka, eap-aka-prime, eap-mschapv2,
eap-teap) now use the @ prefix, consistent with handler-level pipeline blocks.
The old unprefixed syntax is still accepted but will emit a deprecation warning.
Before
eap-tls {
tls { ... }
post-authentication {
invoke "LOG_AUTHENTICATION";
}
}
After
eap-tls {
tls { ... }
@post-authentication {
invoke "LOG_AUTHENTICATION";
}
}
Fixed Log Level for Unauthorized HTTP Management Requests
The HTTP management middleware was logging all API errors at the Error level,
including expected 401 Unauthorized responses from unauthenticated requests.
This caused noisy "No user context found in the request" error log entries
during normal operation whenever an unauthorized request was received.
The log level for client errors (4xx) has been downgraded to Debug, while
server errors (5xx) continue to be logged at the Error level.
radiator-client: --expect-response-code accepts packet type names
The --expect-response-code option in radiator-client now accepts
case-insensitive packet type names in addition to numeric codes.
For example, instead of:
radiator-client --expect-response-code 3 ...
You can now write:
radiator-client --expect-response-code Access-Reject ...
Valid names: Access-Request, Access-Accept, Access-Reject,
Accounting-Request, Accounting-Response, Access-Challenge,
Status-Server, Disconnect-Request, Disconnect-Ack, Disconnect-Nak,
CoA-Request, CoA-Ack, CoA-Nak.
Error messages for unexpected response codes now also display the packet type name alongside the numeric code for improved readability.
Lua print() routed to application log
The Lua print() function is now overridden in all Radiator script contexts.
Instead of writing to stdout (which is not accessible in production deployments),
print() emits a structured DEBUG-level log entry so output is always captured
alongside other application events.
Each call produces a log entry with "message": "Lua print output" and fields:
msg— the printed value (plain string for a single argument; compact JSON array for multiple arguments; tables serialized to JSON)script— script identifier:"name"for inline scripts,"name(file.lua)"for file-based scriptsline— the line number within the Lua source
-- auth_enrichment.lua
local context, previous = ...
print("identity: " .. context.aaa.identity)
print(context.radius.request) -- table → compact JSON
return previous
Example log output (JSON format):
{
"level": "DEBUG",
"message": "Lua print output",
"fields": {
"msg": "identity: alice@example.com",
"script": "auth_enrichment(auth_enrichment.lua)",
"line": "3"
}
}
Enable DEBUG logging to see print() output (--debug flag or loglevel debug;
in a logging block).
Backend Server Priority Selection Fixed
The priority field in RADIUS, LDAP, and SQL backend server configurations was being parsed but not respected at runtime. Servers were always selected in alphabetical order by name, regardless of their configured priority.
This has been fixed. Backend servers are now selected according to their configured priority value (0 = highest priority, 255 = lowest), with alphabetical ordering (by server name) used as a tie-breaker when multiple servers share the same priority.
What Changed
- RADIUS backends: Server selection now respects the
priorityfield - LDAP backends: Server selection now respects the
priorityfield - SQL backends (MySQL, PostgreSQL, MSSQL): Priority field added and server selection now respects it
- Default behavior: Servers with no
priorityfield default to priority 0 (highest) - Tie-breaking: When servers have the same priority, they are ordered alphabetically by name
See Backend Load Balancing for details on server selection algorithms and the priority field.
Fixed an issue where parts of the management UI would always render with the dark theme when the user had not explicitly set a theme preference, regardless of the operating system's light/dark mode setting. This affected the sidebar logo, Monaco code editors, canvas charts, and Mermaid diagrams.
Documentation Search Improvements
A clear button now appears next to the search bar when search highlights are active on a documentation page. Clicking it removes the highlights, same as pressing Escape.
The toolbar page title and instance info are now hidden on smaller screens to give more space to the search bar and other controls.
Fix pre-proxying attribute duplication
When using modify in @pre-proxying without a copy block, attributes set by modify would be duplicated in the proxied request. For example, setting radiusproxy.request.attr.User-Name would result in both the modified and original User-Name appearing in the outgoing request.
The proxy request is now populated from the original request before running the pre-proxying pipeline, so modify operations correctly replace existing attributes.
Reverse CoA support
Add support for sending RADIUS "reverse" requests to NAS devices over their existing connections and receiving them into a local RADIUS server via a backend for NAS or AP simulation.
See the Reverse CoA Messaging article for details on this new feature.
Known Issues
When the /var/lib/radiator directory has 15_certificates.radconf file, and the file has not been edited once, the rpm package upgrade will show a warning warning: file /var/lib/radiator/15_certificates.radconf: remove failed: No such file or directory. The warning is harmless and can be ignored, as the 15_certificates.radconf file has been renamed.