curl走HTTPS代理报SSL证书错误:proxy-cacert与cacert双层信任排查

直连HTTPS正常,一加代理就报curl: (60) SSL certificate problem;明明传了--cacert,错误却没消失。先别急着把证书全塞进一个文件:如果代理地址以https://开头,这条请求可能先后验证代理和目标站两张证书。本文用双CA实验区分信任与CONNECT边界。

一、先看代理URL,不要只看目标URL

访问https://目标站,不代表连接代理时也使用TLS。http://代理通常先收明文CONNECT,再转发隧道内的目标TLS;https://代理则先建立外层TLS,随后CONNECT,再在隧道里验证目标站。

代理像转车站:买对目的地的票,不代表已通过入口。本文仅讨论普通CONNECT;TLS拦截会改变目标层实际签发链,应另按组织授权核对。

二、确认curl构建与生效环境

先保存curl -V的完整输出,检查TLS后端以及Features是否包含HTTPS-proxy。HTTPS代理支持与--proxy-cacert在curl 7.52.0加入,但最低版本号不保证当前构建启用了所需后端能力。

bash 复制代码
curl -q -V
[ -z "${HTTPS_PROXY+x}" ] || printf '%s\n' 'HTTPS_PROXY已设置:在本机核对,不打印凭据'
[ -z "${NO_PROXY+x}" ] || printf '%s\n' 'NO_PROXY已设置:核对是否绕过代理'

-q须放首个参数,只禁用默认curl配置,不清除环境。上面不打印代理URL,避免泄露口令。还应核对小写https_proxy、all_proxy、大写形式及CA变量,不把完整环境贴到工单。

本轮测试前清除了大小写代理、CA环境变量,并显式传--noproxy空串,防止localhost被NO_PROXY绕过。生产排障前先确认路由授权:强制走代理会改变流量去向,不要把隔离实验参数无条件铺到所有请求。

三、两套CA分别交给两个参数

以下域名和端口是占位值。proxy-ca.pem为获准的代理CA,target-ca.pem为目标CA。信任来源须独立核验,不从报错连接随手下载后直接信任。

bash 复制代码
curl -q --noproxy '' --proxy https://proxy.example.test:8443 \
  --proxy-cacert proxy-ca.pem --cacert target-ca.pem \
  --connect-timeout 5 --max-time 15 --verbose \
  https://app.example.test/

--proxy-cacert服务于HTTPS代理的证书验证,--cacert服务于目标服务器验证。两者配置CA信任,不关闭名称检查。代理URL中的名称要匹配代理证书,目标URL中的名称要匹配目标证书;仅换CA不能修复名字不匹配。

本轮双CA互不相关,代理证书匹配127.0.0.1,目标匹配localhost。只给目标CA或对调CA,均先在代理层失败。两层若同由获准CA签发,可引用同一文件,但参数职责不合并。

四、用CONNECT事件定位,不靠错误60猜层级

本轮curl 7.61.1、OpenSSL 1.1.1k构建中,两层错误CA都返回60。代理与目标事件才把它们拆开:前者没有CONNECT,后者已有CONNECT却没有目标HTTP请求。相同的unable to get local issuer certificate不能定位层级。

隔离条件 退出码 / CONNECT 目标HTTP请求
两套CA都正确 0 / 1次 1次,HTTP 200
代理CA错误 60 / 0次 0次
目标CA错误 60 / 1次 0次
只给目标--cacert 60 / 0次 0次
普通HTTP代理,目标CA正确 0 / 1次 1次,HTTP 200

CONNECT的200只表示代理建立了隧道,不是目标站返回了业务成功。真实故障还可能是407认证、CONNECT被拒、端口不可达或超时,不能把"没有CONNECT"一律归因证书。--verbose日志用于分段定位,分享前需脱敏认证头、Cookie、URL参数和内部名称。

五、HTTPS_PROXY变量名不等于HTTPS代理

HTTPS_PROXY描述的是HTTPS目标请求使用哪个代理;变量值里的scheme才指定连接代理的方式。下面显式配置的是http://代理,因而没有外层代理TLS,仍需验证隧道内的目标证书。

bash 复制代码
HTTPS_PROXY=http://proxy.example.test:8080 \
  curl -q --noproxy '' --cacert target-ca.pem \
  --connect-timeout 5 --max-time 15 https://app.example.test/

本轮同样命令经普通HTTP CONNECT成功,代理端没有TLS建立事件,目标仍有TLS与HTTP请求。不要为了让错误消失,就把本来要求https://的代理改成http://;这会改变客户端到代理的保护方式,也可能暴露代理认证信息。正确做法是修复所要求的那层信任。

六、脚本保留失败,不用跳过验证当修复

在任务专用、可信且不被其他用户写入的目录运行下例,避免日志和响应文件覆盖业务文件或跟随不可信软链接。该片段不依赖本文内部实验脚本;替换占位地址与获准CA后即可用于请求自检。

bash 复制代码
umask 077
rc=0
curl -q --noproxy '' --proxy https://proxy.example.test:8443 \
  --proxy-cacert proxy-ca.pem --cacert target-ca.pem \
  --connect-timeout 5 --max-time 15 --fail --silent --show-error \
  --output response.txt https://app.example.test/ \
  >curl.log 2>&1 || rc=$?
[ "$rc" -eq 0 ] || exit "$rc"
printf '%s\n' 'TLS验证及本次HTTP请求通过,业务内容仍需核对'

本轮实际重放了正文命令,并覆盖正确双CA、两层错误CA、日志路径不可写及响应输出失败:失败分支不出现成功提示。--fail在此阻断HTTP错误响应,不保证返回内容就是正确业务结果;业务字段、状态与副作用仍需另验。

另外只在隔离负对照中验证:-k不能跳过代理证书验证,--proxy-insecure不能跳过目标证书验证。它们不是修复建议,更不能两项一起打开后宣布恢复。让门卫离岗,不是补好了证件。

七、验收清单与实验边界

实验只监听回环随机端口,代理只准CONNECT到本次目标端口,未创建开放代理;临时凭据、线程和监听均已清理,未修改系统CA、生产代理或服务。Python服务端TLS库与curl客户端TLS库分别记录,不能把模拟端版本当成curl版本。其他构建需重跑正反例。

  • 代理URL、实际路由和是否TLS拦截已确认,未被NO_PROXY意外绕过。
  • 代理与目标的信任来源、名称匹配分别通过,未关闭验证。
  • CONNECT响应与目标HTTP响应分开记录,错误码、日志和退出码一致。
  • 脚本失败关闭,真实代理认证、目标业务和多节点另行验收。

八、参考资料

相关推荐
星恒随风1 小时前
Linux进程间通信(二):深入理解System V共享内存、消息队列、信号量与内核IPC资源管理
linux·运维·服务器·笔记·学习·状态模式
IMPYLH1 小时前
Linux 文件检索:find、locate、grep 与 which 实战指南
linux·运维·服务器
脚踏实地,坚持不懈!2 小时前
Linux 内存回收机制:水位线、kswapd 与 Direct Reclaim
linux·jvm·算法
SKH.2 小时前
Linux驱动-key、中断
linux·运维·服务器
小龙报2 小时前
【零到全栈】从扩展名到终端实操,打通 Linux 基础
linux·ubuntu·云原生·职场和发展·创业创新·学习方法·程序员创富
星恒随风2 小时前
Linux进程间通信(一):从匿名管道、命名管道到进程池,深入理解Pipe、FIFO与文件描述符
linux·运维·笔记·学习·状态模式
wuminyu3 小时前
ZGC染色指针在虚拟线程stackChunkOop上的并发屏障全景分析
java·linux·c语言·jvm·c++
硅基手札3 小时前
【I2C 技术系列 11】I2C Linux 驱动框架
linux·驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
imDwAaY3 小时前
程序里的地址是真实内存地址吗?从虚拟内存到地址转换
java·linux·服务器