FreeRADIUS is the most widely used RADIUS server. Switches, Wi-Fi access points, VPN gateways and routers send it authentication requests, and it answers with accept or reject plus attributes such as the VLAN a user belongs to. In this tutorial you will install FreeRADIUS 3 on Ubuntu 24.04, test it locally with radtest, register a network device as a client, authenticate users from the local users file and from an LDAP directory, and return VLAN assignments.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS with a static IP address, for example a CubePath VPS, and a non-root user with sudo privileges.
  • The IP address of at least one network device (NAS) that will query the server, called nas_ip in this guide.
  • Optionally, an LDAP directory and a read-only bind account if you want to authenticate directory users.

RADIUS uses UDP port 1812 for authentication and 1813 for accounting. Only your network devices need to reach these ports.

Step 1 - Installing FreeRADIUS

Install the server, the client utilities (radtest, radclient) and the LDAP module:

sudo apt update
sudo apt install -y freeradius freeradius-utils freeradius-ldap

The package starts the freeradius service with a working default configuration. Check the version and the service:

freeradius -v | head -n 1
systemctl status freeradius --no-pager
radiusd: FreeRADIUS Version 3.2.5, for host x86_64-pc-linux-gnu
● freeradius.service - FreeRADIUS multi-protocol policy server
     Active: active (running) since ...

On Ubuntu and Debian the configuration lives in /etc/freeradius/3.0/ (on RHEL-based systems it is /etc/raddb/). The files you will touch are:

PathPurpose
clients.confNetwork devices allowed to send requests, and their shared secrets
mods-config/files/authorizeLocal users file (also reachable through the users symlink)
mods-available/ and mods-enabled/Modules such as ldap and eap, enabled with symlinks
sites-enabled/defaultMain virtual server: the authorize and authenticate sections

Step 2 - Running FreeRADIUS in debug mode

Debug mode prints every request, every module that runs and every attribute returned. It is the fastest way to see why something fails, so use it while you configure the server. Stop the service and start the daemon in the foreground:

sudo systemctl stop freeradius
sudo freeradius -X

The last line should be:

Ready to process requests

Leave it running and open a second SSH session for the next commands. Press Ctrl+C to stop debug mode when you are done.

Step 3 - Adding a test user

Add a local user at the top of the users file. Entries are processed in order, so user entries belong before the DEFAULT entries in the file:

sudo nano /etc/freeradius/3.0/mods-config/files/authorize

Insert these lines at the very beginning of the file. The second line must start with a tab or spaces:

testuser	Cleartext-Password := "your_test_password"
	Reply-Message := "Hello, %{User-Name}"

Restart debug mode (Ctrl+C, then sudo freeradius -X) so it reads the file again. From the second session, send a request to the server. The default clients.conf already allows localhost with the secret testing123:

radtest testuser your_test_password 127.0.0.1 0 testing123
Sent Access-Request Id 214 from 0.0.0.0:40612 to 127.0.0.1:1812 length 78
	User-Name = "testuser"
	User-Password = "your_test_password"
	...
Received Access-Accept Id 214 from 127.0.0.1:1812 to 127.0.0.1:40612 length 38
	Reply-Message = "Hello, testuser"

Try a wrong password as well: you should get Access-Reject, and the debug output explains which module rejected it.

Step 4 - Registering network devices as clients

Every device that sends requests must be declared in clients.conf with a shared secret. FreeRADIUS silently ignores requests from unknown addresses. Generate a long random secret:

openssl rand -base64 24

Open the clients file:

sudo nano /etc/freeradius/3.0/clients.conf

Add a block at the end of the file for each device or subnet, using the secret you generated:

client core-switch {
	ipaddr = 192.0.2.10
	secret = your_shared_secret
	shortname = core-switch
}

client office-aps {
	ipaddr = 192.0.2.0/24
	secret = your_other_shared_secret
	shortname = office-aps
}

Configure the same secret on the device, with the RADIUS server set to your_server_ip, port 1812 for authentication and 1813 for accounting.

Check the syntax without starting the server:

sudo freeradius -XC | tail -n 1
Configuration appears to be OK

Step 5 - Opening the firewall to your devices only

RADIUS shared secrets are the only protection for the protocol, so do not expose the ports to the whole Internet. Allow them only from your devices:

sudo ufw allow OpenSSH
sudo ufw allow from 192.0.2.0/24 to any port 1812,1813 proto udp
sudo ufw enable
sudo ufw status
To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
1812,1813/udp              ALLOW       192.0.2.0/24

Now test from the device itself (most switches and VPN appliances have a "test RADIUS" function) while debug mode is running. You should see the request arrive from nas_ip with testuser.

Step 6 - Authenticating users from LDAP

To use directory accounts instead of the local file, configure the ldap module:

sudo nano /etc/freeradius/3.0/mods-available/ldap

Change these settings inside the ldap { ... } block, leaving the rest of the file as it is:

ldap {
	server = 'ldaps://ldap.example.com'
	identity = 'cn=radius-reader,ou=ServiceAccounts,dc=example,dc=com'
	password = your_bind_password
	base_dn = 'dc=example,dc=com'

	user {
		base_dn = "ou=People,${..base_dn}"
		filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
	}
	...
}

Enable the module with a symlink:

sudo ln -s ../mods-available/ldap /etc/freeradius/3.0/mods-enabled/ldap

The default virtual server already calls the module in its authorize section (the -ldap line). What decides how the password is checked depends on the directory:

  • If the bind account can read the users' userPassword attribute, FreeRADIUS compares it with the pap module and nothing else is needed.
  • If it cannot (Active Directory and most hardened OpenLDAP setups), FreeRADIUS must verify the password by binding as the user. Enable that in the default site.

For the second case, open the default site:

sudo nano /etc/freeradius/3.0/sites-enabled/default

In the authorize { ... } section, find the commented block right after -ldap and uncomment it:

	-ldap

	if ((ok || updated) && User-Password) {
		update {
			control:Auth-Type := ldap
		}
	}

In the authenticate { ... } section, uncomment the LDAP authentication type:

	Auth-Type LDAP {
		ldap
	}

Check the configuration, restart debug mode and test with a directory user:

sudo freeradius -XC | tail -n 1
radtest jdoe 'jdoe_password' 127.0.0.1 0 testing123
Received Access-Accept Id 57 from 127.0.0.1:1812 to 127.0.0.1:52010 length 20

Step 7 - Assigning VLANs with reply attributes

Switches and access points doing 802.1X place a user in a VLAN when the Access-Accept contains three standard tunnel attributes. For a local user, add them as reply items in the users file:

alice	Cleartext-Password := "your_alice_password"
	Tunnel-Type = VLAN,
	Tunnel-Medium-Type = IEEE-802,
	Tunnel-Private-Group-Id = "20"

Every reply line except the last ends with a comma. For LDAP users, match on group membership with the LDAP-Group attribute in the same file, after the user entries:

DEFAULT	LDAP-Group == "cn=engineering,ou=Groups,dc=example,dc=com"
	Tunnel-Type = VLAN,
	Tunnel-Medium-Type = IEEE-802,
	Tunnel-Private-Group-Id = "30"

For group lookups to work, check that the group { ... } block in mods-available/ldap has the right base_dn and membership_attribute (usually memberOf) for your directory. Restart debug mode and test:

radtest alice your_alice_password 127.0.0.1 0 testing123
Received Access-Accept Id 88 from 127.0.0.1:1812 to 127.0.0.1:35510 length 43
	Tunnel-Type:0 = VLAN
	Tunnel-Medium-Type:0 = IEEE-802
	Tunnel-Private-Group-Id:0 = "20"

Step 8 - Running FreeRADIUS as a service

When everything works in debug mode, stop it with Ctrl+C and start the service. The systemd unit checks the configuration before starting:

sudo systemctl enable --now freeradius
systemctl is-active freeradius
active

To log every accepted and rejected login to /var/log/freeradius/radius.log, edit /etc/freeradius/3.0/radiusd.conf and, inside the log { ... } section, set auth = yes. Restart the service with sudo systemctl restart freeradius. Accounting records from your devices are written by the detail module under /var/log/freeradius/radacct/.

Troubleshooting

No response at all from radtest or the device. The request never reached FreeRADIUS or came from an unknown client. In debug mode, look for Ignoring request to auth address * port 1812 ... from unknown client. Add the device's real source address to clients.conf and check sudo ufw status.

Access-Reject with a correct password from a real device. The shared secret differs between the device and clients.conf. The debug output typically shows a garbled User-Password and a warning to double-check the shared secret on the server and the NAS.

The service fails to start after a change. Run sudo freeradius -XC to see the exact file and line, and check sudo journalctl -u freeradius -n 50 --no-pager.

EAP clients reject the server certificate. The package ships a self-signed certificate for testing. For production Wi-Fi, set private_key_file, certificate_file and ca_file in the tls-config tls-common block of /etc/freeradius/3.0/mods-available/eap to a certificate from your own CA, and install that CA on the clients.

Conclusion

You now have FreeRADIUS on Ubuntu 24.04 answering your network devices, authenticating users from a local file or an LDAP directory and returning VLAN assignments, with the RADIUS ports restricted to your equipment. Next, replace the test EAP certificate to enable WPA2/WPA3-Enterprise on your Wi-Fi, store accounting data in SQL with the sql module, or add a second FreeRADIUS server and configure it as a backup on every device.