如何测试 NO_PROXY 规则,防止流量绕过代理

NO_PROXY 是出口策略,不是无害的便利变量。一个过宽的域名后缀、通配符、大小写变量冲突,或运行时特有的解析方式,都可能让本应经过代理的请求直接访问目标。后果包括出口 IP 异常、审计证据缺失、区域结果不一致,甚至代理故障被静默直连掩盖。
下面是一套可重复的上线前测试方法。所有测试必须只针对已获授权的网络与端点。
先确认进程实际生效的配置
不要只看部署模板,要记录运行进程真正读取到的值:
- 大写与小写的代理环境变量;
- 命令行中的代理和绕过参数;
- 应用或 SDK 使用的代理 Agent;
- 容器、无服务器平台和服务管理器注入的环境;
- 操作系统自动发现的代理设置;
- 允许或阻止直连出口的网络策略。
修改环境变量后,应重启或回收测试实例。长时间运行的进程可能仍保留旧环境或旧连接池。
不要假设所有客户端都按同一方式解析
不同 HTTP 栈没有统一语义。当前 Node.js 内置代理支持在大小写变量同时存在时优先使用小写变量,并可处理精确主机、域名后缀、通配符、IP、IP 范围以及主机加端口规则;同时还需要通过文档规定的环境变量、命令行、全局配置或 Agent 显式启用。
curl 支持主机、域名、通配符和 CIDR。IPv6 数字地址不加方括号。libcurl 的空 no-proxy 选项表示所有主机重新使用代理,这与通配符“全部绕过代理”正好不同。
Python urllib 可从环境变量和操作系统发现代理;文档中的 no_proxy 使用逗号分隔的主机名后缀,并可附带端口。自定义 ProxyHandler 会改变或关闭这种自动发现。
因此,应把每种运行时及版本视为独立契约并逐一验证。
建立路由决策矩阵
准备两个自有测试端点:一个必须经过代理,一个明确获准直连。还要加入“不应命中”的反例,以便发现规则意外扩张。
| 测试项 | 策略意图 | 预期路由 |
|---|---|---|
| 精确主机名 | 仅一个内部服务 | 直连 |
| 同级主机名 | 不应继承精确匹配 | 代理 |
| 域名后缀 | 获批内部命名空间 | 直连 |
| 相似后缀 | 不得误匹配 | 代理 |
| 主机加端口 | 仅批准端口绕过 | 按端口区分 |
| IPv4 地址 | 单个批准端点 | 直连 |
| IPv4 CIDR/范围 | 客户端支持时使用 | 直连 |
| IPv6 地址 | 单个批准端点 | 直连 |
| IPv6 CIDR/范围 | 视客户端能力而定 | 客户端特定 |
| 通配符 | 仅隔离诊断 | 全部直连 |
| 空值 | 不设置绕过 | 代理 |
| 大小写冲突 | 明确优先级 | 客户端特定 |
同时测试空格、尾随点、国际化域名、Punycode、重定向、DNS 别名,以及在无关域名中包含相同字符串的情况。每次匹配都应当狭窄、明确且有记录。
用证据确认路径,不靠响应码猜测
HTTP 200 只能证明可达,不能证明经过了哪条路径。至少收集两种独立证据:
- 代理侧只会在请求经过代理时出现的合成请求标识;
- 目标侧观测到的已知代理出口或已知直连测试出口。
建议只记录别名和分类:
runtime_version
policy_hash
test_case
destination_alias
expected_route
proxy_observed
egress_class
status_class
latency_bucket
两侧证据不一致就应判定失败,不能用成功响应覆盖路由偏差。
在真实部署边界中测试
测试必须运行在生产所用的容器镜像、主机类型和启动流程内。本地终端可能存在不同的变量大小写、继承环境或系统代理设置。至少验证:
- 全新进程启动;
- 连接复用后的热进程;
- 配置变更并按要求重启;
- 代理不可用时不会静默直连;
- 从允许主机重定向到未允许主机;
- 适用场景下的 IPv4 与 IPv6 解析。
Node.js 应分别测试全局代理配置和应用自定义 Agent;curl 应比较环境变量与显式参数;Python 应确认自定义 opener 是否覆盖环境发现。
让意外直连以失败结束
稳健方案应使用短小、经审核的绕过清单,并在网络层阻止意外直连:
- 出站 Web 流量默认走代理;
- 仅使用精确主机或范围很窄的后缀例外;
- 生产持久配置禁止通配符;
- 启动时拒绝含糊或格式错误的条目;
- 对直连目标及端口设置允许清单;
- 重试次数有界且不得切换为直连;
- 在脱敏遥测中记录策略哈希;
- 分阶段上线并准备已知可用的回滚配置。
不要把代理凭证放在 URL、日志或测试夹具中。如果全局变量无法安全表达需求,应改用按目标显式路由。
上线检查清单
- [ ] 已获取大小写变量的实际生效值。
- [ ] 已记录运行时和客户端版本。
- [ ] 已测试精确主机、后缀、端口、IPv4 与 IPv6。
- [ ] 已识别不受支持的 CIDR 或范围语法。
- [ ] 已验证通配符和空值行为。
- [ ] 重定向不会逃离预期路由。
- [ ] 代理侧与目标侧证据一致。
- [ ] 代理故障不会触发直连降级。
- [ ] 网络层限制了直连出口。
- [ ] 日志不含凭证或敏感标识。
- [ ] 已测试金丝雀重启和回滚。
相关 98IP 内容还包括代理故障转移恢复演练、curl 代理认证 407 排错和代理凭证轮换。
常见问题
.example.internal 是否只匹配一个主机?
通常它表示后缀,而不是单一精确主机,但具体行为取决于客户端。应同时测试父域、子域、同级主机和相似域名。
* 能否作为临时默认值?
不安全。常见客户端会因此让所有目标绕过代理。仅在隔离诊断环境使用,并阻止它进入生产持久配置。
防火墙能替代应用测试吗?
不能。网络限制是重要的失败关闭控制,但应用层测试能在错误变成事故前发现配置偏差。
代理请求失败后是否应直接重试?
除非独立批准的策略明确允许,否则不应这样做。静默直连会改变身份、地域和审计范围,应使用有界代理重试并明确失败。
合规说明
仅测试自有或已获授权的基础设施与端点。绕过清单应限于合法业务需求,遵守目标服务条款和速率限制,尽量减少数据收集,不得利用路由控制规避访问限制或掩盖滥用行为。
资料说明:Node.js 项目 HTTP 文档;curl 项目 curl 手册与 CURLOPT_NOPROXY 文档;Python Software Foundation urllib 文档。查阅日期:2026 年 9 月 5 日。