FreeRADIUS
--- freeradius-original-no-comments/3.0/radiusd.conf
# correct_escapes = true may or may not be needed
- auth = no
+ auth = yes
auth_badpass = no
auth_goodpass = no
+ msg_goodpass = "msg_goodpass: nas: %{NAS-IP-Address}"
+ msg_badpass = "msg_badpass: nas: %{NAS-IP-Address}"
--- freeradius-original-no-comments/3.0/users
DEFAULT Hint == "SLIP"
Framed-Protocol = SLIP
+DEFAULT
+ Message-Authenticator = 0
--- freeradius-original-no-comments/3.0/mods-config/files/authorize
Framed-Compression = Van-Jacobson-TCP-IP
DEFAULT Hint == "SLIP"
Framed-Protocol = SLIP
+DEFAULT
+ Message-Authenticator = 0
--- freeradius-original-no-comments/3.0/sites-enabled/inner-tunnel
# change all -sql to sql
Message-Authenticator and Mikrotik
As of RouterOS version 7.15 changelog, Mikrotik introduced the following two changes:
*) radius - added "require-message-auth" option that requires "Message-Authenticator" in received Access-Accept/Challenge/Reject messages; *) radius - include "Message-Authenticator" in any RADIUS communication messages besides accounting for all services;
When you upgrade from a previous version, currently Mikrotik sets the require-message-auth=yes instead of no. This means that if you're running FreeRADIUS, most likely you won't be able to login to your routers anymore using RADIUS authentciation. I hope you know what the local credentials are!
It's taken much too long to learn how to get FreeRADIUS to add the Message-Authenticator attribute in response messages.
# Ubuntu 22.04 - /etc/freeradius/3.0/mods-config/files/authorize
# Add the following to the end of the file, and make sure you're not breaking anything else in the process
DEFAULT
Message-Authenticator = 0
Using environment variables with Docker
This is an example of using the string String Expansion Alternation syntax with environment variables. If the environment variable doesn't exist, it will return the value sqlite instead.
sql {
dialect = "%{$ENV{RADIUSD_SQL_DIALECT}:-sqlite}"
driver = "rlm_sql_${dialect}"
Here's another example:
# a bunch of random examples so you get the idea
type = %{$ENV{RADIUSD_SERVER_DEFAULT_TYPE}:-auth}
private_key_password = %{$ENV{RADIUSD_MOD_EAP_TLS_PRIVATE_KEY_PASSWORD}:-YouReallyNeedThisEnvVariable}
private_key_file = %{$ENV{RADIUSD_MOD_EAP_TLS_PRIVATE_KEY_FILE}:-/opt/freeradius/certs/server.key}
certificate_file = %{$ENV{RADIUSD_MOD_EAP_TLS_CERTIFICATE_FILE}:-/opt/freeradius/certs/server.crt}
tls_min_version = "%{$ENV{RADIUSD_MOD_EAP_TLS_MIN_VERSION}:-1.2}"
tls_max_version = "%{$ENV{RADIUSD_MOD_EAP_TLS_MAX_VERSION}:-1.2}"
enable = %{$ENV{RADIUSD_MOD_EAP_TLS_OCSP_ENABLE}:-no}
override_cert_url = %{$ENV{RADIUSD_MOD_EAP_TLS_OCSP_OVERRIDE_CERT_URL}:-no}
Configs
Environment setup
mkdir -p /opt/freeradius/certs
mkdir -p /opt/freeradius/certs/trusted
mkdir -p /opt/freeradius/db
mkdir -p /opt/freeradius/tmp
chown -R freerad:freerad /opt/freeradius
find /opt/freeradius -type d -exec chmod 770 {} \;
All files that need updating
radiusd.conf
clients.conf
users
mods-config/files/authorize
mods-enabled/linelog
mods-enabled/python3
mods-enabled/sql
mods-enabled/eap
sites-enabled/default
sites-enabled/inner-tunnel
Base changes
--- freeradius-original-no-comments/3.0/radiusd.conf
# correct_escapes = true may or may not be needed
- auth = no
+ auth = yes
auth_badpass = no
auth_goodpass = no
+ msg_goodpass = "msg_goodpass: nas: %{NAS-IP-Address}"
+ msg_badpass = "msg_badpass: nas: %{NAS-IP-Address}"
--- freeradius-original-no-comments/3.0/users
DEFAULT Hint == "SLIP"
Framed-Protocol = SLIP
+DEFAULT
+ Message-Authenticator = 0
--- freeradius-original-no-comments/3.0/mods-config/files/authorize
Framed-Compression = Van-Jacobson-TCP-IP
DEFAULT Hint == "SLIP"
Framed-Protocol = SLIP
+DEFAULT
+ Message-Authenticator = 0
--- freeradius-original-no-comments/3.0/sites-enabled/inner-tunnel
# change all -sql to sql
--- freeradius-original-no-comments/3.0/sites-enabled/default
# change all -sql to sql, and a number of other items as needed
#
@@ -1,6 +1,6 @@
server default {
listen {
- type = auth
+ type = auth+acct
ipaddr = *
port = 0
limit {
@@ -34,8 +34,10 @@
}
}
authorize {
+ linelog_recv_request
filter_username
preprocess
+ auth_log
chap
mschap
digest
@@ -44,7 +46,7 @@
ok = return
}
files
- -sql
+ sql
-ldap
expiration
logintime
@@ -75,12 +77,12 @@
}
accounting {
detail
- unix
- -sql
+ sql
exec
attr_filter.accounting_response
}
session {
+ sql
}
post-auth {
if (session-state:User-Name && reply:User-Name && request:User-Name && (reply:User-Name == request:User-Name)) {
@@ -91,12 +93,16 @@
update {
&reply: += &session-state:
}
- -sql
+ sql_session_start
+ reply_log
+ linelog_send_accept
+ sql
exec
remove_reply_message_if_eap
Post-Auth-Type REJECT {
- -sql
+ sql
attr_filter.access_reject
+ linelog_send_reject
eap
remove_reply_message_if_eap
}
@@ -111,8 +117,12 @@
}
}
pre-proxy {
+ linelog_send_proxy_request
+ pre_proxy_log
}
post-proxy {
+ linelog_recv_proxy_response
+ post_proxy_log
eap
}
}
# file: mods-available/linelog
#
# Added based on this URL:
# https://wiki.freeradius.org/guide/eduroam
linelog linelog_recv_request {
filename = syslog
syslog_facility = local0
syslog_severity = debug
format = '{"action":"Recv-Request", "User-Name":"%{User-Name}", "Called-Station-Id":"%{Called-Station-Id}", "EAP-Type":"%{EAP-Type}", "NAS-IP-Address":"%{NAS-IP-Address}", "Calling-Station-Id":"%{Calling-Station-Id}", "Message-Authenticator":"%{Message-Authenticator}", "NAS-Port-Type":"%{NAS-Port-Type}", "NAS-Port-Id":"%{NAS-Port-Id}", "TLS-Client-Cert-Subject-Alt-Name-Dns":"%{TLS-Client-Cert-Subject-Alt-Name-Dns}", "TLS-Client-Cert-Serial":"%{TLS-Client-Cert-Serial}", "TLS-Client-Cert-Issuer":"%{TLS-Client-Cert-Issuer}"}'
}
linelog linelog_send_accept {
filename = syslog
syslog_facility = local0
syslog_severity = info
format = '{"action":"Send-Accept", "User-Name":"%{User-Name}", "Called-Station-Id":"%{Called-Station-Id}", "EAP-Type":"%{EAP-Type}", "NAS-IP-Address":"%{NAS-IP-Address}", "Calling-Station-Id":"%{Calling-Station-Id}", "Message-Authenticator":"%{Message-Authenticator}", "NAS-Port-Type":"%{NAS-Port-Type}", "NAS-Port-Id":"%{NAS-Port-Id}", "TLS-Client-Cert-Subject-Alt-Name-Dns":"%{TLS-Client-Cert-Subject-Alt-Name-Dns}", "TLS-Client-Cert-Serial":"%{TLS-Client-Cert-Serial}", "TLS-Client-Cert-Issuer":"%{TLS-Client-Cert-Issuer}"}'
}
linelog linelog_send_reject {
filename = syslog
syslog_facility = local0
syslog_severity = error
format = '{"action":"Send-Reject", "User-Name":"%{User-Name}", "Called-Station-Id":"%{Called-Station-Id}", "EAP-Type":"%{EAP-Type}", "NAS-IP-Address":"%{NAS-IP-Address}", "Calling-Station-Id":"%{Calling-Station-Id}", "Message-Authenticator":"%{Message-Authenticator}", "NAS-Port-Type":"%{NAS-Port-Type}", "NAS-Port-Id":"%{NAS-Port-Id}", "TLS-Client-Cert-Subject-Alt-Name-Dns":"%{TLS-Client-Cert-Subject-Alt-Name-Dns}", "TLS-Client-Cert-Serial":"%{TLS-Client-Cert-Serial}", "TLS-Client-Cert-Issuer":"%{TLS-Client-Cert-Issuer}"}'
}
linelog linelog_send_proxy_request {
filename = syslog
syslog_facility = local0
syslog_severity = debug
format = "action = Send-Proxy-Request, %{pairs:proxy-request:}"
}
linelog linelog_recv_proxy_response {
filename = syslog syslog_facility = local0 syslog_severity = debug
reference = "messages.%{proxy-reply:Response-Packet-Type}"
messages {
Access-Accept = "action = Recv-Proxy-Accept, User-Name = %{User-Name}, Calling-Station-Id = %{Calling-Station-Id}, %{pairs:proxy-reply:}"
Access-Reject = "action = Recv-Proxy-Reject, User-Name = %{User-Name}, Calling-Station-Id = %{Calling-Station-Id}, %{pairs:proxy-reply:}"
Access-Challenge = "action = Recv-Proxy-Challenge, User-Name = %{User-Name}, Calling-Station-ID = %{Calling-Station-Id}, %{pairs:proxy-reply:}"
}
}
Solution specific changes
EAP-TLS
# file: mods-enabled/eap
eap {
default_eap_type = tls
timer_expire = 60
ignore_unknown_eap_types = no
cisco_accounting_username_bug = no
max_sessions = ${max_requests}
tls-config tls-common {
private_key_file = /opt/freeradius/certs/freerad1.domain.com.key
certificate_file = /opt/freeradius/certs/freerad1.domain.com.crt
ca_file = /opt/freeradius/certs/root-ca-certificate.crt
check_crl = no
check_all_crl = no
ca_path = /opt/freeradius/certs/trusted
ca_path_reload_interval = 3600
allow_expired_crl = no
cipher_list = "DEFAULT"
cipher_server_preference = yes
tls_min_version = "1.2"
tls_max_version = "1.2"
ecdh_curve = ""
cache {
enable = no
lifetime = 24 # hours
store {
Tunnel-Private-Group-Id
}
}
verify {
}
ocsp {
enable = no
override_cert_url = no
use_nonce = yes
}
}
tls {
tls = tls-common
}
}
SQLite
# file: mods-enabled/sql
# note: all other dialect references have been removed
sql {
dialect = "sqlite"
driver = "rlm_sql_${dialect}"
sqlite {
filename = "/opt/freeradius/db/freeradius.sqlite3"
busy_timeout = 200
bootstrap = "${modconfdir}/${..:name}/main/sqlite/schema.sql"
}
radius_db = "radius"
acct_table1 = "radacct"
acct_table2 = "radacct"
postauth_table = "radpostauth"
authcheck_table = "radcheck"
groupcheck_table = "radgroupcheck"
authreply_table = "radreply"
groupreply_table = "radgroupreply"
usergroup_table = "radusergroup"
delete_stale_sessions = yes
pool {
start = ${thread[pool].start_servers}
min = ${thread[pool].min_spare_servers}
max = ${thread[pool].max_servers}
spare = ${thread[pool].max_spare_servers}
uses = 0
retry_delay = 30
lifetime = 0
idle_timeout = 60
}
client_table = "nas"
group_attribute = "SQL-Group"
$INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
}
Original Configs
Just for reference... from a system running Ubuntu 24.04.3 freeradius 3.2.5+dfsg-3~ubuntu24.04.3
freeradius/3.0
# file: radiusd.conf
prefix = /usr
exec_prefix = /usr
sysconfdir = /etc
localstatedir = /var
sbindir = ${exec_prefix}/sbin
logdir = /var/log/freeradius
raddbdir = /etc/freeradius/3.0
radacctdir = ${logdir}/radacct
name = freeradius
confdir = ${raddbdir}
modconfdir = ${confdir}/mods-config
certdir = ${confdir}/certs
cadir = ${confdir}/certs
run_dir = ${localstatedir}/run/${name}
db_dir = ${raddbdir}
libdir = /usr/lib/freeradius
pidfile = ${run_dir}/${name}.pid
max_request_time = 30
cleanup_delay = 5
max_requests = 16384
hostname_lookups = no
log {
destination = files
colourise = yes
file = ${logdir}/radius.log
syslog_facility = daemon
stripped_names = no
auth = no
auth_badpass = no
auth_goodpass = no
msg_denied = "You are already logged in - access denied"
}
checkrad = ${sbindir}/checkrad
ENV {
}
security {
user = freerad
group = freerad
allow_core_dumps = no
max_attributes = 200
reject_delay = 1
status_server = yes
require_message_authenticator = auto
limit_proxy_state = auto
}
proxy_requests = yes
$INCLUDE proxy.conf
$INCLUDE clients.conf
thread pool {
start_servers = 5
max_servers = 32
min_spare_servers = 3
max_spare_servers = 10
max_requests_per_server = 0
auto_limit_acct = no
}
modules {
$INCLUDE mods-enabled/
}
instantiate {
}
policy {
$INCLUDE policy.d/
}
$INCLUDE sites-enabled/
# file: clients.conf
client localhost {
ipaddr = 127.0.0.1
proto = *
secret = testing123
nas_type = other # localhost isn't usually a NAS...
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
client localhost_ipv6 {
ipv6addr = ::1
secret = testing123
}
# file: proxy.conf
proxy server {
default_fallback = no
}
home_server localhost {
type = auth
ipaddr = 127.0.0.1
port = 1812
secret = testing123
response_window = 20
zombie_period = 40
revive_interval = 120
status_check = status-server
check_interval = 30
check_timeout = 4
num_answers_to_alive = 3
max_outstanding = 65536
coa {
irt = 2
mrt = 16
mrc = 5
mrd = 30
}
limit {
max_connections = 16
max_requests = 0
lifetime = 0
idle_timeout = 0
}
}
home_server_pool my_auth_failover {
type = fail-over
home_server = localhost
}
realm example.com {
auth_pool = my_auth_failover
}
realm LOCAL {
}
freeradius/3.0/mods-available
# file: mods-available/eap
eap {
default_eap_type = md5
timer_expire = 60
ignore_unknown_eap_types = no
cisco_accounting_username_bug = no
max_sessions = ${max_requests}
md5 {
}
gtc {
auth_type = PAP
}
tls-config tls-common {
private_key_password = whatever
private_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
certificate_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
ca_file = /etc/ssl/certs/ca-certificates.crt
ca_path = ${cadir}
cipher_list = "DEFAULT"
cipher_server_preference = no
tls_min_version = "1.2"
tls_max_version = "1.2"
ecdh_curve = ""
cache {
enable = no
lifetime = 24 # hours
store {
Tunnel-Private-Group-Id
}
}
verify {
}
ocsp {
enable = no
override_cert_url = yes
url = "http://127.0.0.1/ocsp/"
}
}
tls {
tls = tls-common
}
ttls {
tls = tls-common
default_eap_type = md5
copy_request_to_tunnel = no
use_tunneled_reply = no
virtual_server = "inner-tunnel"
}
peap {
tls = tls-common
default_eap_type = mschapv2
copy_request_to_tunnel = no
use_tunneled_reply = no
virtual_server = "inner-tunnel"
}
mschapv2 {
}
}
freeradius/3.0/sites-available
# file: sites-available/default
server default {
listen {
type = auth
ipaddr = *
port = 0
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
listen {
ipaddr = *
port = 0
type = acct
limit {
}
}
listen {
type = auth
ipv6addr = :: # any. ::1 == localhost
port = 0
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
listen {
ipv6addr = ::
port = 0
type = acct
limit {
}
}
authorize {
filter_username
preprocess
chap
mschap
digest
suffix
eap {
ok = return
}
files
-sql
-ldap
expiration
logintime
pap
Autz-Type New-TLS-Connection {
ok
}
}
authenticate {
Auth-Type PAP {
pap
}
Auth-Type CHAP {
chap
}
Auth-Type MS-CHAP {
mschap
}
mschap
digest
eap
}
preacct {
preprocess
acct_unique
suffix
files
}
accounting {
detail
unix
-sql
exec
attr_filter.accounting_response
}
session {
}
post-auth {
if (session-state:User-Name && reply:User-Name && request:User-Name && (reply:User-Name == request:User-Name)) {
update reply {
&User-Name !* ANY
}
}
update {
&reply: += &session-state:
}
-sql
exec
remove_reply_message_if_eap
Post-Auth-Type REJECT {
-sql
attr_filter.access_reject
eap
remove_reply_message_if_eap
}
Post-Auth-Type Challenge {
}
Post-Auth-Type Client-Lost {
}
if (EAP-Key-Name && &reply:EAP-Session-Id) {
update reply {
&EAP-Key-Name := &reply:EAP-Session-Id
}
}
}
pre-proxy {
}
post-proxy {
eap
}
}
# file: sites-available/inner-tunnel
server inner-tunnel {
listen {
ipaddr = 127.0.0.1
port = 18120
type = auth
}
authorize {
filter_username
chap
mschap
suffix
update control {
&Proxy-To-Realm := LOCAL
}
eap {
ok = return
}
files
-sql
-ldap
expiration
logintime
pap
}
authenticate {
Auth-Type PAP {
pap
}
Auth-Type CHAP {
chap
}
Auth-Type MS-CHAP {
mschap
}
mschap
eap
}
session {
radutmp
}
post-auth {
-sql
if (0) {
update reply {
User-Name !* ANY
Message-Authenticator !* ANY
EAP-Message !* ANY
Proxy-State !* ANY
MS-MPPE-Encryption-Types !* ANY
MS-MPPE-Encryption-Policy !* ANY
MS-MPPE-Send-Key !* ANY
MS-MPPE-Recv-Key !* ANY
}
update {
&outer.session-state: += &reply:
}
}
Post-Auth-Type REJECT {
-sql
attr_filter.access_reject
update outer.session-state {
&Module-Failure-Message := &request:Module-Failure-Message
}
}
}
pre-proxy {
}
post-proxy {
eap
}
} # inner-tunnel server block
# file: sites-available/tls
listen {
ipaddr = *
port = 2083
type = auth+acct
proto = tcp
virtual_server = default
clients = radsec
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
tls {
private_key_password = whatever
private_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
certificate_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
ca_file = /etc/ssl/certs/ca-certificates.crt
fragment_size = 8192
ca_path = ${cadir}
ca_path_reload_interval = 3600
cipher_list = "DEFAULT"
cipher_server_preference = no
tls_min_version = "1.2"
tls_max_version = "1.3"
ecdh_curve = ""
cache {
enable = no
lifetime = 24 # hours
}
require_client_cert = yes
verify {
}
}
}
clients radsec {
client 127.0.0.1 {
ipaddr = 127.0.0.1
proto = tls
secret = radsec
}
}
home_server tls {
ipaddr = 127.0.0.1
port = 2083
type = auth
secret = radsec
proto = tcp
status_check = none
tls {
private_key_password = whatever
private_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
certificate_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
ca_file = /etc/ssl/certs/ca-certificates.crt
fragment_size = 8192
ca_path = ${cadir}
cipher_list = "DEFAULT"
connect_timeout = 30
}
}
home_server_pool tls {
type = fail-over
home_server = tls
}
realm tls {
auth_pool = tls
}
-end