Mac VPN 与 PAC 代理:两个决策,而非一项设置
PAC 文件按请求选择代理,VPN 则选择路由。追踪下载、选择和套接字,别再把 DIRECT 当作离线。
你的 Mac 会先下载一份策略脚本,再向该脚本询问某个请求应使用哪个中间层,而隧道只决定如何到达被选中的跳点。两个答案落在同一个套接字上,因此当两者都成功之后仍然失败的请求,看上去像一处故障,其实是两处。把下载与选择分开、把选择与路由分开,就是这类工单的大部分工作。下面的顺序遵循 macOS 的实际做法,并给出让每个阶段可见的检查。
PAC 地址与代理在哪里分道扬镳
像 https://config.corp.example/proxy.pac 这样的地址指向一份要下载的文档,而那份文档指向要转发流量的服务器。把 URL 粘贴到代理主机字段,等于让客户端向一个从不监听此类连接的文档服务器发起代理连接。文档里有一个函数,客户端每个请求都会调用它,其返回值就是该单一目标的下一条跳点。这种间接性解释了为什么同一网络服务上的两台机器,即使都持有正确的 URL,也可能表现不同。它也解释了为什么策略变更无需任何人改动代理本身就能生效。
在解释下游任何内容之前先读脚本,因为其中的规则承载着组织的例外以及例外的生效顺序。靠前的宽范围可能遮蔽靠后的内部后缀,本该明显绕过代理的主机却一直用它。当有人坚称策略有错时,脚本是唯一能了结这一说法的地方。
脚本必须先被取到,才能做出任何决定
下载策略需要一个已解析的名称以及通往提供该文件者的路由,而一条已连接的隧道并不保证两者中的任何一个。如果主机只能通过私有 DNS 解析,那么当时使用的解析器就决定了下载能否开始。如果主机位于私有网段,路由必须从本机而非同事的笔记本出发。更早会话中缓存的副本会把这一切藏起来,直到缓存过期或机器重启。
按顺序执行下面三项检查,并在第一个失败处停下,因为这些阶段并不共享同一个原因。三者合起来说明策略是否已配置、是否可达以及它返回什么。
scutil --proxy
networksetup -getautoproxyurl "Wi-Fi"
curl -sS -o /dev/null -w "%{http_code}\n" https://config.corp.example/proxy.pac
第一条命令打印当前活动服务的代理字典,包括系统持有的任何自动配置 URL。第二条报告绑定到某个具名服务的 URL,这在会在 Wi-Fi 与 Ethernet 之间切换的笔记本上很重要。第三条在不渲染的情况下取回文件,因此 404、超时与解析失败仍可区分。如果最后一条命令能返回脚本,而应用仍然失败,那么问题已经越过了取回阶段。
DIRECT 答案究竟跳过了什么
PAC 规范给客户端一套很小的词汇,DIRECT 就是其中之一。这个词告诉客户端对该请求跳过代理,却只字不提套接字随后使用哪个接口。完整隧道上的直连套接字仍然经 VPN 出去,也仍然把隧道出口地址呈现给目标。
把 DIRECT 读成在 VPN 之外,就是把人引向错误设置面板的那处错误。镜像情形同样常见,因为可以经由分离路由访问私有代理,而机器的其余部分留在隧道之外。此时目标报告的地址属于代理自己的出口,可能位于另一个国家。为一个内部主机和一个公网主机写下预期,很快就能暴露这一点,因为脚本常常对两者给出不同答案。一旦知道是哪个跳点产生了该地址,报告就不再神秘。
以同一个错误页呈现的三种失败
策略可能下载失败,选中的代理可能不可达,而到达服务的请求仍可能在那里被拒绝。三者都以超时或通用错误页的形式出现,没有一个点出究竟断在哪一阶段。把决策与错误一起记录,才能把它们分开。下面两个探测针对脚本选中的跳点,每个分支各一个。
curl -v --max-time 8 http://proxy.corp.example:8080/
curl -v --max-time 8 https://intranet.corp.example/
到代理的连接一旦完成,问题就转向认证或代理自身的策略,那属于另一位负责人。到内部主机的连接在路径上没有代理地完成,就排除了路由,指向策略。两条命令都会忽略 macOS 的代理字典,除非 shell 导出代理变量,所以先清掉它们。两个探测谁也不能证明另一个,这就是一个错误页能误导这么久的原因。
随工单交付什么
发送活动网络服务、PAC URL、失败主机的求值结果以及错误出现的阶段。这四项让下一位能复现该决策,而不是猜测涉及哪一层。还要加上测试时刻的隧道状态,因为重连之前抓到的结果已经过时。也保留脚本版本,因为策略文件会在无通知的情况下变化,昨天的答案可能不再有效。
记录脚本取不到时客户端的行为,因为各实现并不相同,而代理决策并不是整台设备的急停开关。有些退回直连,有些则关闭失败,这个差别会改变一次中断看起来像是代理坏了,还是像是策略在正常工作。在事故中依赖它之前,请针对确切的客户端与版本确认该行为。