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 和回环监听,测试后清理私钥及独立进程;没有修改生产配置或系统信任库。

相关推荐
山岚的运维笔记1 小时前
mysql 专业笔记 -- 第 36 章:MySQL 管理
运维·数据库·笔记·后端·mysql·oracle·dba
DFT计算杂谈2 小时前
TRIQS 4.0.2 完整安装与使用指南
运维
蓝速科技3 小时前
商用复杂环境翻译机耐用性选型指南丨蓝速科技
大数据·运维·数据库·人工智能·科技
yt004yt3 小时前
工业园区绿岛 VOCs 废气治理管网设计与能碳节能设计经验分享
大数据·运维
starzy19903 小时前
Flink Sliding Window 详解及代码实现:从窗口重叠到状态爆炸防控
运维·数据库·flink
志栋智能5 小时前
集成是关键:让巡检超自动化融入现有工具链
运维·服务器·数据库·架构·自动化
AI智能从业者5 小时前
数码家电客服机器人怎么选?客户问参数问题机器人能不能接住
运维·人工智能·自动化
捷烽6 小时前
光纤熔接机是什么?工作原理与马达结构(2026)
运维·网络·信息与通信
当下新鲜事6 小时前
空调机房水泵常见问题解答:赛莱默B&G冷冻泵与冷却泵技术说明
大数据·运维·物联网·业界资讯