Generate a RADIUS client registration once, correctly, for both server
families: a FreeRADIUS 3.x clients.conf block and the matching
Windows NPS New-NpsRadiusClient PowerShell — from one set of inputs,
with a shared secret generated by crypto/rand.
Built for the moment you're staring at
Ignoring request from unknown client in a FreeRADIUS debug log, or adding
a RADIUS client in NPS and wondering what a good shared secret looks like.
Everything runs locally. The CLI is a single static binary; the browser version on authhound.com is this exact Go core compiled to WebAssembly. No server round-trips, no accounts, no telemetry — the secret is generated on your machine and never leaves it.
Download a signed release archive for Linux, macOS, or Windows:
curl -LO https://github.com/authhound/radius-client-config/releases/latest/download/radius-client-config_linux_amd64.tar.gz
tar xzf radius-client-config_linux_amd64.tar.gz
./radius-client-config versionOr build from source:
go install github.com/authhound/radius-client-config/cmd/radius-client-config@latestGenerate both outputs with a fresh 32-character secret:
radius-client-config all --name probe01 --ip 10.0.4.20Just the FreeRADIUS block, for a whole management subnet:
radius-client-config freeradius --name mgmt-net --ip 10.0.4.0/24Just the NPS PowerShell, hardened secret length, no symbols (for NAS gear that rejects punctuation):
radius-client-config nps --name wlc01 --ip 10.0.9.5 --length 48 --charset alnumBring your own secret — never on the command line (argv leaks into shell
history and ps output). From a chmod 600 file, from stdin, or from an
interactive no-echo prompt:
radius-client-config all --name probe01 --ip 10.0.4.20 --secret-file /root/.radius-secret
umask 077 && openssl rand -base64 24 | radius-client-config all --name probe01 --ip 10.0.4.20 --secret-stdin
radius-client-config all --name probe01 --ip 10.0.4.20 --prompt-secretMachine-readable output (versioned schema, see docs/json-schema.md):
radius-client-config all --name probe01 --ip 10.0.4.20 --json# FreeRADIUS 3.x client block — generated by radius-client-config (runs locally; nothing is transmitted)
# Add to clients.conf:
# Debian/Ubuntu: /etc/freeradius/3.0/clients.conf
# RHEL/Rocky: /etc/raddb/clients.conf
client probe01 {
ipaddr = 10.0.4.20
secret = "kFwzheGDtqfvSw~^_A+Gr,heNOaP.dpe"
# Require Message-Authenticator on every request from this client
# (BlastRADIUS hardening). Requires FreeRADIUS 3.2.5+ / 3.0.27+;
# remove this line on older versions.
require_message_authenticator = yes
}
# Check syntax, then reload:
# Debian/Ubuntu: freeradius -XC && systemctl reload freeradius
# RHEL/Rocky: radiusd -XC && systemctl reload radiusd
# Secret: 32 chars from a 76-symbol set ≈ 199 bits of entropy.
# Note: if NAT sits between the client and the server, the server sees a
# different source IP — register the post-NAT address instead.
The NPS output registers the same client with the same secret via
New-NpsRadiusClient, including the service restart NPS needs before the
client is live, and the nps.msc console path for admins who don't use
PowerShell.
| Flag | Default | |
|---|---|---|
--name |
— | client name (clients.conf label / NPS -Name) |
--ip |
— | IPv4/IPv6 address or CIDR |
--secret-file / --secret-stdin / --prompt-secret |
generate | mutually exclusive secret sources |
--length |
32 | generated-secret length (min 16, max 128 — the NPS limit) |
--charset |
alnum-symbols |
or alnum; symbols are chosen to never need quoting/escaping |
--nas-type |
unset | FreeRADIUS nas_type (no NPS equivalent) |
--require-message-authenticator |
true |
BlastRADIUS hardening line in both outputs |
--coa-note |
off | note that CoA/Disconnect uses a separate port (typically UDP 3799) |
--json |
off | versioned JSON document with both outputs |
Exit codes: 0 ok · 1 runtime failure · 2 usage error.
The same core compiles to WASM (~3.6 MB raw, ~1.0 MB gzipped) with a one-function JS API:
const out = JSON.parse(generateClientConfig(JSON.stringify(request)));
// {ok: true, result: {...}} or {ok: false, error: "..."}Request/response shapes are in docs/json-schema.md.
To try it: ./wasm/build.sh && python3 -m http.server -d wasm 8080, then
open http://localhost:8080/test.html. Secret generation in the browser
uses the Web Crypto CSPRNG (crypto.getRandomValues) via Go's
crypto/rand; the test page refuses to run without it.
- The core library (
clientconfig/) is pure computation — a test fails if it ever importsnet,net/http,os,os/exec, orsyscall. - Secrets are drawn from
crypto/randwith rejection sampling (no modulo bias) and are never accepted as command-line arguments, logged, or echoed. - Output honesty: syntax matches currently-shipping FreeRADIUS 3.x and the
documented
New-NpsRadiusClientsignature; where versions differ (require_message_authenticatorneeds 3.2.5+/3.0.27+, NPS CIDR ranges need Windows Server 2016+), the generated output says so in a comment instead of guessing.
Releases ship SHA-256 checksums, per-archive SPDX SBOMs, and a keyless
cosign signature over checksums.txt:
cosign verify-blob \
--certificate checksums.txt.pem --signature checksums.txt.sig \
--certificate-identity-regexp 'https://github.com/authhound/radius-client-config' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checksums.txt
sha256sum -c checksums.txt --ignore-missingPrefer not to install anything? The same core, compiled to WASM, runs at authhound.com/tools/radius-client-config — generation stays in your browser; nothing is uploaded.
Registered the client but authentication still fails? Test the server from inside your network with authhound-probe — registering a RADIUS client for the probe host is exactly what this tool generates. Got a failing log instead? Paste it into the free RADIUS log analyzer for a plain-English diagnosis. To work out why FreeRADIUS is ignoring a client, see the walkthrough at authhound.com/kb/freeradius-unknown-client.
Apache-2.0. See LICENSE.