Windows 上的 L2TP/IPsec 错误 789:创建配置文件并固定提案
错误 789 失败在 IPsec 交换阶段,而不是密码。用脚本创建 L2TP 配置文件,固定提案,并按阶段阅读后续代码。
Windows 上 L2TP 连接出现错误 789,意思是隧道在 IPsec 安全交换内部就已经失效,而这远早于 Windows 把用户密码发往任何地方。重试密码,或者在目录里重置它,都无法推进这个阶段。真正能推进的,是用 PowerShell 按网关实际提供的提案重建连接,因为“设置”里的向导会悄悄选择 IPsec 变换,却从不告诉你它选了什么。这就是对应的操作手册:创建配置文件,固定提案,然后用一张阶段表,让下一个代码能告诉你一些信息。
不使用向导创建配置文件
Add-VpnConnection 会写入原生电话簿条目,因此结果与“设置”应用之后编辑的是同一个对象。当连接需要对机器上每个账户都存在时,请从提升的会话运行;当它应保持私有时,请从用户上下文运行:
Add-VpnConnection -Name "Contoso L2TP" `
-ServerAddress 203.0.113.10 `
-TunnelType L2tp `
-EncryptionLevel Required `
-AuthenticationMethod MSChapv2 `
-RememberCredential -Force
让 -AuthenticationMethod 与网关提供的方式一致。MSChapv2、EAP 和 PAP 不能互换,而要求服务器不会接受的方法的配置文件,会在查询任何用户记录之前就失败。任何正式环境都要保留 -EncryptionLevel Required;Optional 允许 Windows 在对端提供时接受未加密隧道,这是实验室里的捷径,不是可以上线的设置。
如果传入 -L2tpPsk,那把预共享密钥会被写进磁盘上的配置文件,任何能读到该机器上配置文件的人都能把它恢复出来。当网关支持时,请用证书做对端认证;当 PSK 无法避免时,把它挡在仓库和 shell 历史之外,而不是粘贴进每一次预置运行。
固定提案,而不是接受默认值
Windows 会自己填充 IPsec 变换,所以两台看起来配置相同的机器,面对同一个网关却可能以不同方式失败。Set-VpnConnectionIPsecConfiguration 是固定这些变换的受支持方法:
Set-VpnConnectionIPsecConfiguration -ConnectionName "Contoso L2TP" `
-AuthenticationTransformConstants SHA256128 `
-CipherTransformConstants AES128 `
-EncryptionMethod AES128 `
-IntegrityCheckMethod SHA256 `
-DHGroup Group14 `
-PfsGroup None `
-PassThru -Force
那套组合只是一种常见的 L2TP/IPsec 安排,并非通用答案。每个参数只接受固定的取值列表,平台无法映射的组合会以 cmdlet 错误返回,而不是悄悄把配置文件削弱。请把这种错误看作关于取值的说明,而不是关于你权限的说明。
从网关文档或它当前运行的阶段 1 和阶段 2 设置中复制提案,并且只在网关记录被拒绝的变换时,一次只改一个字段。把五个字段挨个试一遍,只会得到一条碰巧能用的连接,以及一份对下一个人什么也解释不了的记录。
按阶段读故障,而不是按错误文字
789 属于 在客户端对话框里看起来都一样的一族代码。再次改动配置文件之前,先把代码对应到它所属的阶段:
| What you see | Stage that failed | Check first |
|---|---|---|
| Nothing happens, then a timeout | No IKE response at all | Server address, UDP 500 and 4500 reachability, NAT traversal on the gateway |
| Error 789 | IKE security exchange | Proposal, pre-shared key, certificate trust and client clock |
| Error 691 or a repeated password prompt | User authentication | Account state, domain, RADIUS or NPS policy |
| Tunnel up, some hosts unreachable | Routing or DNS after PPP | Split-tunnel routes, pushed DNS, remote subnet mask |
这里凭据的两种角色都很重要。预共享密钥或证书用于双方互相认证,而 MSChapv2 或 EAP 用于在隧道内部认证你。重置用户密码修不好前者,新的共享密钥修不好后者,而同时改动两者会毁掉“到底哪一个错了”的证据。
在证书路径上,先检查客户端时钟、信任链和网关期望的身份,再考虑放宽校验。关闭校验会把一个看得见的 789 变成一条连接成功却信任了错误对端的隧道,而客户端里没有任何东西会告诉你这件事发生过。Microsoft 的远程访问错误列表 是这些代码本身的参考,它把安全层的失败与登录失败区分开。
在宣称修好之前,从两端都做验证
把配置文件读回来,但不要指望输出里能看到提案。Get-VpnConnection 会报告名称、服务器地址、隧道类型、加密级别和认证方法,它对变换一无所知,所以读回只能确认条目存在并指向正确的主机:
Get-VpnConnection -Name "Contoso L2TP" |
Select-Object Name, ServerAddress, TunnelType, EncryptionLevel, AuthenticationMethod
从命令行拨号,这样拿到的是数字而不是对话框:rasdial "Contoso L2TP" 会返回与界面相同的失败代码,rasdial "Contoso L2TP" /disconnect 则在两次尝试之间把它清掉。然后针对同一次尝试去读网关一侧。strongSwan 的 ipsec statusall 或路由器的 IKE 日志会说明到达的是哪个提案、被拒绝的又是哪个,而把它与 cmdlet 的取值对比,是唯一真正能证明两端达成一致的步骤。
连接成功不是终点。打开用户真正关心的应用,因为路由、推送的 DNS 和拆分隧道设置在隧道起来之后才会失败,而且根本不弹出任何错误对话框。
把它自动化,并且干净地回滚
无人值守安装正是要写脚本的原因,而拨号这一步正是凭据泄漏的地方。rasdial 会把凭据放在自己的命令行上,其他本地用户和大多数日志收集器都能读到:
subprocess.run(["rasdial", "Contoso L2TP"], check=True)
优先使用限定在运行任务的那个账户上的凭据,或者让配置文件用 -RememberCredential 保存它,而不是在计划任务里把密码当参数传进去。预置命令本身用 -Force 重复执行是安全的,但它们会乐于覆盖掉旧提案,所以只有脚本携带当前取值时,重跑才有帮助。
配置文件会跨重启保留,并且比它们当初对应的网关配置活得更久。当网关提案变化时,请删掉条目重新创建,而不是在上面再叠一套取值:Remove-VpnConnection -Name "Contoso L2TP" -Force,然后用新的一套重复那两条 cmdlet。
用脚本旁边放着的四项检查来闭环:Get-VpnConnection 显示预期的隧道类型和加密级别;网关为你的客户端地址记录阶段 1 和阶段 2 已完成;rasdial 返回成功并且应用能打开;脚本里记录的提案与当前网关设置一致。下次再遇到 789 时,这次对比只需要几分钟,而不是一个下午。