<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrew</title>
    <description>The latest articles on DEV Community by Andrew (@shipcode25).</description>
    <link>https://dev.to/shipcode25</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4188560%2F717e3c3b-61af-4561-9abf-1a86fa6b0b97.png</url>
      <title>DEV Community: Andrew</title>
      <link>https://dev.to/shipcode25</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shipcode25"/>
    <language>en</language>
    <item>
      <title>Changing a FreeRADIUS Client Secret on Ubuntu and Verifying It with radtest</title>
      <dc:creator>Andrew</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:43:57 +0000</pubDate>
      <link>https://dev.to/shipcode25/changing-a-freeradius-client-secret-on-ubuntu-and-verifying-it-with-radtest-2d51</link>
      <guid>https://dev.to/shipcode25/changing-a-freeradius-client-secret-on-ubuntu-and-verifying-it-with-radtest-2d51</guid>
      <description>&lt;p&gt;Ubuntu's FreeRADIUS package ships with a &lt;code&gt;localhost&lt;/code&gt; client whose shared secret&lt;br&gt;
is &lt;code&gt;testing123&lt;/code&gt;. Here you'll change that secret, restart the server, and confirm&lt;br&gt;
with &lt;code&gt;radtest&lt;/code&gt; that FreeRADIUS really uses the new value.&lt;/p&gt;

&lt;p&gt;For the editing part I'll use RadiusDeck, a small open-source UI that edits&lt;br&gt;
&lt;code&gt;clients.conf&lt;/code&gt; directly, with no SQL database involved. If you also need user management or accounting,&lt;br&gt;
&lt;a href="https://github.com/lirantal/daloradius" rel="noopener noreferrer"&gt;daloRADIUS&lt;/a&gt; covers that.&lt;/p&gt;

&lt;p&gt;I tested everything below on Ubuntu 22.04.2 with the packaged FreeRADIUS 3.0.26,&lt;br&gt;
RadiusDeck 0.1.0 and Python 3.12.15. The &lt;code&gt;radtest&lt;/code&gt; output is from that run.&lt;/p&gt;
&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;p&gt;You'll need a test Ubuntu machine with sudo, Git, and a browser on the same&lt;br&gt;
machine. The steps edit &lt;code&gt;/etc/freeradius/3.0/&lt;/code&gt; and restart the system service,&lt;br&gt;
so take a VM snapshot first if the machine matters to you.&lt;/p&gt;

&lt;p&gt;Ubuntu 24.04 and 26.04 still package FreeRADIUS 3.x, so the paths and the&lt;br&gt;
&lt;code&gt;freerad&lt;/code&gt; group should be the same there.&lt;/p&gt;

&lt;p&gt;All passwords and secrets here are throwaway lab values. The web UI will listen&lt;br&gt;
on &lt;code&gt;127.0.0.1:18080&lt;/code&gt; with no login, which is fine on loopback and nowhere else.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install FreeRADIUS
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;freeradius freeradius-utils python3-venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The package starts the &lt;code&gt;freeradius&lt;/code&gt; service right away, with its configuration&lt;br&gt;
in &lt;code&gt;/etc/freeradius/3.0/&lt;/code&gt;. &lt;code&gt;freeradius-utils&lt;/code&gt; gives you &lt;code&gt;radtest&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install RadiusDeck
&lt;/h2&gt;

&lt;p&gt;RadiusDeck needs Python 3.12 or newer. Ubuntu 22.04 ships 3.10. My first&lt;br&gt;
install attempt there failed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: Package 'radiusdeck' requires a different Python: 3.10.12 not in '&amp;gt;=3.12'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I did instead: bootstrap &lt;code&gt;uv&lt;/code&gt; with the system Python, then let &lt;code&gt;uv&lt;/code&gt; create a&lt;br&gt;
separate Python 3.12 environment for RadiusDeck:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; v0.1.0 &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/radiusdeck/radiusdeck.git radiusdeck-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;radiusdeck-demo
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .uv-bootstrap
.uv-bootstrap/bin/python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;uv
.uv-bootstrap/bin/uv venv &lt;span class="nt"&gt;--python&lt;/span&gt; 3.12 &lt;span class="nt"&gt;--seed&lt;/span&gt; .venv312
.venv312/bin/python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks a bit roundabout. I still prefer it to adding a third-party PPA such as&lt;br&gt;
deadsnakes: Ubuntu's own &lt;code&gt;python3&lt;/code&gt; stays untouched. If 3.12 isn't installed,&lt;br&gt;
&lt;code&gt;uv&lt;/code&gt; downloads a managed interpreter just for this environment&lt;br&gt;
(&lt;a href="https://docs.astral.sh/uv/guides/install-python/" rel="noopener noreferrer"&gt;uv docs&lt;/a&gt;). The &lt;code&gt;--seed&lt;/code&gt;&lt;br&gt;
option puts pip into the new environment.&lt;/p&gt;

&lt;p&gt;On Ubuntu 24.04 and later, the system &lt;code&gt;python3&lt;/code&gt; is already 3.12 or newer, so a&lt;br&gt;
plain venv should be enough.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; v0.1.0 &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/radiusdeck/radiusdeck.git radiusdeck-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;radiusdeck-demo
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv312
.venv312/bin/python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let your user write clients.conf
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; freerad &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log out and back in (or reconnect over SSH) so the new group applies, then check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;freerad&lt;/code&gt; should be in the list. On my machine &lt;code&gt;clients.conf&lt;/code&gt; was owned by&lt;br&gt;
&lt;code&gt;freerad:freerad&lt;/code&gt; with mode &lt;code&gt;-rw-r-----&lt;/code&gt;, so the group could read it but not&lt;br&gt;
save it. One more change fixes that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;g+w /etc/freeradius/3.0/clients.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Get a working baseline
&lt;/h2&gt;

&lt;p&gt;Two different credentials are involved. They're easy to mix up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Credential&lt;/th&gt;
&lt;th&gt;Initial value&lt;/th&gt;
&lt;th&gt;Configured in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Password of user &lt;code&gt;bob&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hello&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mods-config/files/authorize&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared secret of client &lt;code&gt;127.0.0.1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;testing123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clients.conf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The client is the device that sends RADIUS requests. Here &lt;code&gt;radtest&lt;/code&gt; plays that&lt;br&gt;
device. RadiusDeck manages the client, &lt;code&gt;bob&lt;/code&gt; stays in FreeRADIUS's own files.&lt;/p&gt;

&lt;p&gt;Open the users file. &lt;code&gt;/etc/freeradius/3.0/users&lt;/code&gt; is a symlink to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sudoedit /etc/freeradius/3.0/mods-config/files/authorize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ubuntu's copy has a commented-out &lt;code&gt;bob&lt;/code&gt; example. Uncomment both lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bob Cleartext-Password := "hello"
    Reply-Message := "Hello, %{User-Name}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the example isn't there, add these lines near the top, above any &lt;code&gt;DEFAULT&lt;/code&gt;&lt;br&gt;
entries.&lt;/p&gt;

&lt;p&gt;Also check that &lt;code&gt;clients.conf&lt;/code&gt; still has the stock &lt;code&gt;localhost&lt;/code&gt; client with&lt;br&gt;
&lt;code&gt;testing123&lt;/code&gt;. If someone already changed the secret, use the real one below.&lt;br&gt;
Don't add a second client for &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In terminal A, validate the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;freeradius &lt;span class="nt"&gt;-C&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it reports no errors, restart the service and send the first request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart freeradius
radtest bob hello 127.0.0.1 0 testing123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With no port in the server address, &lt;code&gt;radtest&lt;/code&gt; sends to UDP 1812. The &lt;code&gt;0&lt;/code&gt; is the&lt;br&gt;
NAS-Port attribute (&lt;a href="https://www.freeradius.org/radiusd/man/radtest.html" rel="noopener noreferrer"&gt;radtest manual&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't move on without &lt;code&gt;Access-Accept&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Point RadiusDeck at the system clients.conf
&lt;/h2&gt;

&lt;p&gt;In terminal B, from the &lt;code&gt;radiusdeck-demo&lt;/code&gt; checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;APP_RADIUS_CLIENTS_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/freeradius/3.0/clients.conf &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;APP_BACKUP_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;APP_BACKUP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/.system-demo-backups"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
.venv312/bin/python &lt;span class="nt"&gt;-m&lt;/span&gt; radiusdeck.cli.main serve &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--host&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;--port&lt;/span&gt; 18080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://127.0.0.1:18080" rel="noopener noreferrer"&gt;http://127.0.0.1:18080&lt;/a&gt; and go to &lt;strong&gt;Clients&lt;/strong&gt;. You're looking at the&lt;br&gt;
server's real &lt;code&gt;clients.conf&lt;/code&gt; now. Backups go to &lt;code&gt;.system-demo-backups&lt;/code&gt; in the&lt;br&gt;
checkout. RadiusDeck creates that directory with mode &lt;code&gt;0700&lt;/code&gt; and the files with&lt;br&gt;
&lt;code&gt;0600&lt;/code&gt;, which matters, since every backup contains your secrets.&lt;/p&gt;
&lt;h2&gt;
  
  
  Change the secret, then apply it
&lt;/h2&gt;

&lt;p&gt;Find the client with IP &lt;code&gt;127.0.0.1&lt;/code&gt;, normally named &lt;code&gt;localhost&lt;/code&gt;. Click &lt;strong&gt;Edit&lt;/strong&gt;,&lt;br&gt;
enter &lt;code&gt;new_secret&lt;/code&gt; in &lt;strong&gt;Shared secret&lt;/strong&gt;, and click &lt;strong&gt;Save Changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The file has changed. The running server still holds &lt;code&gt;testing123&lt;/code&gt; in memory and&lt;br&gt;
keeps using it until it restarts. That gap is where most "I changed it and&lt;br&gt;
nothing happened" moments come from.&lt;/p&gt;

&lt;p&gt;Validate before restarting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;freeradius &lt;span class="nt"&gt;-C&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart freeradius
radtest bob hello 127.0.0.1 0 new_secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;bob&lt;/code&gt; and &lt;code&gt;hello&lt;/code&gt; didn't change. Only the secret between the client and the&lt;br&gt;
server did. The running server accepted the new one.&lt;/p&gt;
&lt;h2&gt;
  
  
  If something fails
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;requires a different Python: 3.10.12&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You're installing with the system Python. Use &lt;code&gt;.venv312/bin/python&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RadiusDeck can't read &lt;code&gt;clients.conf&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;id&lt;/code&gt; in the terminal that starts RadiusDeck. No &lt;code&gt;freerad&lt;/code&gt; means that session started before &lt;code&gt;usermod&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The client list loads, but saving fails&lt;/td&gt;
&lt;td&gt;Group write is missing on &lt;code&gt;clients.conf&lt;/code&gt;. Repeat the &lt;code&gt;chmod g+w&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The page doesn't open&lt;/td&gt;
&lt;td&gt;RadiusDeck listens on loopback only. Open the URL on the same machine and keep terminal B running.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;new_secret&lt;/code&gt; gets no answer&lt;/td&gt;
&lt;td&gt;Restart after saving, then make sure you edited the client with IP &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the service won't come back after a restart, its journal is the first place&lt;br&gt;
to look. I didn't need it in my run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; freeradius &lt;span class="nt"&gt;-n&lt;/span&gt; 50 &lt;span class="nt"&gt;--no-pager&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleaning up
&lt;/h2&gt;

&lt;p&gt;Stop RadiusDeck with &lt;strong&gt;Ctrl+C&lt;/strong&gt; in terminal B. FreeRADIUS keeps running with&lt;br&gt;
&lt;code&gt;bob&lt;/code&gt; and &lt;code&gt;new_secret&lt;/code&gt;, so remove the test user and set a real secret before the&lt;br&gt;
machine does anything else.&lt;/p&gt;

&lt;p&gt;If an edit goes wrong, RadiusDeck saved the previous &lt;code&gt;clients.conf&lt;/code&gt; before&lt;br&gt;
writing. &lt;strong&gt;Backups → Restore latest&lt;/strong&gt; puts it back. I didn't test the restore&lt;br&gt;
here.&lt;/p&gt;

&lt;p&gt;On a real access point or VPN gateway, the device needs the new secret too.&lt;br&gt;
Until both sides match, nobody behind that device can authenticate, so pick a&lt;br&gt;
quiet moment for the switch.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>networking</category>
      <category>security</category>
      <category>freeradius</category>
    </item>
  </channel>
</rss>
