OUR SITES NetworkRADIUS FreeRADIUS

Radius Module

The radius module in v4 implements RADIUS proxying and replication. In v3, RADIUS proxying is a special kind of configuration, with it’s own load-balancing, fail-over etc. That configuration is now simpler in v4. The outbound RADIUS proxying is done by just another module: the radius module. Load-balancing and redundant fail-over are handled by the load-balance and redundant keywords.

In v4, the radius module most clearly maps to a home_server in v4. The radius module typically makes a connection from one source IP address to a server at one destination IP/port. It may open multiple source ports, depending on how many packets are being proxied.

Recommendations

In most cases, the v3 configuration be moved to v4 without too many problems. For each home_server foo { …​ } in v3, create a v4 module `radius foo { …​ } `. Copy over the IP address, port, and secret configuration items. When this step is finished, the bulk of the basic work is done.

Converting a home_server_pool foo { …​ } can be done by creating a module in mods-enabled, either load-balance foo { …​ }, or redundant foo { …​ }. i.e. the v3 module configuration does not have any instantiate section. You can just list "virtual" modules directly in the modules directory!

The contents of the load-balance or redundant section will be the list of home servers which will part of that load-balance or redundant pool.

Then, anywhere you want to use a home server, just list it’s name in an unlang processing section. Anywhere you want to list a home server pool, just list it’s name in an unlang processing section.

Unlike v3, there is no Proxy-To-Realm attribute, or Home-Server-Pool, or Home-Server-Name. Instead, you just configure a module (radius, or load-balance), and then use the module anywhere you want to proxy packets.

In most cases, you can just set something like Auth-Type := example.com, and then have a section authenticate example.com { …​ }, which contains the radius modules which do proxying. See sites-available/default for sample configuration which uses this pattern.

Behavior

The module adds a Proxy-State attribute to all proxied packets. This Proxy-State contains a 32-bit random number, which is unique to this module. This unique number helps to detect proxy loops.

The reply from home server is appended to the reply list for the current packet.

For security reasons, the module ensures that all proxied Access-Request packets contain a Message-Authenticator attribute. This behavior is NOT configurable, and CANNOT be changed. This behavior is part of the BlastRADIUS mitigations.

Any proxied packet (including Accounting-Request) can receive a Protocol-Error response packet. This packet is an explicit NAK that something went wrong processing the request.

Unlike v3, the server does not support any "pre-proxy" or "post-proxy" processing sections. Similarly, this module does not support any "proxy" or "proxy-reply" list. Instead, the current request is proxied as-is, and the proxied reply is added to the current reply list. If you want to modify the proxied request and/or proxied reply, then you should use a subrequest block to create a child request. That child request can then be modified independently of the parent. Any reply attributes will have to be copied back manually to the parent request.

Configuration Settings

The configuration settings of this module are similar to the home_server settings in v3.

The module has the following return codes:

Code Description

invalid

Something went wrong sending the request, or the reply packet is invalid.

ok

the module received an ack (Access-Accept, Accounting-Response, CoA-ACK, etc.)

handled

the module received an Access-Challenge

fail

there was no response to the proxied request

reject

the module received a nak (Access-Reject, CoA-NAK, etc.)

noop

the module saw its own Proxy-State attribute, and is will not proxy the request. This behavior prevents proxy loops.

mode

What kind of client behavior is being used.

proxy - forward packets which are received from a NAS - each packet has a Proxy-State attribute added. - it looks for, and stops proxy loops - retransmissions are sent only when the NAS retransmits - the module fails if it does not receive a reply

client - originate packet, and do retransmissions ourselves - no Proxy-State is added. - the module retransmits as needed - the module fails if it does not receive a reply

replicate - send packets without waiting for a reply. - no Proxy-State is added - the module does not expect any reply - all replies are discarded - the module continues with "ok" immediately after sending the packet.

unconnected-replicate - replicate packets to dynamic destinations - For unconnected UDP sockets only. - It MUST have transport = udp - It MUST have src_ipaddr = * and no src_port - You CANNOT use the module "in place" as with normal proxying. - It is only supported via the function %replicate.sendto.ipaddr(ipaddr, port, secret)

dynamic-proxy - proxy packets to dynamic destinations - RFC 7585 dynamic DNS lookups are not supported - It MUST have src_ipaddr = * and no src_port - You CANNOT use the module "in place" as with normal proxying. - Proxying is only supported via the function %proxy.sendto.ipaddr(ipaddr, port, secret)

The server can still be used to create (i.e. originate) packets via this module when mode = proxy is set. The module can automatically detect the difference between proxied packets and client packets it originates. Originated packets are taken from the detail file, or result when changing packet type (e.g. Accounting-Request to Disconnect-Request), or when the current request is a subrequest, and the parent request is from a different protocol.

Note that there is no mode = unconnected, where the module then both proxies packets, and replicates them. The need to track replies when proxying means that it’s difficult to both proxy and replicate at the same time. As a result, there are two "unconnected" modes, one for each of "proxy" and "replicate".

transport

Transport protocol. Can be udp or tcp.

max_attributes

Maximum number of attributes to decode in response.

Default is 255.

type

List of allowed packet types.

The module will only send packets types which are listed here. Other types of packets will be ignored. The main purpose of the type configuration is to ensure that the correct packets are being sent to the home server. This entry serves as a double-check against misconfigurations.

In v3, the home_server configuration of auth, acct, or auth+acct is used to find a home server. That is, when FreeRADIUS has an Access-Request packet in v3, it proxies it by looking up a matching home_server.

In v4, proxying is done by listing the radius module in a processing section, such as authenticate radius { …​ }, or recv Accounting-Request { …​ }. So unlike v3, the module doesn’t have to find a proxy destination for a particular kind of packet. Instead, the administrator configures the module to send packets to a destination.

As a result, the module doesn’t really care about what kind of packets it sends. It has a packet, a destination where that packet should be sent, and it sends the packet.

In order to change packet types, see the subrequest keyword.

Status-Server is reserved for connection signaling, and cannot be proxied.

Unlike v3, all packet types are allocated from the same 8-bit ID space. This change does not affect the majority of RADIUS proxying, which only sends one type of packet. This change does not affect the home server which receives these packets, as the home server does not track IDs except to correlate requests to replies.

The only visible difference, then, between v3 and v4 is that in some cases, the new radius module will use more source ports when proxying.

This change simplifies the implementation of the RADIUS client.

require_message_authenticator::Require Message-Authenticator in responses.

A server should include Message-Authenticator attribute as the first attribute in responses to Access-Request packets. This behavior mitigates against the BlastRADIUS attack.

However, not all servers follow this security practice. As a result, this module can be configured to either not require, or require, Message-Authenticator.

If value is auto, then the module will automatically detect the existence of Message-Authenticator in response packets. Once the module sees a Message-Authenticator, it will automatically change the configuration internally to yes. This change prevents security "down-bidding" attacks.

Allowed values: yes, no, auto

The default is auto.

response_window: If we do not receive any replies within this time period, then start zombie_period

zombie_period

If the home server does not reply to packets within response_window, then zombie_period starts.

When zombie_period starts, a connection is marked zombie, and then is not used to send new packets. If there are no responses on this connection within zombie_period, the module either closes the connection (no status_check subsection), or starts pinging the home server (status_check.type = Status-Server).

revive_interval

If there are no status checks, mark the home server alive after revive_interval timeout.

Some home servers do not support status checks via the Status-Server packet. Others may not have a "test" user configured that can be used to query the server, to see if it is alive. For those servers, we have NO WAY of knowing when it becomes alive again. Therefore, after the server has been marked dead, we wait a period of time, and mark it alive again, in the hope that it has come back to life.

If it has NOT come back to life, then the module will wait for zombie_period before marking it dead again. During the zombie_period, ALL AUTHENTICATIONS WILL FAIL, because the home server is still dead. There is NOTHING that can be done about this, other than to enable the status checks, as documented above.

e.g. if zombie_period is 40 seconds, and revive_interval is 300 seconds, the for 40 seconds out of every 340, or about 10% of the time, all authentications will fail.

If the zombie_period and revive_interval configurations are set smaller, than it is possible for up to 50% of authentications to fail.

As a result, we recommend enabling status checks, and we do NOT recommend using revive_interval.

The revive_interval configuration is used ONLY if the status_check subsection is not used. Otherwise, revive_interval is not necessary, and should be deleted.

Useful range of values: 10 to 3600

Check the status of a connection

status_check { …​ }

For "are you alive?" queries.

If the home server does not respond to proxied packets, the module starts pinging the home server with these packets.

type

You can specify any type of request packet here, e.g. 'Access-Request', 'Accounting-Request' or 'Status-Server'.

Status-Server is recommended as other packet types may be interpreted incorrectly, or may ve proxied to a remote server, which defeats the purpose of the status checks.

If you specify another type of packet, it MUST be listed as an allowed type above.

Status-Server packet contents are fixed and cannot be edited.

For other packet types, you can set the contents here. The section MUST be set over "&request.<attribute> = value", and anything else will cause a parse error.

We RECOMMEND that you use packet contents which lets the other end easily tell that they are not "real" packets from a NAS.

The example here is for Access-Request. The contents will vary by other packet types.

The module will automatically update the contents of the Event-Timestamp attribute to be the time when the packet is sent. The module will also automatically add a Proxy-State attribute.

Do NOT do SQL queries, LDAP queries, dynamic expansions, etc. in this section. The contents are created when a connection is opened, and are not changeable after that.

Transport Protocols

The module supports multiple transport protocols.

File Output

Write raw RADIUS packets (no IP or UDP header) to a file. This transport can only be used for mode = replicate

UDP Transport

Much like the v3 home_server configuration.

src_ipaddr

IP we open our socket on.

Destination IP address, port, and secret.

Use ipv4addr = …​ to force IPv4 addresses. Use ipv6addr = …​ to force IPv6 addresses.

interface

Interface to bind to.

max_packet_size

Our max packet size. may be different from the parent.

recv_buff

How big the kernel’s receive buffer should be.

send_buff

How big the kernel’s send buffer should be.

TCP

The TCP configuration is identical to the udp configuration.

Connection trunking

Each worker thread (see radiusd.conf, num_workers), has it’s own set of connections. These connections are grouped together into a "pool".

Much of the configuration here is similar to the old connection "pool" configuration in v3. However, there are more configuration parameters, and therefore more control over the behavior.

start

Connections to create during module instantiation.

If the server cannot create specified number of connections during instantiation it will exit.

Set to 0 to allow the server to start without the database being available.

min

Minimum number of connections to keep open.

max

Maximum number of connections.

If these connections are all in use and a new one is requested, the request will NOT get a connection.

connecting

Maximum number of sockets to have in the "connecting" state.

If a home server goes down, the module will close old / broken connections, and try to open new ones. In order to avoid flooding the home server with connection attempts, set the connecting value to a small number.

uses

number of packets which will use the connection.

After uses packets have been sent the connection will be closed, and a new one opened. For no limits, set uses = 0.

lifetime

lifetime of a connection, in seconds.

After lifetime seconds have passed, no new packets will be sent on the connection. When all replies have been received, the connection will be closed.

For no limits, set lifetime = 0.

It is possible to use precise times, such as lifetime = 1.023, or even qualifiers such as lifetime = 400ms.

open_delay

How long (in seconds) a connection must be above per_connection_target before a new connection is opened.

Parsing of this field is the same as for lifetime.

close_delay

How long (in seconds) a connection must be below per_connection_target before a connection is closed.

manage_interval

How often (in seconds) the connections are checked for limits, in order to open / close connections.

connection { …​ }

Per-connection configuration.

connection_timeout

How long to wait before giving up on a connection which is being opened.

reconnect_delay

If opening a connection fails, or an open connection fails, we wait reconnect_delay seconds before attempting to open another connection.

request { …​ }

Per-request configuration.

per_connection_max

The maximum number of requests which are "live" on a particular connection.

per_connection_target

The target number of requests which are "live" on a particular connection.

There can be a balance between overloading a connection, and under-utilizing it. The default is to fill each connection before opening a new one.

free_delay

How long to wait before freeing internal resources associated with the connection.

Retransmission timers.

Each packet can have its own retransmission timers.

The sections are named for each packet type. The contents are the same for all packet types.

Access requests packets

initial_rtx_time

If there is no response within this time, the module will retransmit the packet.

Value should be 1..5.

max_rtx_time

The maximum time between retransmissions.

Value should be 5..30

  • The following are maximums that all apply.

i.e. if any one of the limits is hit, the retransmission stops.

max_rtx_count

How many times the module will send the packet before giving up.

Value should be 1..20 (0 == retransmit forever)

max_rtx_duration

The total length of time the module will try to retransmit the packet.

Value should be 5..60

Accounting Packets

i.e. If you want retransmit forever, you should set:

max_rtx_time = 0
max_rtx_count = 0

CoA Packets

Disconnect packets

Status-Server packets

The configuration here helps the module determine if a home server is alive and responding to requests.

The Status-Server packets CANNOT be proxied.

Replication of Packets

The module supports replication of packets to new destinations at run time. In this context, replication means "send the packet, and do not wait for the response". This functionality is most useful when copying large amounts of accounting data to multiple destinations.

The module can then only be used as a dynamic expansion. That is, you cannot specify the replicate module directly in a processing section.

Usage

This module can only be used as a dynamic expansion. Since the module does not wait for any response, the expansion does not return any value.

The module can be called from any processing section as follows:

The function takes three arguments:

This function allows the module to send packets to any destination, where the destination is chosen dynamically at run time. The arguments to the function can be take from other attributes, database queries, etc.

Generally you only want to replicate accounting packets.

We are not opening a socket from our server to their server. We are replicating packets.

For replicated packets, only UDP is supported.

UDP Transport

For unconnected modes, only UDP is supported.

src_ipaddr

The source IP address used by the module.

src_port cannot be used. If it is used here, the module will refuse to start. Instead, the module will open a unique source port per thread.

secret cannot be used. If it is used, the value will be ignored.

Other Configuration

No other configuration items are supported when using mode = unconnected-replicate.

The pool configuration is ignored, as is status-check, along with all per-packet timeouts.

A dynamic proxy module

We are not opening a socket from our server to their server. We are replicating packets.

Both UDP and TCP are supported.

UDP Transport

src_ipaddr

The source IP address used by the module.

src_port cannot be used. If it is used here, the module will refuse to start. Instead, the module will open a unique source port per thread.

secret cannot be used. If it is used, the value will be ignored.

Connection trunking

See above for documentation on connection trunking.

start

Connections to create during module instantiation.

If the server cannot create specified number of connections during instantiation it will exit.

Set to 0 to allow the server to start without the database being available.

min

Minimum number of connections to keep open.

max

Maximum number of connections.

If these connections are all in use and a new one is requested, the request will NOT get a connection.

connecting

Maximum number of sockets to have in the "connecting" state.

If a home server goes down, the module will close old / broken connections, and try to open new ones. In order to avoid flooding the home server with connection attempts, set the connecting value to a small number.

uses

number of packets which will use the connection.

After uses packets have been sent the connection will be closed, and a new one opened. For no limits, set uses = 0.

lifetime

lifetime of a connection, in seconds.

After lifetime seconds have passed, no new packets will be sent on the connection. When all replies have been received, the connection will be closed.

For no limits, set lifetime = 0.

It is possible to use precise times, such as lifetime = 1.023, or even qualifiers such as lifetime = 400ms.

open_delay

How long (in seconds) a connection must be above per_connection_target before a new connection is opened.

Parsing of this field is the same as for lifetime.

close_delay

How long (in seconds) a connection must be below per_connection_target before a connection is closed.

manage_interval

How often (in seconds) the connections are checked for limits, in order to open / close connections.

connection { …​ }

Per-connection configuration.

connection_timeout

How long to wait before giving up on a connection which is being opened.

reconnect_delay

If opening a connection fails, or an open connection fails, we wait reconnect_delay seconds before attempting to open another connection.

request { …​ }

Per-request configuration.

per_connection_max

The maximum number of requests which are "live" on a particular connection.

per_connection_target

The target number of requests which are "live" on a particular connection.

There can be a balance between overloading a connection, and under-utilizing it. The default is to fill each connection before opening a new one.

free_delay

How long to wait before freeing internal resources associated with the connection.

Default Configuration

radius {
	mode = proxy
	transport = udp
#	max_attributes = 255
	type = Access-Request
	type = Accounting-Request
	require_message_authenticator = auto
	response_window = 15
	zombie_period = 10
	revive_interval = 3600
	status_check {
		type = Status-Server
#		update request {
#			&User-Name := "test-user"
#			&User-Password := "this-is-not-a-real-password"
#			&NAS-Identifier := "Status check.  Are you alive?"
#			&Event-Timestamp = 0
#		}
	}
	file {
	     filename = ${logdir}/packets.bin
	}
	udp {
#		src_ipaddr = *
		ipaddr = 127.0.0.1
		port = 1812
		secret = testing123
#		interface = eth0
#		max_packet_size = 4096
#		recv_buff = 1048576
#		send_buff = 1048576
	}
#	tcp {
#		...
#	}
	pool {
		start = 0
		min = 1
		max = 8
		connecting = 1
		uses = 0
		lifetime = 0
		open_delay = 0.2
		close_delay = 1.0
		manage_interval = 0.2
		connection {
			connection_timeout = 3.0
			reconnect_delay = 5
		}
		request {
			per_connection_max = 255
			per_connection_target = 255
			free_delay = 10
		}
	}
	Access-Request {
		initial_rtx_time = 2
		max_rtx_time = 16
		max_rtx_count = 2
		max_rtx_duration = 30
	}
	Accounting-Request {
		initial_rtx_time = 2
		max_rtx_time = 16
		max_rtx_count = 5
		max_rtx_duration = 30
	}
	CoA-Request {
		initial_rtx_time = 2
		max_rtx_time = 16
		max_rtx_count = 5
		max_rtx_duration = 30
	}
	Disconnect-Request {
		initial_rtx_time = 2
		max_rtx_time = 16
		max_rtx_count = 5
		max_rtx_duration = 30
	}
	Status-Server {
		initial_rtx_time = 2
		max_rtx_time = 5
		max_rtx_count = 5
		max_rtx_duration = 30
	}
}
#	%replicate.sendto.ipaddr(127.0.0.1, 1813, 'testing123')
#	* IP address where the packet is sent.  It MUST be the same
#	  address family as `src_ipaddr` below.
#	* port where the packet is sent.
#	* secret for this packet.
radius replicate {
	type = Accounting-Request
	mode = unconnected-replicate
	transport = udp
	udp {
		src_ipaddr = *
	}
}
radius proxy {
	type = Access-Request
	mode = dynamic-proxy
	transport = udp
	udp {
		src_ipaddr = *
	}
	pool {
		start = 0
		min = 1
		max = 8
		connecting = 1
		uses = 0
		lifetime = 0
		open_delay = 0.2
		close_delay = 1.0
		manage_interval = 0.2
		connection {
			connection_timeout = 3.0
			reconnect_delay = 5
		}
		request {
			per_connection_max = 255
			per_connection_target = 255
			free_delay = 10
		}
	}
}