整套Radius环境基于mariadb+freeradius+daloradius,daloradius本身有踢用户下线功能,但是只能逐个用户踢下线。本文结合网上其他网友的方法,提供一段可执行的python代码,用以定时批量将已停断的用户主动踢下线。
程序的原理是查找所有当前在线的但是却被daloradius放入daloRADIUS-Disabled-Users用户组(daloradius是用这种方法禁用、停断用户的)的用户以及在线信息(用户的Acct-Session-Id等信息),用这些信息向Bras/NAS发送DM信息,完成将用户踢下线的功能。这里有个前提是需要说明,因为我们这套系统要和其他系统对接,通过其他系统完成用户业务的开通和停断,其他系统停断用户只是将用户放入daloRADIUS-Disabled-Users用户组,没有给我们提供诸如用户有效时间等其他的信息,我们只能依靠用户是不是在daloRADIUS-Disabled-Users用户组来判断用户是否已经被停断业务。
服务器上要安装python3以及python的mariadb、pyrad包,并且由于使用了mariadb包,需要连接数据库,还要确保部署此程序的linux服务器已经安装了mariadb或者MariaDB Connector/C 3.1.5以上版本,低于此版本安装mariadb包会报错。
不会安装MariaDB Connector/C的朋友可以参考以下shell命令:
mkdir -p /tmp/mdbccbin
cd /tmp/mdbccbin
curl -O https://downloads.mariadb.com/Connectors/c/connector-c-3.1.10/mariadb-connector-c-3.1.10-ubuntu-bionic-amd64.tar.gz
echo "1b5b513f44967efadf5eae5e34952cd61f94655575d45b5a9182ea1b91d1d1fa mariadb-connector-c-3.1.10-ubuntu-bionic-amd64.tar.gz" | sha256sum -c
# get root
sudo su
tar xvf mariadb-connector-c-3.1.10-ubuntu-bionic-amd64.tar.gz --directory /usr --strip-components 1
echo "/usr/lib/mariadb/" > /etc/ld.so.conf.d/mariadb.conf
ldconfig
# back to regular user
exit
python3 -m pip install --user mariadb
对安全性要求高的朋友可以在数据库中创建一个view,然后只允许一个权限较低的用户访问此view。
CREATE OR REPLACE VIEW view_kick_user
AS
SELECT radacct.Username,
radacct.FramedIPAddress,
radacct.NASIPAddress,
radacct.AcctSessionId,
nas.secret,
nas.shortname
FROM radacct
LEFT JOIN nas ON (nas.nasname = radacct.NASIPAddress)
LEFT JOIN radusergroup ON (radacct.Username = radusergroup.username)
WHERE (radacct.AcctStopTime IS NULL OR radacct.AcctStopTime = '0000-00-00 00:00:00')
AND (radacct.Username LIKE '%')
AND (radusergroup.groupname = 'daloRADIUS-Disabled-Users')
ORDER BY radacctid asc;
对安全性要求不高的朋友可以将后面python程序内的select查询语句改成下面的语句:
SELECT radacct.Username,
radacct.FramedIPAddress,
radacct.NASIPAddress,
radacct.AcctSessionId,
nas.secret,
nas.shortname
FROM radacct
LEFT JOIN nas ON (nas.nasname = radacct.NASIPAddress)
LEFT JOIN radusergroup ON (radacct.Username = radusergroup.username)
WHERE (radacct.AcctStopTime IS NULL OR radacct.AcctStopTime = '0000-00-00 00:00:00')
AND (radacct.Username LIKE '%')
AND (radusergroup.groupname = 'daloRADIUS-Disabled-Users')
ORDER BY radacctid asc
代码如下:
#!/usr/bin/python
from __future__ import print_function
from pyrad.client import Client
from pyrad import dictionary
from pyrad import packet
import mariadb
import datetime
import sys
logfile = open("freeradius_kick_user.log", "a")
try:
conn = mariadb.connect(
user='数据库用户名',
password='数据库密码',
host='数据库服务器地址',
port=3306,
database='数据库名'
)
except mariadb.Error as e:
print(f"Error connecting to MariaDB Platform: {e}")
sys.exit(1)
cur = conn.cursor()
cur.execute("SELECT Username,FramedIPAddress,NASIPAddress,AcctSessionId,secret,shortname FROM view_kick_user;")#这里是查询View的语句,可以改成上面直接查询表的语句。
for item in cur:
ATTRIBUTES = {
"Acct-Session-Id": item[3],
"User-Name": item[0],
"Framed-IP-Address": item[1]
}
logfile.write(str(datetime.datetime.now())+" "+str(item)+"!\n")
print("Program is running......:")
# create coa client
client = Client(server=item[2], secret=item[4].encode(), dict=dictionary.Dictionary("dictionary"))
# set coa timeout
client.timeout = 5
# create coa request packet
attributes = {k.replace("-", "_"): ATTRIBUTES[k] for k in ATTRIBUTES}
# create disconnect request
request = client.CreateCoAPacket(code=packet.DisconnectRequest, **attributes)
# send request
result = client.SendPacket(request)
if result.code==packet.DisconnectACK:
resultstr = " Disconnect ACK "
elif result.code==packet.DisconnectNAK:
resultstr = " Disconnect NAK "
else:
resultstr = " Unknow "
logfile.write(str(datetime.datetime.now())+" "+str(result)+"\n")
logfile.write(str(datetime.datetime.now())+resultstr+" Code:"+str(result.code)+"!\n")
logfile.write(str(datetime.datetime.now())+" end of result\n")
conn.close()
logfile.close()
此程序因为pyrad的原因,只能在linux环境下运行,windows下运行会出现AttributeError: module ‘select’ has no attribute 'poll’的错误提示。程序里涉及的字典档可以到github上pyrad项目的示例文件夹中下载,并与本程序放在同一级目录中。
本程序执行后,在程序同级目录下会生成一个freeradius_kick_user.log日志文件用以查看执行结果及排错。
linux下定时执行程序的方法请大家自行百度吧。
关于非正常掉线用户的数据清理问题,可以用以下语句查找这种用户:
【2020年12月1日更正】:需要将在线时间再减去acctsessiontime,再与1800秒比较,1800秒可以根据需要设置,记录用户会话时间间隔由用户或者用户组的Acct-Interim-Interval属性决定,默认可能是300秒,我自己设置为600秒,这样的话1800秒意味着NAS半个小时3次没有上报记账信息即为非正常离线。
select radacctid,acctstarttime, acctstoptime, username from radacct where acctstoptime is null and timestampdiff(second, acctstarttime, current_timestamp)-acctsessiontime>1800
将用户为空的下线时间修改为当前时间的语句如下:
update radacct set acctstoptime = current_timestamp where acctstoptime is null and timestampdiff(second, acctstarttime, current_timestamp)-acctsessiontime>1800
另外,cron里每半个小时执行一次脚本的设置如下:
*/30 * * * * python3 clear_abnormal_user.py >> /tmp/freeradius_clear_abnormal_user.log 2>&1
而不是:
30 * * * * python3 clear_abnormal_user.py >> /tmp/freeradius_clear_abnormal_user.log 2>&1
上面的语句意思是每个小时每逢半点执行一次,比如:6点半、7点半、8点半,以此类推。
参考文章:
1.PostgreSQL下的FreeRadius 清除非正常掉线用户
2.Error pip install mariadb on ubuntu server