Nginx报No required SSL certificate was sent:mTLS客户端证书排查

Nginx 开了双向 TLS,却出现 400 No required SSL certificate was sent。换 fullchain 也没用:被检查的可能是客户端。下面用 curl 区分"没带证书""带了但不可信""验错信任方向",不靠关闭验证掩盖故障。

一、先分清是谁在验证谁

普通 HTTPS 中,客户端验证服务器;mTLS 再增加服务器验证客户端。两条信任链不是同一个开关。curl --cacert 指定客户端信任的服务端 CA;--cert 与 --key 指定客户端证书和本地私钥(私钥不发送给服务器);Nginx 的 ssl_client_certificate 指向验证客户端证书的 CA 文件。

图中命令行参数属于 curl;"服务器身份"指客户端验证服务器的信任方向。CA 包不会凭空生成客户端身份,就像大楼照片不能当工牌。本文只讨论客户端到 Nginx 的一段,先确认谁终止 TLS。

二、这句 400 与 495、496 是什么关系

Nginx 官方文档区分两类内部错误:496 表示没有提交必需的客户端证书,495 表示客户端证书验证错误,均可供 error_page 处理。它们不等于浏览器默认一定收到的 HTTP 状态。

这次我在隔离回环环境使用 Nginx 1.26.3、curl 7.61.1,固定 TLS 1.2 做对照:必需证书缺失时,对外是 HTTP 400 和上述英文提示;换成另一 CA 签发的客户端证书,则是 HTTP 400 与 The SSL certificate error。不同版本、TLS 后端或网关可能表现为握手失败,不要只按浏览器文案下结论。

同时记录 HTTP、正文和退出码。不加 --fail 时,HTTP 400 仍可能对应 curl 退出 0;HTTP 000 表示没收到 HTTP 响应,不是服务器状态码。

三、先用最小配置确认验证边界

下面是实验用 server 片段,不可覆盖生产配置。准备服务端 fullchain、匹配私钥和客户端 CA 文件;路径为示意,8443 仅绑定回环。

nginx 复制代码
server {
    listen 127.0.0.1:8443 ssl;
    server_name mtls.test;
    ssl_certificate /path/to/server-fullchain.pem;
    ssl_certificate_key /path/to/server.key;
    ssl_client_certificate /path/to/client-ca.pem;
    ssl_verify_client on;
    ssl_verify_depth 1;
    location / {
        return 200 "verify=$ssl_client_verify\n";
    }
}

ssl_verify_depth 默认是 1,应结合实际中间证书链和版本验证,不是越大越保险,更不能修复信任了错误 CA 的问题。这里仅测试根 CA 直接签发的叶子证书,未验证多级中间 CA 的深度边界。诊断响应只用于回环实验,生产环境不对外暴露详细验证原因。

四、同一目标做三次 curl 对照

准备 PEM 格式的 client.crt、client.key 和 server-ca.crt。假定已启动 8443 实验监听,服务端 SAN 包含 mtls.test。

bash 复制代码
curl --noproxy '*' --http1.1 \
  --tlsv1.2 --tls-max 1.2 \
  --resolve mtls.test:8443:127.0.0.1 \
  --cacert server-ca.crt \
  --cert client.crt --key client.key \
  --include --write-out '\nhttp=%{http_code}\n' \
  https://mtls.test:8443/

第一次删掉 --cert/--key,看无身份对照;第二次带可信客户端证书;第三次换成另一 CA 签发的测试客户端证书。第三次不要改 --cacert,否则同时改变另一条信任链,排查又绕回去了。

--resolve 只替换连接地址,URL 保留主机名供 SNI 和名称校验使用;IP URL 加 Host 头并不等价。固定 TLS 1.2 是为了实验结果可比,两个 TLS 参数要一起理解:单独 --tlsv1.2 表示最低版本,不是最高版本。不要把实验限制照搬成生产策略。

五、optional 并不是"少检查一点也没事"

以下是本次隔离测试的真实结果。每个模式都更换三类客户端输入,保持服务端信任配置不变。

模式 不带客户端证书 可信证书 另一 CA 证书
on 400,缺少证书 200,SUCCESS 400,证书错误
optional 200,NONE 200,SUCCESS 400,证书错误
optional_no_ca 200,NONE 200,SUCCESS 200,但 FAILED

optional 允许不提交,已经提交的证书仍要验证;optional_no_ca 不要求证书由受信 CA 签发,官方用途是把实际验证交给外部服务,不是万能跳过所有错误。没有后续验证与授权,直接改这个开关,等于把门禁故障修成了门常开。

HTTP 200 同时伴随 FAILED:unable to verify the first certificate,直接说明应用成功不等于 mTLS 通过。

六、带了证书还失败,检查这几处

先查有效期、签发者和用途,再查链。文件名叫 client.crt 不代表它一定适合客户端认证;若证书限制了扩展用途,要确认包含 clientAuth。以下只读取公开证书,不打印私钥。

bash 复制代码
openssl x509 -in client.crt -noout -subject -issuer -dates
openssl x509 -in client.crt -noout -purpose
openssl verify -purpose sslclient \
  -CAfile client-ca.pem client.crt

若实际使用中间 CA,离线验证需要通过 -untrusted 提供对应中间证书;客户端发送的链也应齐全,不能靠只信任错误根来补救。检查 Nginx 实际读取的 CA 文件、文件权限及客户端证书与私钥是否配对。不要把私钥上传在线工具,也不要把完整证书、身份信息和调试日志随手贴到工单。

反向对照只换错 server-ca.crt,本次得到 curl 退出 60、HTTP 000。这是客户端不信任服务器,两个方向要分别修。

七、续期工具解决不了客户端授权

服务端证书更新与客户端身份管理要分开安排。中立比较三类资料与工具:Let's Encrypt 官方文档用于核对 ACME 签发与验证规则;acme.sh提供脚本化证书申请与安装流程;CertbotX提供平台化服务端证书生命周期管理。这里比较的是运维层次,不是客户端 PKI 或权限系统的替代方案。

服务端续期不会自动让客户端进入可信列表;认证通过也不等于允许访问任意租户。账号映射、资源权限、吊销与审计需另设规则。网关不能相信外部随意传来的"客户端身份"请求头。

八、验收:拒绝该拒绝的,也要写进测试

  • 固定目标域名、SNI、端口与 TLS 终止节点。
  • 不带证书应按策略拒绝,正确身份才能通过。
  • 错误 CA 的客户端证书不能被误放行。
  • 分别核对客户端与服务端的信任链。
  • 同时检查 HTTP、curl 退出码和 ssl_client_verify。
  • 证书认证后继续执行业务授权,不用 -k 或关闭验证掩盖问题。

技术依据:Nginx SSL 模块与curl 官方手册。本次实验仅使用临时 CA 和回环监听,测试后清理私钥及独立进程;没有修改生产配置或系统信任库。

相关推荐
志栋智能8 小时前
运维超自动化的版本控制与回滚机制
运维·自动化
中云DDoS CC防护蔡蔡8 小时前
DDOS和CC攻击谁更可怕
运维·服务器·经验分享·网络安全·ddos
Ruiery8 小时前
Linux 6.6内核内存管理深度解析(十六):mmap — 用户态“申请内存“的真相
linux·运维·服务器
ClickHouseDB8 小时前
ClickHouse Cloud 与 Snowflake 实时性价比对比:CostBench 测试中的 412 倍差距来
运维·数据库·系统架构
2501_915106328 小时前
抓包抓到乱码或压缩字节怎么解析,TraceEagle五种查看模式和一键解码
网络协议·计算机网络·网络安全·ios·adb·https·udp
欧米欧9 小时前
Linux系统篇(七),环境变量的底层溯源,核心获取机制与安全应用
linux·运维·服务器
Ruiery9 小时前
Linux 6.6内核内存管理深度解析(十八):KASLR 内核地址空间随机化
linux·运维·服务器·安全
todoitbo9 小时前
群晖NAS部署JmalCloud:Docker安装、文件管理、博客与公网访问完整教程
运维·docker·容器
时间售卖铺9 小时前
触发器与函数实战:MySQL高效编程秘诀
运维·数据库·mysql·云原生
前端精髓10 小时前
调用大模型API
运维·服务器·microsoft