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
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