为什么只靠 PPP CHAP 无法满足 MPPE 要求
要求 MPPE 的 PPTP 配置文件,在 PPP 落在普通 CHAP 上时就会失败。请按顺序检查认证方式、密钥来源和迁移。
坚持加密的 PPTP 配置文件通常在同一个地方失败:PPP 商定的认证交换从不产生 MPPE 所需的密钥材料。隧道看起来半死不活,密码被接受,链路在任何流量通过之前就断开了。在 CHAP 下拉菜单里反复尝试直到勉强连上,会掩盖真正的缺陷,而这个缺陷是认证方式与保护策略之间的一份契约。下面的检查按顺序排列,每一项都排除比下一项更省事的成因。
一条 PPTP 连接里跑着两场协商
PPTP 在 GRE 隧道里承载一个普通 PPP 会话,两者各自协商。外层决定数据包如何封装、存在哪条控制通道;内层 PPP 会话决定对端是谁、上面跑什么保护。因此密码提示只能证明内层的事,已完成的外层握手对两者都证明不了什么。配置文件在这里失败,是因为人们在调试隧道,而分歧却坐在 PPP 内部。
普通 CHAP 是 RFC 1994 中的质询—应答交换,只建立在共享密钥之上,别无其他。MS-CHAPv2 是另一种交换,消息不同、输出也不同,规范在 RFC 2759。两个名字里共有的那个词,是它们在链路上唯一的共同点。两个名字都没有说明交换会产出什么,而这恰恰是唯一要紧的部分。
所以只标着 CHAP 的选项在旧配置文件里是有歧义的。当服务器要 MS-CHAPv2、客户端却提供普通 CHAP 时,链路控制仍可能完成,密码也可能被接受。失败来得更晚,要等服务器试图从一个没有产出任何东西的交换里推导保护密钥。
把所需方式与它能产出的密钥对应起来
MPPE 密钥并不是 PPP 的一般属性。对 Microsoft CHAP 家族来说,推导方式规定在 RFC 3079 里,它以 MS-CHAP 交换的输出而不是共享密钥作为输入。EAP 方式走另一条路到达同一点,认证服务器把密钥作为 MS-MPPE-Recv-Key 和 MS-MPPE-Send-Key 属性返回。普通 CHAP 在两条路径上都不出现,这就是这种不匹配存在的全部原因。
要求 MPPE 却拒绝一切能提供密钥的方式,这样的配置内部自相矛盾。pppd 解决矛盾的办法是拒绝链路,而不是凭空造出密钥,所以可见的结果是一个通过认证、随后在网络阶段终止的会话。只要服务器还在拒绝唯一能推导出东西的方式,重试客户端就不会改变这一点。
由此得出的规则很短:如果配置文件要求 MPPE,方式就必须是 MS-CHAP 的变体,或者是服务器会返回其密钥的 EAP 方式。只拒绝 MS-CHAPv2 并不能强制这一点,因为 MS-CHAPv1 也会推导密钥,除非单独拒绝否则仍然可用;两者都拒绝就只剩普通 CHAP 和 PAP,它们什么都不推导,让要求变得无法满足。请明确写出接受的方式,而不是列出你不喜欢的方式。为了让客户端连上而放弃加密要求,是降级,不是修复。
编辑客户端之前先读服务器选项
这些不匹配大多能在服务器的两个文件里看到,读它们比重建客户端更省事。在 Linux PPTP 端点上,相关设置放在 pptpd 传下去的 pppd 选项文件里。要在服务器上运行,而不是看客户端状态界面。从那些要求、拒绝或指明保护模式的行开始:
sudo grep -nE 'require-|refuse-|mppe|mschap' /etc/ppp/options.pptpd把输出当成契约来读,而不是一列开关。要求 MPPE 却拒绝两种 MS-CHAP 变体的行,任何客户端都无法满足;缺少对 MS-CHAP 方式的要求,就会给不产生密钥的交换留下空子。然后再检查谁被允许认证,这由 secrets 文件决定:
sudo sed -n '1,20p' /etc/ppp/chap-secrets
sudo pppd --version改动之前先记下三个值:外层协议、服务器要求的认证方式,以及保护是强制还是可选。这三者决定之后每一个决定,而从客户端对话框里猜它们,正是配置文件最终只配了一半的原因。工作时把文件开着,因为下一节还要用这三个值。
按 PPP 阶段定位失败
PPP 按阶段报告失败,阶段告诉你契约的哪一条被破坏。从未离开链路控制的链路有封装或协商问题;认证通过随后死掉的链路,通常是策略或密钥问题。一直在线却什么都不承载的链路已经进入路由,那是另一项排查。
日志保留阶段行,它们比调高 debug 级别更有用。请求 PPP 进程和保护相关消息,然后先读认证结果,再看别的。最后几行通常点出拒绝的那一条:
sudo journalctl -b --no-pager | grep -iE 'pppd|pptpd|mppe|chap' | tail -40在这段输出里,先找认证结果,再找“要求了保护却没有协商”的抱怨。第二条消息就是上面那个矛盾的签名,只要服务器还拒绝唯一能产生密钥的方式,重试多少次都清不掉它。有两个陷阱值得点名:不看阶段行就提高 debug 级别只会制造噪音;抓取质询材料或密码,则把配置任务变成了凭据泄露。改动任何选项之前,先记下那个阶段。
在旧端点关掉之前规划退路
MPPE 是历史上的链路保护机制,它的存在并不能让 PPTP 成为新部署的合理选择。微软现行指引已经建议新的远程访问部署不要使用 PPTP,也不要使用带预共享密钥的 L2TP/IPsec。让旧端点继续活着,是对既有服务的决定,不是给下一个的模板。
端点必须留下时,把契约写下来:外层协议、要求的认证方式、强制保护,以及协商它的客户端版本。可以走时,在碰旧配置文件之前先选定替代方案网关的身份、它的客户端认证,以及凭据放在哪里。然后规划路由、DNS,以及每个用户被授权访问的资源。要按所有者的要求来验证目标客户端,而不是按一条能连通的连接来验证。
有意的拒绝应当保持为拒绝,因为为了让测试通过而退回无保护模式,会把策略失败变成暴露。完成状态是一份受支持、有文档的契约,而不是让一条旧连接成功的最小改动集合。在替代方案通过同样的检查之前,把旧配置文件留在原处,不使用,并记录下来。