RADIUS 返回 Access-Challenge 后 VPN 登录卡住
Access-Challenge 既不是成功,也不是最终拒绝。看看 NAS、网关和客户端如何必须在同一次交换中传递下一次响应。
密码是对的,VPN 登录却依然失败,而 RADIUS 服务器可能完全照做了它被要求做的事。网关很可能收到了一个 Access-Challenge,却没显示任何有用的东西,随后超时,或把那个应答当成拒绝。从客户端看,这像是凭据错误或隧道损坏。原因通常藏在一次谁也没抓包的对话中间。
一次登录,两次 RADIUS 交换
当后端需要一个第二因素时,它不会用接受或拒绝来回答第一个请求,而是返回代码 11,并等待用户的下一次响应;网关必须在后续请求里把它送回去。那次后续请求属于同一段认证对话,而不是一次新的登录尝试。
在正常配置下,用户会看到第二个提示:先密码,再一次性验证码;如果运维方有文档说明,也可能是一个合并提示。在异常配置下,第二个提示从不出现,登录就直接失败。
这就是要追的症状,而且只从客户端就能确认:如果 VPN 客户端只问一个秘密,随后却拒绝一个你明知正确的密码,那么这次交换很可能停在挑战那一步。
挑战实际携带了什么
RADIUS 规范在第 4.4 节描述了代码 11,并允许两个这里很关键的属性。Reply-Message 保存给用户看的文本,State 保存服务器希望原样回传的不透明值。NAS 不应解释 State,只需原样返回。
后续请求使用新的 Identifier 和新的 Request Authenticator,并且必须携带同一个 State 值以及用户的响应。服务器正是通过这个 State 把它与待处理的挑战匹配起来,所以重建或丢弃该属性的网关无法完成这套流程。每个应答里的 response authenticator 也必须校验通过,否则 NAS 应丢弃该数据包。
第 4.4 节补充的细节解释了大多数静默失败:不支持挑战的 NAS 会把 Access-Challenge 当作拒绝。如果你的网关的 VPN 服务早于这项支持、或者干脆没有,后端索取第二因素的请求就会变成没有任何提示的即时“认证失败”。
串联请求丢在哪里
第一个可疑点不是凭据,而是提示通路。为网页登录渲染 OTP 输入框的门户,不会自动为 VPN 服务也这样做;有些网关只把挑战传给声明具备该能力的客户端。用同一个账号在两个入口都试一次;如果门户可用而 VPN 不可用,你看到的是交接问题,不是令牌问题。
第二个可疑点是输入格式。不按厂商文档的分隔符把密码和验证码拼在一起,或者让已保存的凭据重放旧验证码,失败的样子和密码错误一模一样。请使用运维方文档里的分开提示或合并格式,绝不要自创分隔符。
与版本相关的缺陷确实存在,但多年前的已修复版本清单并不是今天部署的诊断。请阅读你实际运行的软件的发布说明,并把观察到的交换阶段留作证据。
把一次尝试从头追到尾
在开启服务端调试的情况下复现一次,并让其他所有会话都远离抓包窗口:
$ sudo radsniff -i eth0 -x -f 'udp port 1812'
$ sudo freeradius -X
$ radtest alice 'password' 127.0.0.1 0 testing123
在输出里看对话的形状,而不是只看一行。先是一个 Identifier 为 3 的 Access-Request,接着是携带 State 的 Access-Challenge,然后是一个 Identifier 为 4 的 Access-Request,它重复那个 State 并带上用户的响应。如果第二个请求缺失,网关从未跟进。如果它到达时没有 State,服务器无法与待处理挑战匹配,通常会拒绝。
分享抓包时请隐去凭据、令牌和不需要的身份数据,并把同一次尝试的事件关联起来,而不是混入多个浏览器标签页的重试。时间戳在这里很有用;客户端放弃之后才到达的挑战,对用户来说像拒绝,对日志来说像慢响应。
你想要的完成状态是一次走完并获授权的对话,随后是预期的服务访问。仅凭 RADIUS 服务器返回了一个应答,能证明的东西要少得多。