Ubuntu's FreeRADIUS package ships with a localhost client whose shared secret
is testing123. Here you'll change that secret, restart the server, and confirm
with radtest that FreeRADIUS really uses the new value.
For the editing part I'll use RadiusDeck, a small open-source UI that edits
clients.conf directly, with no SQL database involved. If you also need user management or accounting,
daloRADIUS covers that.
I tested everything below on Ubuntu 22.04.2 with the packaged FreeRADIUS 3.0.26,
RadiusDeck 0.1.0 and Python 3.12.15. The radtest output is from that run.
Before you start
You'll need a test Ubuntu machine with sudo, Git, and a browser on the same
machine. The steps edit /etc/freeradius/3.0/ and restart the system service,
so take a VM snapshot first if the machine matters to you.
Ubuntu 24.04 and 26.04 still package FreeRADIUS 3.x, so the paths and the
freerad group should be the same there.
All passwords and secrets here are throwaway lab values. The web UI will listen
on 127.0.0.1:18080 with no login, which is fine on loopback and nowhere else.
Install FreeRADIUS
sudo apt install freeradius freeradius-utils python3-venv
The package starts the freeradius service right away, with its configuration
in /etc/freeradius/3.0/. freeradius-utils gives you radtest.
Install RadiusDeck
RadiusDeck needs Python 3.12 or newer. Ubuntu 22.04 ships 3.10. My first
install attempt there failed with:
ERROR: Package 'radiusdeck' requires a different Python: 3.10.12 not in '>=3.12'
What I did instead: bootstrap uv with the system Python, then let uv create a
separate Python 3.12 environment for RadiusDeck:
git clone --branch v0.1.0 --depth 1 https://github.com/radiusdeck/radiusdeck.git radiusdeck-demo
cd radiusdeck-demo
python3 -m venv .uv-bootstrap
.uv-bootstrap/bin/python -m pip install uv
.uv-bootstrap/bin/uv venv --python 3.12 --seed .venv312
.venv312/bin/python -m pip install .
It looks a bit roundabout. I still prefer it to adding a third-party PPA such as
deadsnakes: Ubuntu's own python3 stays untouched. If 3.12 isn't installed,
uv downloads a managed interpreter just for this environment
(uv docs). The --seed
option puts pip into the new environment.
On Ubuntu 24.04 and later, the system python3 is already 3.12 or newer, so a
plain venv should be enough.
git clone --branch v0.1.0 --depth 1 https://github.com/radiusdeck/radiusdeck.git radiusdeck-demo
cd radiusdeck-demo
python3 -m venv .venv312
.venv312/bin/python -m pip install .
Let your user write clients.conf
sudo usermod -a -G freerad "$USER"
Log out and back in (or reconnect over SSH) so the new group applies, then check:
id
freerad should be in the list. On my machine clients.conf was owned by
freerad:freerad with mode -rw-r-----, so the group could read it but not
save it. One more change fixes that:
sudo chmod g+w /etc/freeradius/3.0/clients.conf
Get a working baseline
Two different credentials are involved. They're easy to mix up:
| Credential | Initial value | Configured in |
|---|---|---|
Password of user bob
|
hello |
mods-config/files/authorize |
Shared secret of client 127.0.0.1
|
testing123 |
clients.conf |
The client is the device that sends RADIUS requests. Here radtest plays that
device. RadiusDeck manages the client, bob stays in FreeRADIUS's own files.
Open the users file. /etc/freeradius/3.0/users is a symlink to it:
sudoedit /etc/freeradius/3.0/mods-config/files/authorize
Ubuntu's copy has a commented-out bob example. Uncomment both lines:
bob Cleartext-Password := "hello"
Reply-Message := "Hello, %{User-Name}"
If the example isn't there, add these lines near the top, above any DEFAULT
entries.
Also check that clients.conf still has the stock localhost client with
testing123. If someone already changed the secret, use the real one below.
Don't add a second client for 127.0.0.1.
In terminal A, validate the configuration:
sudo freeradius -C
If it reports no errors, restart the service and send the first request:
sudo systemctl restart freeradius
radtest bob hello 127.0.0.1 0 testing123
With no port in the server address, radtest sends to UDP 1812. The 0 is the
NAS-Port attribute (radtest manual).
You should see:
Sent Access-Request Id 72 from 0.0.0.0:48371 to 127.0.0.1:1812 length 73
User-Name = "bob"
User-Password = "hello"
NAS-IP-Address = 127.0.1.1
NAS-Port = 0
Cleartext-Password = "hello"
Received Access-Accept Id 72 from 127.0.0.1:1812 to 127.0.0.1:48371 length 50
Message-Authenticator = 0x1858ce772b854342eb35e3c222306682
Reply-Message = "Hello, bob"
Don't move on without Access-Accept.
Point RadiusDeck at the system clients.conf
In terminal B, from the radiusdeck-demo checkout:
APP_RADIUS_CLIENTS_PATH=/etc/freeradius/3.0/clients.conf \
APP_BACKUP_ENABLED=true \
APP_BACKUP_DIR="$PWD/.system-demo-backups" \
.venv312/bin/python -m radiusdeck.cli.main serve \
--host 127.0.0.1 --port 18080
Open http://127.0.0.1:18080 and go to Clients. You're looking at the
server's real clients.conf now. Backups go to .system-demo-backups in the
checkout. RadiusDeck creates that directory with mode 0700 and the files with
0600, which matters, since every backup contains your secrets.
Change the secret, then apply it
Find the client with IP 127.0.0.1, normally named localhost. Click Edit,
enter new_secret in Shared secret, and click Save Changes.
The file has changed. The running server still holds testing123 in memory and
keeps using it until it restarts. That gap is where most "I changed it and
nothing happened" moments come from.
Validate before restarting:
sudo freeradius -C
sudo systemctl restart freeradius
radtest bob hello 127.0.0.1 0 new_secret
You should see:
Received Access-Accept Id 54 from 127.0.0.1:1812 to 127.0.0.1:37652 length 50
Message-Authenticator = 0xeca826a457d5d0280471925dd9b891b3
Reply-Message = "Hello, bob"
bob and hello didn't change. Only the secret between the client and the
server did. The running server accepted the new one.
If something fails
| Symptom | What to check |
|---|---|
requires a different Python: 3.10.12 |
You're installing with the system Python. Use .venv312/bin/python. |
RadiusDeck can't read clients.conf
|
Run id in the terminal that starts RadiusDeck. No freerad means that session started before usermod. |
| The client list loads, but saving fails | Group write is missing on clients.conf. Repeat the chmod g+w. |
| The page doesn't open | RadiusDeck listens on loopback only. Open the URL on the same machine and keep terminal B running. |
new_secret gets no answer |
Restart after saving, then make sure you edited the client with IP 127.0.0.1. |
If the service won't come back after a restart, its journal is the first place
to look. I didn't need it in my run:
sudo journalctl -u freeradius -n 50 --no-pager
Cleaning up
Stop RadiusDeck with Ctrl+C in terminal B. FreeRADIUS keeps running with
bob and new_secret, so remove the test user and set a real secret before the
machine does anything else.
If an edit goes wrong, RadiusDeck saved the previous clients.conf before
writing. Backups → Restore latest puts it back. I didn't test the restore
here.
On a real access point or VPN gateway, the device needs the new secret too.
Until both sides match, nobody behind that device can authenticate, so pick a
quiet moment for the switch.
Top comments (0)