起因
家中的WiFi很早前已被笔者分成了多个网络进行隔离,包括主网络供自己用,设备网络供智能设备用,访客网络供朋友用,测试网络供测试一些服务时使用。其中访客网路同样也是采用WAP2-PSK
方式,但是一旦要换密码,应用时就会造成同AP上的其他网络中断,且也不方便利用程序定时更换。最近在考虑了一段时间后,决定采用企业级使用的RADIUS认证方式以实现需求。
分析
既然要使用RADIUS,那么必然离不开大名鼎鼎的软件FreeRADIUS,恰好笔者的主路由器是OpenWrt也支持其部署,那就没有问题只待安装配置了。接着考虑安全性和兼容性,在参考了一些文章后,最终决定采用PEAP-MSCHAPv2
认证方式进行部署。至于用户名和密码,则采用最基础的文件方式进行配置,这样只要定时用代码生成对应格式的文件再重启FreeRADIUS即可达到密码轮换的目的。
处理
在AP上配置非常简单,笔者用的是dd-wrt,安全模式选择WAP2-EAP
,WPA算法选择CCMP-128(AES)
,接着配置RADIUS服务器地址、端口、共享密钥就行。
在FreeRADIUS上配置则略显繁琐。首先在OpenWrt上安装freeradius3-default
、freeradius3-utils
两个包。接着进行配置,大部分采用默认值即可,相关涉及的配置如下:
/etc/freeradius3/radiusd.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| prefix = /usr exec_prefix = /usr sysconfdir = /etc localstatedir = /var sbindir = /usr/sbin logdir = /var/log raddbdir = /etc/freeradius3 radacctdir = /var/db/radacct name = radiusd confdir = ${raddbdir} modconfdir = ${confdir}/mods-config certdir = ${confdir}/certs cadir = ${confdir}/certs run_dir = ${localstatedir}/run/${name} db_dir = ${raddbdir} libdir = /usr/lib/freeradius3 pidfile = ${run_dir}/${name}.pid correct_escapes = true 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 { allow_core_dumps = no max_attributes = 200 reject_delay = 1 status_server = yes }
proxy_requests = no
$INCLUDE clients.conf
thread pool { start_servers = 1 max_servers = 3 min_spare_servers = 1 max_spare_servers = 3 max_requests_per_server = 0 auto_limit_acct = no }
modules { $INCLUDE mods-enabled/ }
instantiate {
}
policy { $INCLUDE policy.d/ }
$INCLUDE sites-enabled/
|
/etc/freeradius3/sites-enabled/default:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| server default { listen { type = auth ipaddr = 172.16.xxx.1 port = 0 limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } authorize { filter_username preprocess eap { ok = return } } authenticate { eap } 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: } remove_reply_message_if_eap Post-Auth-Type REJECT { attr_filter.access_reject eap remove_reply_message_if_eap } } }
|
/etc/freeradius3/sites-enabled/inner-tunnel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server inner-tunnel { authorize { eap { ok = return } files } authenticate { Auth-Type MS-CHAP { mschap } eap } }
|
/etc/freeradius3/clients.conf:
1 2 3 4
| client ap { ipaddr = 172.16.xxx.1/24 secret = xxxxxxxx }
|
/etc/freeradius3/mods-enabled/eap:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| eap { default_eap_type = peap timer_expire = 60 ignore_unknown_eap_types = no cisco_accounting_username_bug = no max_sessions = ${max_requests} tls-config tls-common { private_key_password = whatever private_key_file = ${certdir}/server.pem certificate_file = ${certdir}/server.pem ca_file = ${cadir}/ca.pem dh_file = ${certdir}/dh ca_path = ${cadir} cipher_list = "DEFAULT" cipher_server_preference = no tls_min_version = "1.0" tls_max_version = "1.3" ecdh_curve = "prime256v1" } peap { tls = tls-common default_eap_type = mschapv2 copy_request_to_tunnel = no use_tunneled_reply = no virtual_server = "inner-tunnel" } mschapv2 { } }
|
/etc/freeradius3/mods-enabled/files:
1 2
| Guest Cleartext-Password := "TEST" Reply-Message := "Hello, %{User-Name}"
|
在目录/etc/freeradius3/mods-enabled
保留如下文件:
1 2 3 4 5 6
| always attr_filter eap files mschap preprocess
|
配置成功后,用Python写了段代码,用于定时生成对应格式的文件,并重启FreeRADIUS服务。主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| try: file_path = "/etc/freeradius3/mods-config/files/authorize" with open(file_path, "w") as f: f.truncate(0) f.seek(0) today = datetime.date.today() username = "USERNAME" password = "PASSWORD{}".format(today.strftime("%m%d")) f.write('{username} Cleartext-Password := "{password}"\n'.format(username=username, password=password)) f.write(' Reply-Message := "Hello, %{User-Name}"\n') for i in range(7): username = "USERNAME-{}".format((today + datetime.timedelta(days=-i)).strftime("%m%d")) password = "PASSWORD-{}".format((today + datetime.timedelta(days=-i)).strftime("%m%d")) f.write('{username} Cleartext-Password := "{password}"\n'.format(username=username, password=password)) f.write(' Reply-Message := "Hello, %{User-Name}"\n') subprocess.run("/etc/init.d/radiusd restart", shell=True, check=True) _LOGGER.info("success") except Exception as e: _LOGGER.exception(e)
|
其密码更换策略目前为主密码一天一换,次密码保留一周,一周后过期。
结果
笔者手头上有的安卓手机(魅族)、iPad、Win10笔记本均成功的以该种方式连上了WiFi,证明配置基本完成。其中安卓手机选择添加网络后,再选择802.x EAP
,EAP方法选择PEAP
,阶段二身份验证选择MSCHAPV2
,输入密码和身份即可连接。而iPad添加网络时安全性选择WPA2企业级
,然后输入用户名、密码就行。针对Win10笔记本,配置就略显繁琐,具体的可以参见参考一节的链接文章,按详细步骤操作即可。
尾声
查阅相关资料发现,虽然在2020年6月二维码最著名的zxing库已经添加了该种方式的WiFi二维码格式,但是在2023年12月发布的WPA3 3.2规范第7章中并没有相关说明,所以各设备厂商对其的支持有待考证。就笔者手头上的几台旧设备而言,并不支持直接生成对应的WiFi二维码,且扫描按格式生成的二维码时,也不能成功连接对应的WiFi。
笔者手上的部分设备较老,故TLS最低版本只能配到1.0,即tls_min_version = "1.0"
,虽然在日志中看到了警告,但也没有别的办法了。
参考
- Windows 10 Wireless Setup
- zxing的Wi-Fi Network config (Android, iOS 11+)
- WPA3 Specification v3.2.pdf