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 |
handled |
the module received an |
fail |
there was no response to the proxied request |
reject |
the module received a |
noop |
the module saw its own |
- 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
ortcp
. - 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
, thenzombie_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.
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
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
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.
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
}
}
}