Install FreeRadius On Debian 10
Posted on 2020-11-21 in how-to
本文假定系统为全新安装的 DEBIAN 10,正常安装DEBIAN,开启SSH,SUDO功能。 通过安装 FreeRadius 和 PostgreSQL,支持常规的安装。 本安装教程最主要的功能在于支持 H3C 的 802.1x,并且限制用户允许登录访问的设备数量。 TODO: 对客户端启用证书的功能待下次测试并完善
一、安装
# apt install freeradius freeradius-postgresql postgresql postgresql-client
二、配置 PostgreSQL
参考链接: https://wiki.freeradius.org/guide/SQL-HOWTO-for-freeradius-3.x-on-Debian-Ubuntu
切换至 postgres 用户,执行 psql
$ sudo -i -u postgres $ psql
通过 psql 创建数据库角色与数据库
>> CREATE ROLE radius LOGIN PASSWORD 'radpass'; >> CREATE DATABASE radius WITH owner = radius;
退出 postgres 用户,回到 recozo 用户环境
# cd /etc/freeradius/3.0/mods-config/sql/main/postgresql # psql -h localhost -U radius radius < schema.sql # psql -h localhost -U radius radius < setup.sql -- 如果执行了 CREATE ROLE 可以不执行
三、配置 FreeRadius 3 启用 sql
参考链接: https://networkradius.com/doc/3.0.10/raddb/mods-available/eap/peap.html
# vi /etc/freeradius/3.0/sites-available/default
--------------------EDIT--------------------
uncomment sql in authorize, accounting, session, post-auth section
--------------------------------------------
# vi /etc/freeradius/3.0/sites-available/inner-tunnel
--------------------EDIT--------------------
uncomment sql in authorize, session, post-auth section
--------------------------------------------
# vi /etc/freeradius/3.0/mods-available/sql
--------------------EDIT--------------------
sql {
driver = "rlm_sql_postgresql"
dialect = "postgresql"
# Connection info:
server = "localhost"
port = 5432
login = "radius"
password = "setup.sql中设置的口令"
# Database table configuration for everything except Oracle
radius_db = "radius"
# Set to ‘yes’ to read radius clients from the database (‘nas’ table)
# Clients will ONLY be read on server startup.
read_clients = yes
# Table to keep radius client info
client_table = "nas"
--------------------------------------------
# cd /etc/freeradius/3.0/mods-enabled
# ln -s ../mods-available/sql sql
# chown -h freerad:freerad /etc/freeradius/3.0/mods-enabled/sql
# cd /etc/freeradius/3.0/mods-config/sql/main/postgresql
# vi rczmacsetup.sql
--------------------ADD---------------------
/*
* Table structure for table 'rczmaclimit'
*/
CREATE TABLE rczmaclimit (
UserName text PRIMARY KEY,
MacLimit integer NOT NULL DEFAULT 1, -- 0 Unlimited
Remark text NOT NULL DEFAULT ''
);
GRANT SELECT on rczmaclimit TO radius;
/*
* Table structure for table 'rczmaccheck'
*/
CREATE TABLE rczmaccheck (
id serial PRIMARY KEY,
UserName text NOT NULL,
CallingStationId text NOT NULL,
AuthDate timestamp with time zone NOT NULL default now()
);
create index rczmaccheck_UserName on rczmaccheck (UserName,CallingStationId);
GRANT SELECT, INSERT, UPDATE on rczmaccheck TO radius;
GRANT USAGE, SELECT ON SEQUENCE rczmaccheck_id_seq TO radius;
/*
* Stored Procedure for authorize_check_query
*/
CREATE OR REPLACE FUNCTION rczmac_authorize_check(rczuserName TEXT, rczcallingStationId TEXT)
RETURNS TABLE (id integer, UserName text, Attribute text, Value text, Op varchar(2))
LANGUAGE plpgsql
AS $$
DECLARE
isOk boolean DEFAULT false;
tmpMacLimit integer;
tmpMacTotal integer;
BEGIN
IF EXISTS(
SELECT *
FROM rczmaccheck
WHERE rczmaccheck.UserName = rczmac_authorize_check.rczuserName
AND rczmaccheck.CallingStationId = rczmac_authorize_check.rczcallingStationId
)
THEN
RAISE LOG '用户: % MAC: % ,MAC已登记', rczmac_authorize_check.rczuserName, rczmac_authorize_check.rczcallingStationId;
isOk = true;
ELSE
SELECT MacLimit INTO tmpMacLimit
FROM rczmaclimit
WHERE rczmaclimit.UserName = rczmac_authorize_check.rczuserName;
IF NOT FOUND THEN
RAISE LOG '用户: % MAC: % ,MAC上限无记录', rczmac_authorize_check.rczuserName, rczmac_authorize_check.rczcallingStationId;
tmpMacLimit = 1;
END IF;
IF tmpMacLimit = 0 THEN
RAISE LOG '用户: % MAC: % ,MAC上限为0', rczmac_authorize_check.rczuserName, rczmac_authorize_check.rczcallingStationId;
isOk = true;
ELSE
SELECT COUNT(*) INTO tmpMacTotal
FROM rczmaccheck
WHERE rczmaccheck.UserName = rczmac_authorize_check.rczuserName;
IF tmpMacLimit > tmpMacTotal THEN
RAISE LOG '用户: % MAC: % ,MAC未登记且未达到上限', rczmac_authorize_check.rczuserName, rczmac_authorize_check.rczcallingStationId;
isOk = true;
ELSE
RAISE LOG '用户: % MAC: % ,MAC达到上限禁止登录', rczmac_authorize_check.rczuserName, rczmac_authorize_check.rczcallingStationId;
isOk = false;
END IF;
END IF;
END IF;
RAISE LOG '用户: % 的MAC限制检查结果为: %', rczmac_authorize_check.rczuserName, isOk;
RETURN QUERY
-- 如果 isOk 不通过,不返回用户检查项
SELECT radcheck.id, radcheck.UserName, radcheck.Attribute, radcheck.Value, radcheck.Op
FROM radcheck
WHERE radcheck.UserName = rczmac_authorize_check.rczuserName AND isOk
ORDER BY id;
END;
$$;
/*
* Stored Procedure for post-auth-query
*/
CREATE OR REPLACE PROCEDURE rczmac_post_auth(rczuserName TEXT, rczPass TEXT, rczReply TEXT, rczcallingStationId TEXT)
LANGUAGE plpgsql
AS $$
DECLARE
tmpAuthDate timestamp DEFAULT now();
BEGIN
INSERT INTO radpostauth(username, pass, reply, CallingStationId, authdate)
VALUES (rczuserName, rczPass, rczReply, rczcallingStationId, tmpAuthDate);
IF rczReply = 'Access-Accept' THEN
IF EXISTS(
SELECT *
FROM rczmaccheck
WHERE rczmaccheck.UserName = rczmac_post_auth.rczuserName
AND rczmaccheck.CallingStationId = rczmac_post_auth.rczcallingStationId
)
THEN
RAISE LOG '用户: % MAC: % ,MAC已存在', rczmac_post_auth.rczuserName, rczmac_post_auth.rczcallingStationId;
ELSE
RAISE LOG '用户: % MAC: % ,MAC已新增', rczmac_post_auth.rczuserName, rczmac_post_auth.rczcallingStationId;
INSERT INTO rczmaccheck(UserName, CallingStationId, AuthDate)
VALUES (rczuserName, rczcallingStationId, tmpAuthDate);
END IF;
END IF;
END;
$$;
--------------------------------------------
# psql -h localhost -U radius radius < rczmacsetup.sql
# vi /etc/freeradius/3.0/mods-config/sql/main/postgresql/queries.conf
--------------------EDIT--------------------
# Authorization Queries
authorize_check_query = "\
SELECT * FROM rczmac_authorize_check( \
'%{User-Name}', \
'%{Calling-Station-Id}')"
# postauth_query - Insert some info after authentication
post-auth {
query = "CALL rczmac_post_auth( \
'%{User-Name}', \
'%{%{User-Password}:-Chap-Password}', \
'%{reply:Packet-Type}', \
'%{Calling-Station-Id}')"
--------------------------------------------
# vi /etc/freeradius/3.0/sites-available/inner-tunnel
--------------------ADD---------------------
#
# Look in an SQL database. The schema of the database
# is meant to mirror the "users" file.
#
# See "Authorization Queries" in sql.conf
update request {
&Calling-Station-Id := outer.request:Calling-Station-Id
}
sql
--------------------------------------------
# systemctl enable freeradius
# systemctl restart freeradius
四、验证SQL是否安装正确(可选)
开二个终端,分别运行 FreeRadius 和 测试
终端一
# systemctl stop freeradius # freeradius -X
终端二
# psql -h localhost -U postgres radius
radius=# insert into nas (nasname, shortname, secret, description) values ('xxx.xxx.xxx.xxx', 'Short Name', 'secure secret', 'description'); -- 加入要允许访问的NAS设备信息
radius=# insert into radcheck (username,attribute,op,value) values('username', 'Cleartext-Password', ':=', 'password'); -- 加入要允许访问的用户信息
radius=# \q
# radtest username password localhost 0 testing123
五、生成 eapol_test 工具并验证 eap (可选)
开二个终端,分别运行 FreeRadius 和 测试
终端一
# systemctl stop freeradius # freeradius -X
终端二首先生成 eapol_test
$ sudo apt install pkg-config
$ sudo apt install build-essential
$ sudo apt install libssl-dev
$ sudo apt install libnl-genl-3-dev
$ sudo apt install libdbus-1-dev
$ wget https://w1.fi/releases/wpa_supplicant-2.9.tar.gz
$ tar -zxf wpa_supplicant-2.9.tar.gz
$ cd wpa_supplicant-2.9/wpa_supplicant/
$ cp defconfig .config
$ vi .config
-------------------EDIT---------------------
CONFIG_EAPOL_TEST=y
--------------------------------------------
$ make eapol_test
/* 在全新安装的 Debian 11 中编译出错,原来在 Debian 10 下没有出错 */
/* https://patchwork.ozlabs.org/project/hostap/patch/20191018020419.28686-2-masashi.honma@gmail.com/ */
/* 2022-01-05 */
$ mkdir ~/bin
$ cp eapol_test ~/bin
继续在终端二测试 eap 功能
$ cd ~/bin
$ vi eap-ttls-pap.conf
-------------------ADD----------------------
network={
key_mgmt=WPA-EAP
eap=TTLS
identity="username"
anonymous_identity="anonymous"
password="password"
phase2="auth=PAP"
}
--------------------------------------------
$ ./eapol_test -c eap-ttls-pap.conf -s testing123
六、生成 eap 证书(可选,但是强烈建议)
参考链接: https://networkradius.com/doc/FreeRADIUS-Implementation-Ch6.pdf
参考链接: http://deployingradius.com/documents/configuration/certificates.html
一般来说,802.1X 下建议使用自己的CA,生成自签名证书并将根证书安装在用户终端上。 如果用户终端不安装根证书或使用第三方证书的话,存在以下安全隐患: 1、不安装根证书时,存在伪造服务器的可能(创建一个同名SSID的无线,使用伪造的radius服务器),导致用户名与密码泄露; 2、如果启用 EAP-TLS 并使用第三方根证书的话,此时只要是该机构签发的用户证书均有效,导致用户证书不受控。 一般情况下这不是我们想要的结果;因此,如果要使用其它CA的证书,请仔细考虑清楚。
注意:debian 下的 freeradius 会自动对 EAP 使用 ssl-cert 所生成的 ssl-cert-snakeoil 证书。 该证书是基于debian的自签名证书,主要用于方便需要创建SSL证书的软件包安装, 如果 snakeoil 证书过期,可以使用以下命令重新生成 sudo make-ssl-cert generate-default-snakeoil --force-overwrite , 也就是不用进行证书生成操作,也可以保证 freeradius 支持 EAP。 不过在生产环境下不应该使用该证书,而应根据实际生成或使用对应的证书。 测试中发现在 debian 环境下的 freeradius -X 不会自动生成证书(这点似乎与freeradius 官方的说明不一致)。
删除原来生成的证书(生产环境下慎用)
# cd /etc/freeradius/3.0/certs # rm -f *.pem *.der *.csr *.crt *.key *.p12 serial* index.txt*
生成根证书
# vi /etc/freeradius/3.0/certs/ca.cnf --------------------EDIT-------------------- [ CA_default ] default_days = 3600 [ req ] input_password = whateverCA output_password = whateverCA [certificate_authority] countryName = CN stateOrProvinceName = Jiangxi localityName = Pingxiang organizationName = Organization Name emailAddress = radius@example.com commonName = "XXXXXX Certificate Authority" -------------------------------------------- # make ca.pem # make ca.der
生成服务器证书(countryName, stateOrProvinceName, localityName 要与根证书一致??)
# vi /etc/freeradius/3.0/certs/server.cnf --------------------EDIT-------------------- [ CA_default ] default_days = 3600 [ req ] input_password = whateverSVR output_password = whateverSVR [server] countryName = CN stateOrProvinceName = Jiangxi localityName = Pingxiang organizationName = Organization Name emailAddress = radius@example.com commonName = "XXXXXX Server Certificate" -------------------------------------------- # make server.pem
修改 eap 配置以使用新创建的证书
# vi /etc/freeradius/3.0/mods-available/eap
--------------------EDIT--------------------
tls-config tls-common {
private_key_password = whateverSVR
private_key_file = ${cadir}/server.key
certificate_file = ${cadir}/server.pem
ca_file = ${cadir}/ca.pem
}
--------------------------------------------
# chown freerad:freerad server.key
# chown freerad:freerad server.pem
# chown freerad:freerad ca.pem
# systemctl restart freeradius
七、在 H3C 设备上配置 Radius 并启用(无线)802.1x
八、配置终端的 802.1x 访问
参考链接: https://schoolsysadmin.blogspot.com/2016/03/freeradius-production-ssl-certificates.html
将根CA文件(ca.der)公开,方便用户在终端设备上导入;
windows 下配置无线