tls
The tls clause configures Transport Layer Security (TLS) for server listeners. This configuration applies to all server types that support TLS:
- HTTP servers - Enables HTTPS with HTTP/1.1 and HTTP/2 support
- RADIUS servers - Enables RadSec (RADIUS over TLS, RFC 6614) on port 2083
- TACACS+ servers - Enables TACACS+ over TLS
Parameters
| Parameter | Description | Required |
|---|---|---|
| certificate | Server certificate name (from certificates block) | Yes |
| certificate_key | Server private key name (from certificates block) | Yes |
| server_ca_certificate | Additional certificates to append to the presented server certificate chain, typically intermediates | No |
| require_client_certificate | Whether clients must present a certificate | No |
| client_ca_certificate | CA certificate for validating client certificates | Conditional |
| @verification | Custom certificate verification pipeline | No |
| min_protocol_version | Minimum TLS protocol version (tlsv12 for TLS 1.2, tlsv13 for TLS 1.3) | No |
| max_protocol_version | Maximum TLS protocol version (tlsv12 for TLS 1.2, tlsv13 for TLS 1.3) | No |
| tls12_session_id_resumption | Enables TLS 1.2 SessionID resumption (true or false) | No |
| session_cache_size | Maximum number of stateful TLS resumption cache entries shared by TLS 1.2 SessionID and TLS 1.3 stateful ticket resumption | No |
| tls12_session_id_resumption_lifetime | Maximum lifetime for TLS 1.2 SessionID cache entries as a duration (for example 24h, 30m, or 0s) | No |
| tls13_session_tickets | Number of TLS 1.3 session tickets to send (0 to disable) | No |
| tls13_session_ticket_lifetime | Advertised TLS 1.3 ticket lifetime as a duration (for example 24h, 30m, or 0s); 0 disables resumption tickets | No |
| keylog_filename | File path for TLS key logging (for debugging with Wireshark) | No |
require_client_certificate defaults to true when omitted. Set
require_client_certificate false; for one-way TLS listeners such as a normal
HTTPS endpoint.
The client_ca_certificate parameter is required when
require_client_certificate is set to true.
server_ca_certificate is optional. Use it when the certificate configured in
certificate does not already contain the full chain that clients should
receive from the server.
tls12_session_id_resumption controls TLS 1.2 SessionID based resumption.
When omitted, it defaults to false for all TLS listeners. TLS 1.2 ticket
resumption is always disabled.
session_cache_size sets the maximum number of stateful TLS resumption cache
entries for the listener. The cache is shared by TLS 1.2 SessionID resumption
and TLS 1.3 stateful ticket resumption. When omitted, Radiator stores up to
256 entries. Increase this value for high-volume TLS listeners when clients
need a longer window to resume sessions. The value must be at least 1.
Each cached session stores TLS resumption state, negotiated protocol details,
optional client certificate data, and optional application data. A typical entry
without a client certificate uses a few hundred bytes plus cache overhead.
Entries that include client certificate chains are larger. Set
session_cache_size so that the maximum cache memory fits the listener's
memory budget.
tls12_session_id_resumption_lifetime sets the maximum lifetime for TLS 1.2
SessionID cache entries. The value accepts standard duration units (for
example 24h, 30m, 90s, or 0s). A bare number is interpreted as
milliseconds. Positive sub-second values are rejected because TLS session
lifetimes are stored in whole seconds. When omitted, TLS 1.2 SessionID entries
use a 24-hour lifetime.
tls13_session_tickets controls TLS 1.3 session tickets only. When omitted,
it defaults to 0, so TLS 1.3 session resumption is disabled unless the
listener explicitly enables ticket issuance. TLS 1.3 early data (0-RTT) is
always disabled irrespective of this setting.
tls13_session_ticket_lifetime sets the advertised ticket_lifetime value
carried in each TLS 1.3 NewSessionTicket message. The value accepts
standard duration units (for example 24h, 30m, 90s); a bare number is
interpreted as milliseconds for consistency with other Radiator duration
parameters. The TLS 1.3 ticket_lifetime field is expressed in whole
seconds, so sub-second positive values (such as 100ms) are rejected at
configuration load to avoid silently disabling resumption. Setting it to
0 (or 0s) disables resumption ticket issuance entirely, equivalent to
tls13_session_tickets 0;. When the parameter is omitted, ticket issuance is
disabled. A positive lifetime only has an effect when tls13_session_tickets
is also set to a value greater than 0.
When a listener allows both TLS 1.2 and TLS 1.3, Radiator uses a conservative shared-cache policy. If either protocol has resumption disabled, Radiator disables the shared server-side resumption cache for the listener. This avoids advertising TLS 1.2 SessionIDs that the server will not resume. Use separate TLS 1.2-only and TLS 1.3-only listeners if you need independent resumption settings for each protocol version.
Example: HTTPS Server
certificates {
x509 "SERVER_CERT" {
filename "/var/lib/radiator/certs/server.pem";
}
key "SERVER_KEY" {
filename "/var/lib/radiator/certs/server-key.pem";
}
}
servers {
http "HTTPS_SERVER" {
listen {
protocol tls;
port 8443;
ip 0.0.0.0;
tls {
certificate "SERVER_CERT";
certificate_key "SERVER_KEY";
require_client_certificate false;
}
}
}
}
Example: RadSec Server (RADIUS over TLS)
RadSec typically requires mutual TLS (mTLS) where both server and client authenticate with certificates:
certificates {
x509 "RADSEC_CERT" {
filename "/var/lib/radiator/certs/radsec-server.pem";
}
key "RADSEC_KEY" {
filename "/var/lib/radiator/certs/radsec-server-key.pem";
}
x509 "RADSEC_SERVER_CA" {
filename "/var/lib/radiator/certs/radsec-ca.pem";
}
x509 "RADSEC_CLIENT_CA" {
filename "/var/lib/radiator/certs/radsec-client-ca.pem";
}
}
servers {
radius "RADSEC" {
listen {
protocol tls;
port 2083;
ip 0.0.0.0;
tls {
certificate "RADSEC_CERT";
certificate_key "RADSEC_KEY";
server_ca_certificate "RADSEC_SERVER_CA";
# RadSec requires client certificates
require_client_certificate true;
client_ca_certificate "RADSEC_CLIENT_CA";
@verification {
if any {
cert.valid != true;
# Require certificate issued under network device policy
cert.policy != "1.3.6.1.4.1.99999.1.2.3";
# Require certificate from partner organization
cert.subject.o != "Partner Network Inc";
} then {
reject;
} else {
accept;
}
}
}
}
clients "RADSEC_CLIENTS";
}
}
Example: TACACS+ over TLS
Add a tls { ... } block to a TACACS+ server listen { protocol tls; ... }
section. The TLS listener honors ip-accept, listener timeout,
keepalive, and per-client timeout exactly like the plain TCP listener.
servers {
tacacs-plus "TACACS_TLS" {
listen {
protocol tls;
port 300;
ip 0.0.0.0;
ip-accept "TACACS_TLS_ACCESS";
timeout 30s;
tls {
certificate "TACACS_SERVER_CERT";
certificate_key "TACACS_SERVER_KEY";
require_client_certificate true;
client_ca_certificate "TACACS_CLIENT_CA";
}
}
clients "TACACS_TLS_CLIENTS";
policy "POLICY_TACACS_PLUS";
}
}
Notes:
- TACACS+ TLS and RadSec listeners support
proxy-protocol v2;. The proxy sends the PROXY header before the TLS handshake. Radiator reads the header first, then uses the real client address from it for client matching. - Prefer running TACACS+ TLS and TACACS+ TCP on separate ports (and ideally
separate
tacacs-plusserver blocks). Distinct ports keep the configuration easier to reason about, simplify firewall rules, and avoid surprises when the same client address appears under both transports. If you must share the address space, TACACS+ clients can declareprotocol tls;in theirsourceblock to disambiguate; see clients.tacacs-plus for the matching rules. - For TACACS+ over TLS deployment details, including listener, client, and obfuscation settings, see TACACS+ Authentication, Authorization, and Accounting.
For policy, multistage authentication, and operational guidance, see TACACS+ Authentication, Authorization, and Accounting.
Example: Mutual TLS (mTLS)
When client certificate verification is required:
certificates {
x509 "SERVER_CERT" {
filename "/var/lib/radiator/certs/server.pem";
}
key "SERVER_KEY" {
filename "/var/lib/radiator/certs/server-key.pem";
}
x509 "SERVER_CA" {
filename "/var/lib/radiator/certs/ca.pem";
}
x509 "CLIENT_CA" {
filename "/var/lib/radiator/certs/client-ca.pem";
}
}
servers {
http "MTLS_SERVER" {
listen {
protocol tls;
port 8443;
ip 0.0.0.0;
tls {
certificate "SERVER_CERT";
certificate_key "SERVER_KEY";
server_ca_certificate "SERVER_CA";
# Require and validate client certificates
require_client_certificate true;
client_ca_certificate "CLIENT_CA";
}
}
}
}
EAP-TLS handshake logging
EAP-TLS and the EAP methods that tunnel TLS inside it (EAP-TTLS, EAP-PEAP,
EAP-TEAP) emit a structured logging scope around the inner TLS handshake.
The records below show example log entries. Fragment records do not include
payload bytes; use a packet capture and
keylog_filename for byte-level analysis in Wireshark.
Example JSON log records
A successful EAP-TLS handshake over TLS 1.3 (with --json-log-format):
{
"timestamp": "2026-05-21T10:14:07.482914Z",
"level": "DEBUG",
"namespace": ["server", "radius", "AUTH_RADIUS_UDP"],
"message": "TLS handshake complete",
"fields": {
"context_id": "e7c1d2c6-4a3f-4f9c-8f9a-2d4f1b1c9a44",
"eap_type": "tls",
"eap_identifier": 12,
"eaptls.version": 0,
"tls.role": "server",
"tls.timeout_ms": 5000,
"tls.max_fragment_size": 1024,
"tls.version": "TLSv1_3",
"tls.ciphersuite": "TLS13_AES_256_GCM_SHA384",
"tls.key_exchange_group": "X25519",
"tls.session_resumed": false,
"tls.exporter.label": "EXPORTER_EAP_TLS_Key_Material",
"tls.msk.exported": true,
"tls.msk.len": 128,
"tls.eap_tls13_trailer": true,
"tls.peer_cert.subject": "cn=testUser,o=Radiator Software,c=FI",
"tls.peer_cert.issuer": "cn=Radiator Intermediate CA I2,o=Radiator Software,c=FI",
"tls.peer_cert.chain_len": 2,
"tls.peer_cert.serial": "1a:2b:3c",
"tls.peer_cert.not_before": 1716285247,
"tls.peer_cert.not_before_timestamp": "2024-05-21T09:54:07Z",
"tls.peer_cert.not_after": 1747821247,
"tls.peer_cert.not_after_timestamp": "2025-05-21T09:54:07Z",
"tls.peer_cert.fingerprint_sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"tls.peer_cert.sans": "dns:testuser.radiatorsoftware.example.com",
"tls.peer_cert.authority_key_identifier": "5c8fb3d99eabd69c6f884fa4aab442b5292f2f6b",
"tls.peer_cert.subject_key_identifier": "9d2892c2c6730bd7ee8b069d115ffe5e77d1dc7f",
"tls.peer_cert.intermediates": [
{
"chain_index": 1,
"subject": "cn=Radiator Intermediate CA I2,o=Radiator Software,c=FI",
"issuer": "cn=Radiator Root CA,o=Radiator Software,c=FI",
"serial": "10:02",
"not_before": 1704067200,
"not_before_timestamp": "2024-01-01T00:00:00Z",
"not_after": 1861833600,
"not_after_timestamp": "2028-12-31T00:00:00Z",
"fingerprint_sha256": "cfec366a0526a404cfe154527822b93fb9a8cdfbabf6fc259c0b24ea0ff8176e",
"authority_key_identifier": "884a0c7f8a3f38d8c2e886c83276f6d0a3b5cc37",
"subject_key_identifier": "5c8fb3d99eabd69c6f884fa4aab442b5292f2f6b"
}
],
"identity": "testUser@radiatorsoftware.example.com",
"calling_station_id": "22-44-66-00-11-22",
"called_station_id": "22-44-66-11-22-33:test.radiator",
"nas_identifier": "ap-lobby-1",
"nas_port": 0,
"nas_ip_address": "192.0.2.10"
}
}
A handshake failure where the Windows supplicant rejected the server
certificate or certificate chain. Depending on the Windows version and
certificate problem, the alert can appear as AccessDenied,
InternalError, or another fatal TLS alert:
{
"timestamp": "2026-05-21T10:15:21.107834Z",
"level": "WARN",
"namespace": ["server", "radius", "AUTH_RADIUS_UDP"],
"message": "TLS handshake failed",
"fields": {
"context_id": "1f8d3b4a-3b9d-49a1-b2a4-8a3f4b6c8d10",
"eap_type": "ttls",
"eap_identifier": 4,
"tls.role": "server",
"tls.timeout_ms": 5000,
"tls.max_fragment_size": 1024,
"identity": "anonymous@radiatorsoftware.example.com",
"calling_station_id": "AA-BB-CC-11-22-33",
"called_station_id": "11-22-33-AA-BB-CC:corp-wifi",
"nas_identifier": "ap-floor3-east",
"handshake.error.source": "tls",
"handshake.error.kind": "alert_received",
"handshake.error.detail": "received fatal alert: AccessDenied"
}
}
An outbound fragment record produced at Trace level when
max-fragment-size forces the server certificate flight to be split
across several EAP-TLS fragments:
{
"timestamp": "2026-05-21T10:14:07.481102Z",
"level": "TRACE",
"namespace": ["server", "radius", "AUTH_RADIUS_UDP"],
"message": "EAP-TLS fragment sent to peer",
"fields": {
"context_id": "e7c1d2c6-4a3f-4f9c-8f9a-2d4f1b1c9a44",
"eap_type": "tls",
"eap_identifier": 3,
"tls.role": "server",
"tls.max_fragment_size": 256,
"tls.bytes_total": 2104,
"frag.index": 1,
"frag.size": 252,
"frag.remaining": 1852,
"frag.more": true,
"frag.length_included": true
}
}
See Also
- certificates - Certificate configuration
- tls.verification - Custom certificate verification rules
- servers.http.listen - HTTP listen configuration
- servers.http - HTTP server configuration
- clients.tacacs-plus - TACACS+ client matching, secrets, and timeouts
- servers.listen.ip-accept - Pre-admission IP filtering
- servers.listen.proxy-protocol - PROXY protocol behavior on TCP and TLS listeners
- servers.radius.listen - RADIUS listen configuration
- servers.radius - RADIUS server configuration
Session resumption and CRL enforcement
Resumed TLS sessions skip the certificate verification callback, so a peer
whose certificate has been revoked since the original handshake can continue
using a resumption ticket or session ID until that ticket expires. When prompt
revocation enforcement is required (for example mTLS deployments that rely on
crl or crl-directory),
disable TLS 1.3 session tickets so every connection performs a full handshake:
tls {
certificate "SERVER_CERT";
certificate_key "SERVER_KEY";
server_ca_certificate "CA_CERT";
# Force a full handshake on every connection so the current CRL
# snapshot is consulted for every peer.
tls12_session_id_resumption false;
tls13_session_tickets 0;
}
TLS 1.2 SessionID resumption and TLS 1.3 session tickets are disabled by
default for all TLS listeners. Set tls12_session_id_resumption true; or
tls13_session_tickets N; only on listeners that should allow resumption.