Skip to main content

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