FreeRADIUS clients.conf: Examples and Best Practices

Short answer: each device that sends requests to FreeRADIUS, such as an access point, needs a client block in clients.conf with the source IP address the server sees and a shared secret. The same secret must be configured on the device. Check the configuration with freeradius -XC, restart FreeRADIUS, then send a test request from that address. FreeRADIUS 3 reads static clients only at startup, so a reload or HUP signal does not add them.

Tested with

Addresses in this guide come from documentation ranges (192.0.2.0/24, 203.0.113.0/24). Replace them, and every secret, with your own values. In the examples, the FreeRADIUS server is 192.0.2.1, the access point office-ap-01 is 192.0.2.10, and the other office devices are in 192.0.2.0/24.

What counts as a RADIUS client

In FreeRADIUS terms, a client is the network device (NAS) that sends RADIUS requests, for example an access point or a VPN gateway. The people who log in through that device are users, and they are configured elsewhere, for example in mods-config/files/authorize or an SQL database.

clients.conf answers one question: which devices may talk to this server, and which shared secret each of them uses. Requests from an address that matches no client are ignored; the log message is shown under Unknown client.

Minimal client example

client office-ap-01 {
	ipaddr = 192.0.2.10
	secret = "REPLACE-WITH-A-32-CHARACTER-RANDOM-SECRET"
}

You do not need shortname: the comments in the stock clients.conf describe it as accepted for compatibility with version 1.x and no longer necessary since 2.0. nas_type is optional; the stock file explains that it is used by checkrad.pl for simultaneous-use checks.

Generate the shared secret

The stock clients.conf in FreeRADIUS 3.2.10 recommends a random secret of at least 16 characters, preferably 32, and suggests this command:

dd if=/dev/random bs=1 count=24 | base64

It prints 32 characters. Keep the secret in double quotes in clients.conf. FreeRADIUS warns at startup when a secret is short, for example for the default test client:

Shared secret for client localhost is short, and likely can be broken by an attacker.

Prefer IP addresses to host names

ipaddr also accepts a DNS name, but the stock comments strongly recommend IP addresses. The name is resolved once at startup, later DNS changes are not seen, and the server does not start if the lookup fails.

Several clients and whole networks

You can define one client per device, or one client for a network written in CIDR notation:

client office-ap-01 {
	ipaddr = 192.0.2.10
	secret = "REPLACE-WITH-SECRET-FOR-OFFICE-AP-01"
}

client office-network {
	ipaddr = 192.0.2.0/24
	secret = "REPLACE-WITH-SECRET-FOR-OFFICE-DEVICES"
}

192.0.2.10 matches both entries. When a request arrives, FreeRADIUS uses the most specific matching client, so requests from 192.0.2.10 use office-ap-01. In our test, the access point was accepted only with its own secret, and a request from it with the network secret got no reply. Other addresses in the network, such as 192.0.2.23, used the network secret.

Two client blocks with the same address are an error, and the server does not start:

Failed to add duplicate client dup-b
/etc/freeradius/clients.conf[408]: Failed to add client dup-b

A network entry is convenient, but every device in that range shares one secret. Separate entries let you rotate the secret of one device without touching the others.

Find the clients.conf your server actually loads

Edit the file that the running server reads. The location depends on how FreeRADIUS was installed:

Installation File
Ubuntu 24.04 package (freeradius 3.2.5) /etc/freeradius/3.0/clients.conf
Official Docker image 3.2.10 /etc/freeradius/clients.conf (/etc/raddb is a symlink to /etc/freeradius)

To confirm, run the FreeRADIUS configuration check and look for the included files:

sudo freeradius -XC | grep 'including configuration file'
including configuration file /etc/freeradius/3.0/clients.conf

Keep clients in separate files

clients.conf can include a directory:

$INCLUDE clients.d/

Each file in clients.d/ then holds one or more client blocks. The output of freeradius -XC lists them:

including files in directory /etc/freeradius/clients.d/
including configuration file /etc/freeradius/clients.d/branch-switch-01.conf

In our test, an editor backup file (branch-switch-01.conf~) in the same directory was not loaded.

Apply a change: save, check, restart, test

Each step can succeed while the next one fails, so check them separately.

1. Save the file

Make the change and save clients.conf.

If FreeRADIUS runs in Docker with clients.conf mounted as a single file, edit the file in place. Tools that replace the file, such as sed -i, create a new file on the host. The container keeps seeing the old one until it is restarted. Mounting the whole configuration directory avoids this.

2. Check the configuration

# Package installation
sudo freeradius -XC

# Docker
docker exec <freeradius-container> freeradius -XC

Expected last line:

Configuration appears to be OK

Do not skip this step. FreeRADIUS does not start with a broken configuration. In our Docker test, restarting with a syntax error stopped the server instead of leaving it running with the old configuration. The Ubuntu systemd unit also runs a configuration check before it starts the server.

3. Restart FreeRADIUS

# Package installation (systemd)
sudo systemctl restart freeradius

# Docker
docker restart <freeradius-container>

Static clients are read at startup, so use systemctl restart after adding or changing them. On the Ubuntu package, systemctl reload freeradius only checks the configuration and sends the server a HUP signal. FreeRADIUS lead developer Alan DeKok: “You can’t reload clients.conf via a HUP.” If you need to add clients without restarting, look at FreeRADIUS dynamic clients instead.

4. Send a test request

radtest is in the freeradius-utils package and in the official Docker image. The request must come from the address in ipaddr. Run it on a Linux host at that address, or temporarily add your test host as a client. Use a test user that exists on your server:

radtest test-user test-password 192.0.2.1 0 'REPLACE-WITH-THE-CLIENT-SECRET'

Arguments: user name, password, server address, NAS port (0), and the shared secret. Expected result:

Received Access-Accept from 192.0.2.1:1812 to 192.0.2.10:<port> length 38

Read the result carefully:

Message-Authenticator and BlastRADIUS settings

FreeRADIUS 3.2.10 drops requests without a Message-Authenticator attribute by default, while the Ubuntu 24.04 package (3.2.5) accepts them by default.

Two settings control this: require_message_authenticator and limit_proxy_state. Releases with the fix for the BlastRADIUS vulnerability (3.2.5 and 3.0.27 or later) set global defaults for both in the security section of radiusd.conf, and a client block can override them. Defaults in the versions we checked:

Setting 3.2.10, official image 3.2.5, Ubuntu 24.04
require_message_authenticator yes auto
limit_proxy_state auto auto

Check your own radiusd.conf: the default of require_message_authenticator changed between these versions.

A device that does not send Message-Authenticator gets no reply on 3.2.10 with default settings, and the log shows:

Receive - Insecure packet from host 192.0.2.10:  Packet does not contain required Message-Authenticator attribute.  You may need to set "require_message_authenticator = no" in the configuration.

Recommendations:

Common errors

Wrong shared secret

The device gets no reply. FreeRADIUS 3.2.10 logs:

Dropping packet without response because of error: Received packet from 192.0.2.10 with invalid Message-Authenticator!  (Shared secret is incorrect.) (from client office-ap-01)

The client was found (from client office-ap-01), so the address is right. Compare the secret on the device and in clients.conf character by character. A more specific client entry for the same address takes precedence; see Several clients and whole networks.

Missing closing brace

/etc/freeradius/clients.conf[414]: EOF reached without closing brace for section client starting at line 410
Errors reading or parsing /etc/freeradius/radiusd.conf

Missing equals sign

ipaddr 192.0.2.10 instead of ipaddr = 192.0.2.10:

/etc/freeradius/clients.conf[406]: Expecting section start brace '{' after "ipaddr 192.0.2.10"
Errors reading or parsing /etc/freeradius/radiusd.conf

The number in brackets is the line in clients.conf.

Unknown client

Ignoring request to auth address * port 1812 bound to server default from unknown client 203.0.113.77 port 52631 proto udp

No client matches the source address of the request. Compare the address in the message with the ipaddr values in the loaded clients.conf; ipaddr must match the address in the message. Behind NAT or Docker port publishing, that address can differ from the device’s own. In our Docker test, a request sent from the host to the published port 127.0.0.1:18121 arrived from the Docker network gateway 10.88.10.1; the result depends on the Docker network configuration. If the client is already in the file, restart FreeRADIUS: a client added after startup is not loaded yet.

Frequently asked questions

Do I need to restart FreeRADIUS after editing clients.conf?

Yes. FreeRADIUS 3 reads static clients at startup. systemctl reload sends a HUP signal, and HUP does not reload clients.conf. Check the configuration with freeradius -XC first, because the server does not start with an error in the file.

Where is clients.conf?

On the Ubuntu 24.04 package it is /etc/freeradius/3.0/clients.conf. In the official Docker image 3.2.10 it is /etc/freeradius/clients.conf. The output of freeradius -XC lists the file the server loads.

Can one client entry cover a whole subnet?

Yes. Set ipaddr to a CIDR range such as 192.0.2.0/24. When an address also has its own client entry, FreeRADIUS uses the most specific match.

Can I use a hostname in ipaddr?

FreeRADIUS accepts a DNS name, but resolves it once at startup and does not start if the lookup fails. The comments in the stock configuration recommend IP addresses.

How long should a shared secret be?

The FreeRADIUS 3.2.10 configuration comments recommend at least 16 random characters, preferably 32. dd if=/dev/random bs=1 count=24 | base64 prints a 32-character secret.

Why does FreeRADIUS not answer my device?

When a request gets no reply, the server log usually shows why. The common messages are an unknown client, an invalid Message-Authenticator from a wrong shared secret, and a packet without a required Message-Authenticator attribute. Each is shown under Common errors and Message-Authenticator and BlastRADIUS settings.

Manage clients.conf from a web interface

Editing clients.conf by hand works well for a few devices. If you manage many devices and rotate their secrets, you can use RadiusDeck. This open-source web interface edits the clients.conf file you point it to. It preserves comments, blank lines, order, and nested blocks, and creates a backup before each change. You still apply changes by restarting FreeRADIUS, or through an optional reload sidecar. RadiusDeck manages clients only; user accounts and authentication policies stay in FreeRADIUS.

The RadiusDeck README covers installation, and its demo adds a client and checks a real authentication request against FreeRADIUS.

Sources