Caddy 选择性 mTLS:内网 API 强制双向认证,公网访问零影响
2026/9/8 15:07:27 网站建设 项目流程

Caddy 选择性 mTLS:内网 API 强制双向认证,公网访问零影响

【免费下载链接】caddyFast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS项目地址: https://gitcode.com/GitHub_Trending/ca/caddy

安全团队要求 API 域名启用强制双向认证,但同一个 Caddy 上跑着的公开文档站不能受影响——普通用户不应该被要求提交客户端证书。Caddy 选择性 mTLS 正是为这类"一部分流量必须验证书、另一部分照常放行"的需求设计的:它按 TLS 连接策略(connection_policy)匹配流量,对每个连接应用不同的客户端证书验证规则。

  • connection_policy+match把强制认证限定在指定 IP 段或域名上
  • 先跑通基线双向认证,再叠加 remote_ip / sni 等匹配条件
  • 拿到一张 mTLS 握手排障表,覆盖 CA 路径、证书链、匹配顺序等高频坑

为什么不是"一刀切"

方案安全性可用性配置复杂度
不启用 mTLS仅验证服务端,无客户端身份所有客户端零摩擦
全量 mTLS每个连接都验证客户端证书没有证书的连接全部被拒低,但牺牲可用
选择性 mTLS敏感流量强制验证,其余照常普通访问不受影响中,需写匹配条件

Caddy 的机制核心在 TLS 连接策略模块(ConnectionPolicy):每个 TLS 握手开始时,Caddy 拿 ClientHello 依次匹配已配置的连接策略,第一条命中的策略决定该连接的客户端认证行为client_auth块(模式 + 信任池)就挂在策略上,而不是写死在服务器上。

支持的匹配维度(均作用于握手阶段,早于任何 HTTP 路由):

  • SNI:按请求域名匹配,MatchServerName
  • 远端 IP:按客户端 IP/网段匹配,MatchRemoteIP
  • 本地 IP:按服务器本机 IP 匹配,适合多网卡多入口,MatchLocalIP
  • 正则:SNI 通配,MatchServerNameRE

先想清楚:你的匹配条件是什么

上配置前先把"什么流量需要强制证书"说清楚,匹配条件选错了,后面全白调。

典型场景适用条件推荐 matcher
内网服务网段客户端固定来自办公网/机房 IP 段,如 192.168.1.0/24match remote_ip
特定子域名内部 API 独占一个子域名,如 api.internal.example.commatch sni
正则通配一批子域名都算内部服务,如*.corp.example.commatch sni_regexp

证书文件只需准备好客户端 CA 的根证书,放到 Caddy 可读取的路径:

cp client-ca.crt /etc/caddy/caddy.ca.cer

版本要求:trust_pool子块语法需要较新的 Caddy(v2.7+),升级或安装参见 官方安装文档。⚠️ 老版本只认已废弃的trusted_ca_cert_file字段,混用会在caddy adapt时报错。

配置落地:从最小可用到按需加条件

基线双向认证配置:tls 块 + client_auth + trust_pool

{ srv0 { listen :443 routes { @internal { header Authorization "Bearer sk-internal-..." } handle /api/* { reverse_proxy 10.0.0.20:8080 } } } }

上面是示意路由结构,核心在下面的 TLS 块(取自仓库测试用例 tls_client_auth_cert_file.caddyfiletest):

:443 { respond "OK" tls { client_auth { mode require_and_verify # 关键行:强制提交并完整验证 trust_pool file { pem_file /etc/caddy/caddy.ca.cer # CA 根证书路径 } } } }

这一段的意思是:该 server 上的所有连接都必须提交由这个 CA 签发的有效客户端证书,否则握手直接失败。跑通这个基线再谈选择性——如果基线都不通,加条件只会把排查范围放大。换成你的场景时只改pem_file路径和 CA 来源即可。

加条件:用 connection_policy + match remote_ip 限定强制范围

:443 { respond "OK" tls { # 默认策略:只请求、不强制,普通客户端零摩擦 client_auth { mode request trust_pool file { pem_file /etc/caddy/caddy.ca.cer } } # 内网网段命中后强制验证 connection_policy { match remote_ip 192.168.1.0/24 client_auth { mode require_and_verify trust_pool file { pem_file /etc/caddy/caddy.ca.cer } } } } }

匹配规则要记死:策略按在 Caddyfile 中出现的顺序评估,先命中先生效,后面的策略不参与。⚠️ 如果你把remote_ip策略写在前面、默认策略写在后面,顺序反了就会让 192.168.1.0/24 之外的流量落到不该落的位置——按"越具体越靠前"排。改网段就改match remote_ip后面的 CIDR;要按域名强制,把它换成match sni api.internal.example.com即可。

验收与排障:curl 双场景 + mTLS 握手排障表

快速验收:无证书被拒、带证书通过

# 预期结果:握手失败(tls: certificate required) curl -v https://api.internal.example.com --cacert /etc/caddy/caddy.ca.cer # 预期结果:HTTP 200,返回 "OK" curl https://api.internal.example.com --cacert /etc/caddy/caddy.ca.cer \ --cert client.crt --key client.key

两条命令一拒一通过,说明选择性策略按预期生效。注意--cert/--key指向由该 CA 签发的客户端证书,测试阶段可以让 Caddy PKI 模块 的内部 CA 顺手签发一张。

配置不生效?按这张 mTLS 握手排障表查

现象最可能原因排查命令或操作相关源码或文档
handshake failure且所有客户端全拒pem_file路径错误或文件不可读ls -l /etc/caddy/caddy.ca.cer,确认 Caddy 进程用户可读ClientAuthentication 解析
证书正确但仍被拒客户端证书链断裂:提交的是中间证书,未带全链openssl verify -CAfile /etc/caddy/caddy.ca.cer client.crttrust_pool 模块解析
昨天正常今天全拒服务器时间漂移,证书被判定过期chronyc tracking校时;openssl x509 -enddate -noout -in client.crt同上
内网流量没走强制策略匹配器顺序问题:更宽的策略写在了前面检查 Caddyfile 中connection_policy顺序,具体策略靠前策略评估顺序
改完配置起不来或行为怪异Caddyfile 语法/字段错误caddy adapt --config Caddyfile --pretty查看展开后的 JSONCaddyfile 适配测试用例

生产环境 mTLS 上线 checklist

  • 服务端证书接入 Caddy 自动签发与轮换,客户端 CA 到期纳入日历提醒(Caddy PKI 模块)
  • 客户端证书吊销走 CRL/OCSP 或缩短证书有效期,别等泄露后才发现
  • 内网高频服务启用 TLS 会话复用,减少重复完整握手
  • 打开握手日志,认证失败率异常时能按时间点回溯(日志模块)
  • 把 mTLS 失败率纳入指标监控并设告警阈值(HTTP 指标)
  • 条件逻辑超过两三个维度时改用 CEL 表达式匹配,避免 matcher 叠罗汉(CEL 匹配器)
  • 变更走caddy adapt预校验 + 灰度发布,不直接热加载生产配置

选择性 mTLS 的价值就一句话:敏感服务被双向认证保护,普通访问零摩擦——完整能力参见 Caddy TLS 模块。

【免费下载链接】caddyFast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS项目地址: https://gitcode.com/GitHub_Trending/ca/caddy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询