一份配置跑通 Caddy 选择性 mTLS:按 IP 定要不要客户端证书
【免费下载链接】caddyFast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS项目地址: https://gitcode.com/GitHub_Trending/ca/caddy
内网微服务要求双向 TLS(mTLS,Mutual TLS),同一个域名又对公网开放——如果强制所有访客出示客户端证书,正常用户会在握手阶段被直接拦下。Caddy 对此的解法就是选择性 mTLS:靠几条条件规则,按来源 IP 或 SNI(Server Name Indication)决定验不验客户端证书。下面给出一份可直接落地的 Caddyfile,配 curl 实测和一份踩坑速查,读完你就能把这个条件化双向认证模式搬到自己那套内网入口上。
先搞懂——“双向”到底在互验什么
把这事想成进大楼:刷工牌进门的动作其实是两次验证。服务器先亮出“营业执照”(服务器证书),客户端确认它确实是自己要找的那栋楼;然后服务器反过来查你的“工牌”(客户端证书)——查工牌这一步,就是 mTLS 多出来的那半边。
| 维度 | 全量双向认证 | 选择性 mTLS |
|---|---|---|
| 认证覆盖范围 | 每条连接都查工牌 | 只对命中条件的连接查 |
| 典型场景 | 纯内网服务网格 | 同一域名,内外网流量混跑 |
| 配置复杂度 | 一套信任池全局生效 | 策略 + 匹配器分层写,Caddy 一处搞定 |
Caddy 里做这件事靠两样东西:连接策略connection_policy,以及它内部的match匹配器(sni、remote_ip、sni_regexp等,定义在modules/caddytls/matchers.go)。策略按书写顺序逐个尝试,先命中的生效——这个顺序细节是后面一半坑的根源,先记住。
跑之前的两分钟准备
| 项目 | 要求 |
|---|---|
| Caddy 版本 | 2.6 及以上 |
| CA 根证书 | 一个 PEM/DER 文件,放到/etc/caddy/(自签或企业 CA 均可) |
| 编辑器 | 任意,如 VS Code |
最小化准备就一条命令:
cp 你的ca根证书.pem /etc/caddy/gw-ca.pem怎么自建 CA 这里不展开,任何你信任的 CA 根证书文件都行。
一份完整可跑的 Caddyfile:client_auth 一次配齐
https://gw.corp.example { respond "服务在线,身份未核验" # 特例放最前:内网段一律验证书 connection_policy { match { remote_ip 10.20.0.0/16 # 示例内网段 } client_auth { mode require_and_verify # 没证书或证书无效,握手直接拒 trust_pool file { pem_file /etc/caddy/gw-ca.pem } } } # 默认兜底:只问不验,不挡外网 connection_policy { client_auth { mode request # 询问客户端证书,可有可无 trust_pool file { pem_file /etc/caddy/gw-ca.pem } } } }这行是整篇的命门:mode require_and_verify。如果只写require,客户端必须交证书但服务器不验签名,伪造的证书你拦不住,生产环境别用它。默认策略故意用request而不是require——没有证书的外网流量照样走完握手,所谓“条件化”就是松的这一侧撑起来的。想按域名区分而不是按 IP,把match里的remote_ip换成sni gw.corp.example即可,写法一致;mode 一共四种(request / require / verify_if_given / require_and_verify),松一档紧一档,按需取用。
配置语法可对照仓库里的集成测试用例caddytest/integration/caddyfile_adapt/tls_client_auth_cert_file.caddyfiletest,connection_policy的解析逻辑在modules/caddytls/connpolicy.go,拿不准时翻源码最稳。
动手验证——三组 curl 确认配对了
# ① 内网主机(10.20.x.x),不带证书 curl -v https://gw.corp.example # ② 同内网主机,带有效客户端证书 curl --cert ./ops.crt --key ./ops.key \ --cacert /etc/caddy/gw-ca.pem https://gw.corp.example # ③ 白名单外主机(公网出口),不带证书 curl -v https://gw.corp.example| 命令 | 预期现象 | 不符时查什么 |
|---|---|---|
| ① 内网无证书 | 握手失败,报证书验证类错误 | connection_policy 顺序,是否误落默认策略 |
| ② 内网带证书 | 200,返回正常响应体 | 服务器侧 CA 文件是否就是签发它的那份 |
| ③ 外网无证书 | 200,握手成功 | 默认策略 mode 是否真是 request |
跑之前先来一句caddy adapt --config Caddyfile --pretty做语法自检,它都过不了就别急着 curl。
踩坑速查表
| 现象 | 根因 | 解法 |
|---|---|---|
| 内网带证书仍被拒,报 unknown CA | CA 路径写错,或放的不是签发 CA | openssl x509 -in /etc/caddy/gw-ca.pem -noout -subject核对指纹 |
| 客户端本地验证都过、服务器上不行 | 证书链不完整,缺中间证书 | 重新导出包含完整链的客户端证书 |
| 同一份证书,昨天能通今天不行 | 系统时间漂移,证书被判过期或未生效 | 同步 NTP 时间 |
| 内网流量没被验证书,行为像没配 | 无 match 的默认策略写在前面,先命中兜底了 | 带 match 的策略前置,默认策略放最后 |
再往前一步:让选择性 mTLS 自己跑起来
- 以后如果想把客户端 CA 的签发、续期也自动化,可以翻
modules/caddypki/里的 PKI 模块。 - 以后如果匹配规则复杂到 Caddyfile 写不下,可以看
modules/caddyhttp/celmatcher.go的 CEL 表达式匹配器。 - 以后想审计谁在反复握手失败,把
modules/logging/和modules/metrics/挂上,TLS 失败就能进日志和看板。
走到这里,你只需要记住一句话:特例策略在前、默认策略殿后,内紧外松。哪台机器握手莫名其妙被拒,把caddy adapt的输出贴到评论区,咱们一起拆。
【免费下载链接】caddyFast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS项目地址: https://gitcode.com/GitHub_Trending/ca/caddy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考