Changing IP Pool for Expired Username
Expiration After Certain Connection Time
User Disconnection from RADIUS
This document presents the configurations related to some advanced FreeRADIUS use cases.
The Expiration attribute is entered in radcheck table as follows:
This means that this username will expire on 25 Apr 2020 at 10:42:00. Session-Timeout is given to user based on this.
Note: expiration keyword needs to be present in authorize section in /etc/freeradius/3.0/sites-enabled/default (it is present by default in FreeRADIUS 3.x).
Add the following code in authorize section of /etc/freeradius/3.0/sites-enabled/default:
expiration{
userlock = 1
}
if(userlock){
# Let him connect with EXPIRED pool in reply
ok
update reply {
Reply-Message := "Your account has expired, %{User-Name} / Reason: DATE LIMIT REACHED"
Framed-Pool := "Expired"
}
}
Testing results:
Suppose a user has paid for a certain amount of time and their username needs to be expired after that much time online. We can use the Expire-After attribute for that:
Enter the following code in Post-Auth-Type REJECT section of /etc/freeradius/3.0/sites-enabled/default:
update reply {
Reply-Message = 'Wrong Password'
}
A new table is created to store username to MAC address bindings:
CREATE TABLE IF NOT EXISTS `macs` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`callingstationid` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'na',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
The following code is added to authorize section for MAC addition and authorization. When a username is initially created it has the MAC address set to “na”. Upon first connection the MAC address is saved in macs table, and subsequently it is checked.
update control{
#check if callingstationid does not exist in mac_limit Table
Tmp-String-0 = "%{sql: SELECT callingstationid FROM macs WHERE username = '%{User-Name}'}"
#check if mac is available or not in mac_limit table
Tmp-Integer-2 = "%{sql: SELECT count(*) FROM macs WHERE username = '%{User-Name}' AND callingstationid='%{Calling-Station-Id}'}"
}
if(&control:Tmp-String-0 == "na"){
"%{sql: UPDATE macs SET callingstationid = '%{Calling-Station-Id}' WHERE Username = '%{User-Name}'}"
update reply {
Reply-Message := "New mac Added"
}
}
elsif(&control:Tmp-Integer-2 == 0){
update reply {
Reply-Message += "MAC auth Failed"
}
reject
}
A new table is created to store huntgroups:
CREATE TABLE radhuntgroup (
id int(11) unsigned NOT NULL auto_increment,
groupname varchar(64) NOT NULL default '',
nasipaddress varchar(15) NOT NULL default '',
nasportid varchar(15) default NULL,
PRIMARY KEY (id),
KEY nasipaddress (nasipaddress)
) ;
NAS IP addresses are assigned to various huntgroups:
Users are assigned to usergroups:
The following code is added in authorize section after preprocess. This code adds the Huntgroup-Name attribute to RADIUS Access Request. Then, it allows users of usergroup hg1users to only be connected on NASs belonging to hg1. Similarly, users of usergroup usergroup2 are only allowed to be connected to NASs belonging to hg2.
update request{
Huntgroup-Name := "%{sql:SELECT groupname FROM radhuntgroup WHERE nasipaddress='%{NAS-IP-Address}'}"
}
# bind hg1users usergroup to hg1 huntgroup
if (SQL-Group == "hg1users") {
if (Huntgroup-Name == "hg1") {
#ok
}
else {
update reply {
Reply-Message += "Error: User not allowed connection on this device"
}
reject
}
}
# bind hg2users usergroup to hg2 huntgroup
elsif (SQL-Group == "hg2users") {
if (Huntgroup-Name == "hg2") {
#ok
}
else {
update reply {
Reply-Message += "Error: User not allowed connection on this device"
}
reject
}
}
Suppose we only want to allow requests to RADIUS of the following Service-Type:
-
Framed
-
Login
And we want to reject all other requests. The following code should be added to authorize section:
#Only allow service types "Framed-User" or "Login-User"; reject all others
if((&request:Service-Type=="Framed-User") || (&request:Service-Type=="Login-User")){
#ok
}
else{
update reply {
Reply-Message += "Wrong service type"
}
reject
}
Time Based Quota
We can use Session-Timeout attribute to limit session time of a user. For example, if we wanted to limit a user to only 30 minutes of network access daily we could set Session-Timeout to 1800 (1800 s = 30 min). This will ensure that the user automatically gets disconnected after 30 min. But the problem with this approach is that the user can connect again to get 30 more minutes. To solve this problem, FreeRADIUS has some pre-defined counters that can be used to assign time-based session limits (like daily, monthly, etc).
The file mods-enabled/sqlcounter contains several default counters. This is the configuration of daily counter:
sqlcounter dailycounter {
sql_module_instance = sql
dialect = mysql
counter_name = Daily-Session-Time
check_name = Max-Daily-Session
reply_name = Session-Timeout
key = User-Name
reset = daily
}
This counter checks for an internal attribute Max-Daily-Session and uses the session data in DB to calculate the remaining session time of the user for that day. It then adds that remaining Session-Timeout to RADIUS Access Accept packet.
We need to add dailycounter to sites-available/default in authorize section. In DB add the Max-Daily-Session attribute to radcheck table:
Enable the sqlcounter module:
cd /etc/freeradius/3.0/mods-enabled
ln -s ../mods-available/sqlcounter
Volume Based Quota
We can use sqlcounter module to query accounting data in DB and impose volume-based limits. Suppose we want to allow a user to have 25 MB of volume daily.
In the file mods-enabled/sqlcounter create a new counter:
sqlcounter total_volume {
sql_module_instance = sql
dialect = sql
counter_name = Max-Total-Volume
check_name = Max-Volume
key = User-Name
reset = daily
query = "SELECT SUM(acctinputoctets) + SUM(acctoutputoctets) FROM radacct WHERE UserName='%{${key}}'"
}
Register the counter total_volume in authorize section in sites-available/default. Add the Max-Volume attribute for that user in bytes in radcheck table:
Once the user has exceeded their volume limit they will not be authorized on their next connection attempt. Please note that users who are already connected will not be affected by this restriction. To perform any action on such already-connected sessions that have gone over their volume limit some script can be written that periodically queries the DB and if it finds some user has used up their data limit it performs some action on them (like disconnection, rate-limiting etc).
To disconnect a user from RADIUS we use the user’s username and IP address and secret of NAS in radclient to disconnect that user:
root@ubuntu:~# echo 'User-Name = test1' | radclient -x 192.168.100.35:3799 disconnect ''1234''
Sent Disconnect-Request Id 28 from 0.0.0.0:35685 to 192.168.100.35:3799 length 27
User-Name = "test1"
Received Disconnect-ACK Id 28 from 192.168.100.35:3799 to 0.0.0.0:0 length 30
NAS-Identifier = "MikroTik"
Enabling incoming messages in Mikrotik NAS
hii, there is a problem with the daily total volume. once the limit is reached it resets