ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CentOS下FreeRADIUS+daloradius+OpenPortal认证计费与限速实战

CentOS下FreeRADIUS+daloradius+OpenPortal认证计费与限速实战

1. 为什么这套组合方案至今仍有人在用

先把场景说清楚。你手上有一台 CentOS 服务器,需要给一批用户提供统一的网络接入认证,同时还要对每个用户的带宽做限制——不是简单的限速,而是按用户、按组、按时间段灵活控制。这种需求在园区网、酒店、小型运营商、实验室环境里非常常见。

FreeRADIUS 负责认证、授权、计费这三件事,daloradius 是架在它上面的 Web 管理界面,OpenPortal 则负责把认证页面和用户交互这一层补齐。三者拼在一起,就是一套完整的"认证+管理+门户"方案。这套组合不是最新的技术栈,但它的稳定性经过了大量实际环境的验证,配置逻辑清晰,出了问题也容易定位。

我之所以还愿意花时间整理这套流程,是因为网上能找到的资料要么太老、要么缺关键步骤、要么在流量控制这块一笔带过。实际配下来你会发现,真正卡人的不是 FreeRADIUS 本身,而是 daloradius 和数据库的对接细节,以及限速策略到底该挂在哪一层。这篇文章会把整个链路拆开讲,包括每一步为什么这么做、参数怎么算、哪里容易翻车。

适合读这篇的人:有 Linux 基础、需要搭建一套可控的认证计费系统、并且对流量控制有实际要求的人。纯小白建议先把 CentOS 的基本操作和 MySQL 的增删改查过一遍再来看。

2. 环境准备与组件职责的重新梳理

2.1 三个组件到底谁管什么

很多人配这套东西的时候容易混乱,是因为没搞清楚三个组件的边界。我用一句话概括:FreeRADIUS 是干活的,daloradius 是管账的,OpenPortal 是迎客的。

FreeRADIUS 是核心守护进程,监听认证请求,查数据库验证身份,返回授权属性,同时记录计费信息。它不关心界面好不好看,只关心规则对不对。

daloradius 是一套 PHP 写的 Web 应用,它直接读写 FreeRADIUS 用的那套数据库表。你在界面上添加用户、设置组、查看在线记录,本质上都是在操作 MySQL 里的表。它不参与认证过程,只是把数据库操作可视化了。

OpenPortal 负责的是用户接入时的门户页面——用户连上网络后被重定向到一个登录页,输入账号密码,提交给 RADIUS 认证。它解决的是"用户怎么把凭据交上来"这个问题。

理解了这个分工,后面配置的时候你就知道每一步该去哪个组件的配置文件里找。

2.2 CentOS 基础环境的关键选择

系统版本上,CentOS 7 和 CentOS 8 的包管理差异比较大。CentOS 7 用 yum,CentOS 8 转向了 dnf,而且默认的软件源里 FreeRADIUS 版本也不同。我建议用 CentOS 7 的较新小版本,原因是 daloradius 对 PHP 版本有要求,CentOS 7 默认的 PHP 5.4 太老,需要额外装 remi 源升级到 PHP 7.x,这个组合验证得最充分。

关闭 SELinux 这件事我要单独说。很多人上来就setenforce 0,但重启后又回来了。正确做法是改配置文件:

sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config setenforce 0

为什么必须关?因为 FreeRADIUS 要写日志到非标准目录、daloradius 要读写数据库套接字、OpenPortal 要监听特定端口,SELinux 的默认策略会把这些全挡掉。你也可以花时间写自定义策略,但在搭建阶段,关掉它能让排查问题的复杂度降低一个数量级。

防火墙方面,需要放行的端口包括:RADIUS 认证端口 1812/udp、计费端口 1813/udp、Web 管理界面的 80/tcp、数据库的 3306/tcp(如果数据库不在本机)。用 firewalld 的话:

firewall-cmd --permanent --add-port=1812/udp firewall-cmd --permanent --add-port=1813/udp firewall-cmd --permanent --add-service=http firewall-cmd --reload

注意:1812 和 1813 是 UDP 端口,不是 TCP。我见过有人只开了 TCP,然后死活认证不通,查了半天才发现协议搞错了。

2.3 数据库选型与初始化

MySQL 还是 MariaDB?在 CentOS 7 上,默认源里的 MariaDB 5.5 就够用,daloradius 的建表语句对这两个都兼容。我倾向于用 MariaDB,因为它在 CentOS 上的包依赖更干净。

安装完成后要做几件事:设置 root 密码、删除匿名用户、禁止 root 远程登录。这些是基本安全操作,不展开。重点是要创建一个专用的数据库用户给 daloradius 和 FreeRADIUS 用,不要直接用 root。

CREATE DATABASE radius; GRANT ALL ON radius.* TO 'radius'@'localhost' IDENTIFIED BY '你的强密码'; FLUSH PRIVILEGES;

这里有个细节:FreeRADIUS 连接数据库的方式有两种,一种是通过rlm_sql模块直接用 MySQL 客户端库,另一种是通过 Unix socket。用 localhost 走 socket 性能更好,但要注意 MySQL 的 socket 路径要和 FreeRADIUS 配置里写的一致。CentOS 上通常是/var/lib/mysql/mysql.sock。

3. FreeRADIUS 的安装与核心配置拆解

3.1 安装与目录结构认知

yum install -y freeradius freeradius-mysql freeradius-utils

装完之后先别急着改配置,花两分钟把目录结构看清楚。/etc/raddb/是配置根目录,里面几个关键文件:radiusd.conf是主配置,clients.conf定义哪些客户端可以来认证,users是本地用户文件,sites-available/default是虚拟主机配置,mods-available/sql是数据库模块配置。

/var/log/radius/是日志目录,radius.log记录启动和认证信息,radacct/下面按日期存放计费明细。调试的时候这两个地方是主要的信息来源。

3.2 让 FreeRADIUS 认识数据库

默认安装的 FreeRADIUS 用的是文件认证,我们要把它切到数据库认证。第一步是启用 sql 模块:

ln -s /etc/raddb/mods-available/sql /etc/raddb/mods-enabled/sql

然后编辑/etc/raddb/mods-available/sql,找到sql {这一段,修改以下关键项:

driver = "rlm_sql_mysql" dialect = "mysql" server = "localhost" port = 3306 login = "radius" password = "你的强密码" radius_db = "radius"

再往下找到read_clients = yes这一行,把注释去掉。这个选项让 FreeRADIUS 从数据库的nas表读取客户端定义,而不是从clients.conf读。用 daloradius 管理的时候这个必须开,否则你在界面上添加的 NAS 设备不生效。

还有一个容易漏的地方:acct_table1、acct_table2、postauth_table这些表名要和 daloradius 建的表名一致。daloradius 默认建的表就是radacct、radpostauth这些,所以一般不用改,但如果你手动改过表名,这里必须同步。

3.3 虚拟主机配置里必须改的三处

编辑/etc/raddb/sites-available/default,这个文件决定了认证请求怎么处理。找到authorize {段,确保里面有sql这一行,而且它在files之前。顺序很重要:先查数据库,数据库里没有再去查本地文件。

找到accounting {段,同样确保sql在里面。计费数据要写进数据库,这一行不能少。

找到session {段,把sql加进去。这个段负责同时在线数控制,如果你要做"一个账号只能同时在线一台设备",就靠这里。

改完之后建立软链接到 sites-enabled:

ln -s /etc/raddb/sites-available/default /etc/raddb/sites-enabled/default

3.4 用 radtest 验证认证链路

在动 daloradius 之前,先用命令行工具确认 FreeRADIUS 本身是通的。启动服务:

systemctl start radiusd systemctl enable radiusd

然后用 radtest 测试。但前提是数据库里得有个测试用户。手动插一条:

INSERT INTO radcheck (username, attribute, op, value) VALUES ('testuser', 'Cleartext-Password', ':=', 'testpass');

然后:

radtest testuser testpass localhost 0 testing123

testing123是clients.conf里 localhost 客户端的默认密钥。如果返回Access-Accept,说明认证链路通了。如果返回Access-Reject,去看/var/log/radius/radius.log,里面会写清楚是数据库连不上还是密码不匹配。

这一步非常关键。我见过太多人跳过这步直接配 daloradius,结果界面能登录、能加用户,但认证就是不通,最后排查发现是 FreeRADIUS 根本没连上数据库。先把底层打通,上层的问题才好定位。

4. daloradius 部署中的版本陷阱与数据库对接

4.1 版本选择与 PHP 环境

daloradius 的版本更新不算频繁,但不同版本对 PHP 的要求差异很大。较新的版本需要 PHP 7.0 以上,而 CentOS 7 默认是 PHP 5.4。你需要先装 remi 源:

yum install -y epel-release yum install -y http://rpms.remirepo.net/enterprise/remi-release-7.rpm yum install -y yum-utils yum-config-manager --enable remi-php74 yum install -y php php-mysql php-gd php-common php-mbstring php-cli

装完之后php -v确认版本。这里有个坑:如果你之前装过 PHP 5.4 的相关包,升级后 Apache 可能还在加载旧的模块。最稳妥的做法是重启 httpd 服务,并且检查/etc/httpd/conf.d/php.conf是否指向了新的模块路径。

4.2 导入数据库结构的正确姿势

daloradius 的安装包里有个contrib/db/mysql-daloradius.sql文件,这是建表语句。但直接导入经常会报错,原因是字符集问题。先确认数据库的默认字符集:

SHOW VARIABLES LIKE 'character_set_database';

如果是latin1,导入中文用户信息会乱码。建议在创建数据库时就指定:

CREATE DATABASE radius CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

然后导入:

mysql -u radius -p radius < mysql-daloradius.sql

导入完成后,用SHOW TABLES;确认表都建好了。应该能看到radacct、radcheck、radgroupcheck、radusergroup、nas、operators等表。其中operators表存的是 daloradius 的管理员账号,默认会有一条 admin 记录,密码是加密的,首次登录后要改掉。

4.3 Web 界面配置文件的几个关键项

把 daloradius 的代码放到 Web 目录下,比如/var/www/html/daloradius。然后编辑library/daloradius.conf.php,这是核心配置文件。需要改的项:

$configValues['CONFIG_DB_HOST'] = 'localhost'; $configValues['CONFIG_DB_PORT'] = '3306'; $configValues['CONFIG_DB_USER'] = 'radius'; $configValues['CONFIG_DB_PASS'] = '你的强密码'; $configValues['CONFIG_DB_NAME'] = 'radius';

还有一个容易被忽略的项:CONFIG_MAINTENANCE_LOAD。这个如果设成 1,界面会进入维护模式,什么都点不了。确保它是 0。

另外,daloradius 需要写日志到某个目录,默认是/var/log/daloradius/。这个目录要手动创建并给 Apache 用户写权限:

mkdir -p /var/log/daloradius chown apache:apache /var/log/daloradius

4.4 登录后第一件该做的事

用默认管理员登录后,不要急着加用户。先去Configuration菜单里检查几个设置:RADIUS Settings里的RADIUS Server地址要填 FreeRADIUS 所在机器的 IP,RADIUS Secret要和clients.conf里对应客户端的密钥一致。这个密钥是 daloradius 在界面上执行某些操作(比如断开用户连接)时用的,填错了那些功能会静默失败。

然后去Management里添加一个 NAS。NAS 就是网络接入设备,可以是交换机、无线控制器、或者任何支持 RADIUS 的设备。添加时填 IP 地址和共享密钥,这个密钥要和设备上配置的一致。添加完 NAS 之后,FreeRADIUS 才能接受来自这个设备的认证请求。

5. OpenPortal 的角色与认证页面集成

5.1 OpenPortal 解决的是什么问题

FreeRADIUS 和 daloradius 配好之后,认证是能用了,但用户怎么触发认证?如果是 802.1X 环境,客户端会自动弹出认证框。但如果是 Web 认证环境——比如酒店、商场、园区的访客网络——用户连上 WiFi 后需要打开一个网页输入账号密码,这个网页就是 OpenPortal 提供的。

OpenPortal 本质上是一个轻量级的 Web 服务,它监听特定端口,接收来自网络设备的重定向请求,展示登录页面,然后把用户提交的凭据转发给 RADIUS 做认证。认证通过后,它通知网络设备放行该用户的流量。

5.2 部署与对接要点

OpenPortal 的部署方式取决于你拿到的版本。如果是源码包,解压后放到 Web 目录,配置数据库连接指向同一个 radius 库。它需要读取radcheck表来验证用户,也需要写radacct表来记录会话。

关键配置项通常在一个config.php或类似文件里,需要设置:RADIUS 服务器地址、共享密钥、数据库连接信息、以及监听端口。监听端口默认可能是 8080 之类的,要确保防火墙放行。

对接的时候有一个细节:OpenPortal 提交认证请求的方式。有的版本是直接调用radtest命令,有的是通过 PHP 的 RADIUS 扩展。如果是前者,要确保 Web 用户有执行radtest的权限;如果是后者,要装php-radius扩展。这个扩展在 remi 源里有,装完之后要重启 httpd。

5.3 认证流程的完整链路

用户连上网络,设备把 HTTP 请求重定向到 OpenPortal 的登录页。用户输入账号密码,OpenPortal 把凭据发给 FreeRADIUS。FreeRADIUS 查数据库,验证通过后返回Access-Accept,同时带上授权属性——这些属性里就包含了限速策略。OpenPortal 收到成功响应后,通知网络设备放行,用户开始上网。

计费方面,网络设备会在会话开始时发Accounting-Start,结束时发Accounting-Stop,中间定期发Interim-Update。FreeRADIUS 把这些都写进radacct表,daloradius 从表里读出来展示在线用户和流量统计。

6. 流量控制的三种实现路径与参数计算

6.1 限速到底该在哪一层做

这是整套方案里最核心也最容易搞混的部分。流量控制可以在三个层面实现:RADIUS 属性下发、网络设备本地策略、以及服务器端限速。三者的适用场景完全不同。

RADIUS 属性下发是最灵活的方式。你在数据库里给用户或用户组设置特定的属性,FreeRADIUS 在认证响应里把这些属性发给网络设备,设备根据属性值来限速。这种方式的好处是集中管理,改数据库就生效,不用登录每台设备。缺点是依赖设备支持相应的属性。

网络设备本地策略是在交换机或控制器上直接配限速规则,RADIUS 只负责认证。这种方式简单直接,但用户多了之后管理成本高。

服务器端限速是在 FreeRADIUS 所在的服务器上用 tc 或 iptables 做限速。这种方式适合服务器本身就是网关的场景,但如果只是做认证服务器,流量不经过它,就限不了。

绝大多数场景下,你应该用第一种——RADIUS 属性下发。

6.2 标准属性与厂商私有属性

限速用的 RADIUS 属性分两类。标准属性是 RFC 定义的,所有厂商都认,但功能有限。最常用的是Filter-Id,它是一个字符串,具体含义由设备解释。有的设备把它当作限速策略的名称,有的当作 ACL 名称。

厂商私有属性功能更强,但只在该厂商的设备上生效。比如某些厂商用WISPr-Bandwidth-Max-Down和WISPr-Bandwidth-Max-Up来分别控制上下行带宽,单位是 bits per second。这两个属性在无线认证场景里用得很多。

配置的时候,在 daloradius 的radgroupreply表里给用户组添加属性。比如给"普通用户"组设置下行 2Mbps、上行 512Kbps:

INSERT INTO radgroupreply (groupname, attribute, op, value) VALUES ('normal', 'WISPr-Bandwidth-Max-Down', ':=', '2097152'); INSERT INTO radgroupreply (groupname, attribute, op, value) VALUES ('normal', 'WISPr-Bandwidth-Max-Up', ':=', '524288');

注意单位是 bps,不是 Bps。2Mbps 是 2097152 bps,不是 2097152 字节。这个换算错误我见过不止一次,配完之后用户说网速不对,一查发现单位搞错了。

6.3 按用户、按组、按时段的差异化限速

daloradius 支持用户组的概念。你可以建多个组,每个组设不同的限速属性,然后把用户分配到组里。用户会继承组的属性,同时也可以有自己独有的属性。当用户属性和组属性冲突时,用户属性优先。

按时段限速稍微复杂一些。FreeRADIUS 本身不直接支持基于时间的属性下发,但可以通过radcheck表里的Expiration属性来控制账号有效期,或者用Login-Time属性来控制允许登录的时间段。如果要实现"白天限速 1M、晚上限速 5M"这种动态策略,需要写自定义的 unlang 策略或者用外部脚本。

一个可行的做法是在sites-available/default的post-auth段里加一段逻辑,根据当前时间修改返回的属性。这需要一定的 unlang 编写能力,但灵活性很高。

6.4 限速不生效的排查思路

配了限速但没效果,按这个顺序查:

第一,确认属性真的下发了。在 FreeRADIUS 调试模式下(radiusd -X)看认证响应的Reply-Message或属性列表,确认限速属性在里面。

第二,确认设备认这个属性。不同厂商对属性的支持差异很大。查设备的文档,看它支持哪些限速属性。如果不支持 WISPr 属性,试试Filter-Id或者厂商自己的属性。

第三,确认属性值的单位。前面说了,bps 和 Bps 差 8 倍。还有的设备用 kbps 而不是 bps,值要相应调整。

第四,确认设备上的限速功能是开启的。有的设备默认不启用 RADIUS 下发的限速策略,需要在全局配置里打开一个开关。

7. 计费数据与在线用户监控的落地细节

7.1 radacct 表的结构与关键字段

radacct表是计费数据的核心。每次会话开始,FreeRADIUS 插入一条记录,acctstarttime记录开始时间,acctstoptime为空表示会话进行中。会话结束时更新acctstoptime和acctsessiontime。中间的Interim-Update会更新acctinputoctets和acctoutputoctets,这两个字段就是上下行流量字节数。

acctsessionid是会话的唯一标识,nasipaddress是设备 IP,username是用户名。这几个字段是查询在线用户和统计流量的关键。

7.2 在线用户列表为什么有时不准

daloradius 的在线用户页面读的是radacct表里acctstoptime IS NULL的记录。但实际环境中经常出现"用户已经下线了,但记录还在"的情况。原因是设备没有发送Accounting-Stop包——可能是设备重启了、网络断了、或者配置有问题。

解决办法有两个。一是在 FreeRADIUS 里配置interim_update的超时清理,超过一定时间没有收到更新就把会话标记为结束。二是在 daloradius 里手动清理,或者写个定时脚本定期清理超时的会话记录。

UPDATE radacct SET acctstoptime = NOW(), acctterminatecause = 'Stale-Session' WHERE acctstoptime IS NULL AND acctupdatetime < DATE_SUB(NOW(), INTERVAL 1 HOUR);

这个脚本可以放到 crontab 里每小时跑一次。

7.3 流量统计的精度问题

acctinputoctets和acctoutputoctets的精度取决于设备发送Interim-Update的频率。如果设备每 5 分钟发一次,那流量统计的精度就是 5 分钟。如果设备只在会话结束时发一次,那会话进行中就看不到流量。

在设备上把interim-update的间隔设短一些,比如 60 秒,能提高精度,但会增加数据库写入压力。这个需要根据用户规模和服务器性能来权衡。几百个用户的话,60 秒间隔完全没问题;上万用户的话,要考虑分表或者用时序数据库。

8. 几个真实踩过的坑与排查链路

8.1 认证成功但用户上不了网

这个问题的排查链路是这样的:先看 FreeRADIUS 日志,确认返回的是Access-Accept。如果是,说明认证没问题,问题在授权或设备侧。然后看返回的属性里有没有设备需要的授权属性。有的设备要求必须返回Tunnel-Type、Tunnel-Medium-Type、Tunnel-Private-Group-Id这一组属性才会放行,缺一个都不行。

再检查 NAS 的共享密钥。密钥不匹配的话,设备会丢弃 RADIUS 响应,表现就是认证超时。在 FreeRADIUS 日志里能看到Received packet from x.x.x.x with invalid signature这样的提示。

最后检查设备的 ACL 或策略配置。RADIUS 只负责告诉设备"这个用户通过了",具体放行哪些流量、走哪个 VLAN,还是设备自己决定的。

8.2 daloradius 界面报数据库连接错误

先确认daloradius.conf.php里的数据库信息对不对。然后确认 MySQL 用户有没有从 localhost 连接的权限。有时候用户建了但只给了%的权限,localhost 反而连不上。

还有一个隐蔽的问题:PHP 的 MySQL 扩展。CentOS 7 上默认可能是mysqlnd,但 daloradius 的某些版本用的是旧的mysql_connect函数,这个函数在 PHP 7 里已经被移除了。如果报Call to undefined function mysql_connect(),说明你用的 daloradius 版本太老,需要升级到用mysqli或PDO的版本。

8.3 计费数据重复写入

有时候会发现同一个会话在radacct表里有两条记录。原因是设备发送了重复的Accounting-Start,或者 FreeRADIUS 的acct_unique模块没启用。acct_unique模块会根据会话 ID、用户、NAS 等信息生成一个唯一键,防止重复插入。在sites-available/default的preacct段里确保acct_unique被调用。

8.4 限速属性下发但设备不执行

前面提过,先确认设备支持该属性。如果设备支持但就是不执行,检查属性值的格式。有的设备要求Filter-Id的值必须是设备上已存在的策略名称,不能随便填。这种情况下你需要在设备上先建好策略,然后在 RADIUS 里下发策略名称。

还有一个可能:设备上的 RADIUS 授权功能没开。有的设备默认只做认证不做授权,需要在配置里显式开启。

9. 性能调优与日常维护的几点经验

FreeRADIUS 的性能瓶颈通常在数据库查询上。如果用户量大,可以开启rlm_sql的缓存功能,把常用的查询结果缓存起来。在sql模块配置里找到query_timeout和read_groups这些选项,根据实际情况调整。

数据库方面,radacct表会越来越大,需要定期归档。可以按月分表,或者把历史数据导出后删除。daloradius 本身没有自动归档功能,需要自己写脚本。

日志方面,FreeRADIUS 的调试日志在radiusd -X模式下会输出到终端,正常运行时写到/var/log/radius/radius.log。日志级别可以在radiusd.conf里调整,生产环境建议用auth级别,不要用debug,否则日志量会非常大。

备份方面,最重要的是数据库。radius库里的radcheck、radgroupreply、nas、operators这几张表存的是配置数据,丢了要重新配。radacct表存的是计费数据,丢了影响对账。建议每天定时mysqldump,保留最近 30 天。

我在实际维护中发现,这套系统最怕的不是配置复杂,而是"配完之后没人管"。用户加了不删、组策略改了不测试、数据库满了不清理,这些问题积累起来比初始配置的难度大得多。建议至少每个月检查一次在线用户列表和数据库大小,每季度做一次全量备份恢复演练。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进