curl用IP测试HTTPS报证书不匹配:--resolve与Host头别混用

切换DNS前用IP测试HTTPS,补Host头仍报证书不匹配?命令可能换掉了"要验证谁"。本文用回环实验拆开连接地址、TLS身份和HTTP路由,验证--resolve与--connect-to。

一、Host头管不到前面的TLS握手

访问HTTPS时,客户端先建立连接并完成TLS协商,再发送加密的HTTP请求。HTTP/1.1的Host头属于后面的应用层;给它换个值,不会把URL里的IP变成TLS要验证的域名。

对本文的普通HTTPS直连,curl从URL取得SNI域名和证书验证名。URL是IP时,名称验证对象就是该IP;本机实验未发送域名SNI。仅含DNS SAN的api.example.test证书不能匹配127.0.0.1,补Host头也不行。若服务确实要通过IP访问,应使用包含对应iPAddress SAN的证书,并单独验收,不要把DNS SAN写成IP字符串来代替。

二、先确认你究竟想测哪条路径

"测试指定节点上的域名站点"与"测试IP地址本身的HTTPS身份"不是同一件事。前者应保留域名URL,仅替换连接目标;后者才用IP URL。若绕过CDN直接连源站,结果也只证明源站这条路径,不代表公网边缘节点已经完成换证。

记录curl版本、TLS后端、目标节点与SAN。server.pem是测试服务端证书,root.pem是经核验的实验根证书,均不含私钥;未知服务器证书不能直接当可信根。

bash 复制代码
curl -V
openssl x509 -in server.pem -noout -subject -issuer -ext subjectAltName

示例假定隔离TLS服务监听127.0.0.1:8443,使用api.example.test证书。没有实验服务会连接失败;测实际节点时,替换为获授权的域名、地址和端口。

三、先重现IP加Host的反例

bash 复制代码
# 反例:证书仅有 DNS:api.example.test,没有 IP SAN
curl -q --http1.1 --noproxy '*' --cacert root.pem --max-time 10 \
  -H 'Host: api.example.test' https://127.0.0.1:8443/

本次curl 7.61.1、OpenSSL后端返回51:no alternative certificate subject name matches target host name;服务端未收到HTTP请求。新版错误码可能不同,须保留版本及完整错误,不能只看数字。

-q放在第一个参数,避免默认curlrc偷偷加入其他选项;--noproxy '*'限定本实验不经过环境代理。它不是生产环境绕开网络策略的建议。--max-time限制整次请求耗时,避免测试脚本无限等候。不要加-k把故意失败的反例变成"看似成功"。

四、固定IP但保留域名:用resolve

bash 复制代码
curl -q --http1.1 --noproxy '*' --cacert root.pem --max-time 10 \
  --resolve api.example.test:8443:127.0.0.1 \
  --write-out '\npeer=%{remote_ip} http=%{http_code}\n' \
  https://api.example.test:8443/

--resolve按"主机名:端口:地址"向本次curl的DNS缓存提供记录,不改系统DNS或hosts。此时连接127.0.0.1,URL仍是api.example.test,域名SNI与证书验证名都保留。实测返回HTTP 200,SNI为该域名,Host保留URL中的测试端口。

端口必须匹配URL的有效端口:上述URL显式用8443,映射就不能写443;普通https://域名/默认才是443。逐个节点独立测试并记录remote_ip,别把200当作测遍集群。remote_ip只是连接对端,不能单独证明证书、业务身份或公网路由正确。

五、还要改连接端口:用connect-to

如果URL要保持默认443,而隔离节点实际监听8443,可用"原主机:原端口:目标主机:目标端口"。该选项改变建立网络连接的目标,不改变URL身份、SNI或证书验证名。

bash 复制代码
if curl -q --http1.1 --noproxy '*' --cacert root.pem --max-time 10 \
  --connect-to api.example.test:443:127.0.0.1:8443 \
  --fail --output response.txt https://api.example.test/; then
  printf 'TLS and HTTP status check passed\n'
else
  rc=$?
  printf 'request failed: %s\n' "$rc" >&2
  exit "$rc"
fi

实测以随机回环端口替换示例8443,SNI仍是api.example.test,默认HTTP Host为api.example.test,而不是目标IP或8443。--resolve提供地址映射,不负责把443转到8443;两者不要混写。--fail使本文的HTTP 503负例返回非零,但成功分支不等于业务响应内容正确,仍须检查response.txt;认证场景须另验HTTP结果。

六、用负例确认没有关闭验证

隔离输入 本机7.61.1观察
IP URL加域名Host curl 51,无HTTP请求
域名URL加正确resolve curl 0,HTTP 200
正确resolve但换无关根 curl 60,信任验证失败
wrong.example.test映射同一节点 curl 51,名称验证失败
connect-to改连接端口 curl 0,SNI与URL域名不变
resolve后另改Host TLS仍通过,HTTP收到另一个Host

最后一行很重要:它说明HTTP路由可以与TLS身份不同,并不说明请求仍落到原站点。多租户服务应联合核对证书、SNI、Host及业务内容。实验服务只回显Host,不模拟完整Nginx虚拟主机选择,不能把回显成功写成生产串站验证通过。

七、代理和重定向别混入结论

本文显式使用HTTP/1.1、独立进程直连,无代理、自动跳转或连接复用;HTTP/2的对应字段为:authority,不作为本次实测。代理可能改变解析和连接位置,须先核对网络路径。加-L跳到其他域名或端口时,原resolve条目未必匹配;先检查Location,再为获授权目标增加独立映射。

调试日志可能含Cookie、Authorization和业务数据,分享前要脱敏。本文在临时目录生成测试CA与证书,监听只限回环,逐段重放正文命令;没有修改系统信任库、DNS或生产服务,测试结束清理进程与临时密钥。

八、提交验收记录前核对五件事

  • URL写的是预期身份,而不是为了方便换成IP。
  • 连接IP和端口确实是待测节点,映射键与URL匹配。
  • 保留证书验证,正确CA通过,错误名称和错误CA失败。
  • 分别记录SNI、HTTP Host、退出码、HTTP状态和业务结果。
  • 说明直连、代理、跳转及测试范围,不把单节点成功当全网成功。

参考:curl命令手册;everything curl:名称解析技巧;RFC 9525:TLS服务身份。

相关推荐
Hao2851 小时前
Windows11 下把 WSL2 Ubuntu 26.04 装到 D 盘完整
linux
Android系统攻城狮1 小时前
Linux Gstreamer深度解析之gst_audio_encoder_get_frame_samples_min调用流程与实战(六十)
linux·运维·服务器·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶
_upupup2 小时前
Linux中的自动化构建——make/Makefile
linux·运维·自动化
HEJOO92 小时前
Linux 设备驱动系列——使用全局工作队列
java·linux·算法
slimer3 小时前
07.进制转换上篇:位权展开与十进制互转
linux
顺风尿一寸3 小时前
手写迷你 Docker:用 Namespace + Cgroup v2 实现一个简单容器
linux
一木 之林3 小时前
DeepSeek Agent 开发(三)
java·大数据·linux
大猫会长4 小时前
调用codebuddy集成的supabase授权白屏解决方法
linux·运维·服务器
羔羊++5 小时前
29_实验二十八_buildroot配置与编译
linux