13.6.4 双向认证与客户端证书校验

在需要强化安全的内部管理、B2B接口与敏感系统场景下,HAProxy 支持 TLS 双向认证(mTLS),通过校验客户端证书确认访问方身份。mTLS 要求客户端在握手阶段提交证书并完成链路校验,实现“只有持有合法证书的客户端才可访问”的访问控制。

原理草图(握手与校验):

文章图片

示例:生成 CA 与客户端证书(OpenSSL)

适用于演示环境;生产需加密私钥、规范目录权限与证书生命周期管理。

# 1) 生成根CA
mkdir -p /etc/pki/demoCA/{private,certs,newcerts,crl}
chmod 700 /etc/pki/demoCA/private
openssl genrsa -out /etc/pki/demoCA/private/ca.key 4096
openssl req -x509 -new -key /etc/pki/demoCA/private/ca.key \
  -sha256 -days 3650 -out /etc/pki/demoCA/certs/ca.crt \
  -subj "/C=CN/O=DemoCA/OU=Ops/CN=Demo Root CA"

# 2) 生成客户端证书
openssl genrsa -out /etc/pki/client01.key 2048
openssl req -new -key /etc/pki/client01.key \
  -out /etc/pki/client01.csr \
  -subj "/C=CN/O=DemoClient/OU=App/CN=client01"

openssl x509 -req -in /etc/pki/client01.csr \
  -CA /etc/pki/demoCA/certs/ca.crt \
  -CAkey /etc/pki/demoCA/private/ca.key \
  -CAcreateserial -out /etc/pki/client01.crt \
  -days 365 -sha256

# 3) 生成服务端证书(HAProxy证书)
openssl genrsa -out /etc/pki/haproxy.key 2048
openssl req -new -key /etc/pki/haproxy.key \
  -out /etc/pki/haproxy.csr \
  -subj "/C=CN/O=Demo/OU=Ops/CN=haproxy.example.com"

openssl x509 -req -in /etc/pki/haproxy.csr \
  -CA /etc/pki/demoCA/certs/ca.crt \
  -CAkey /etc/pki/demoCA/private/ca.key \
  -CAcreateserial -out /etc/pki/haproxy.crt \
  -days 365 -sha256

# 4) 组装HAProxy证书(服务端证书+私钥)
cat /etc/pki/haproxy.crt /etc/pki/haproxy.key > /etc/haproxy/certs/server.pem

HAProxy mTLS 配置示例(完整可运行片段)

# /etc/haproxy/haproxy.cfg
global
    log /dev/log local0
    maxconn 50000

defaults
    mode http
    log global
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend https_in
    bind *:443 ssl crt /etc/haproxy/certs/server.pem \
        ca-file /etc/pki/demoCA/certs/ca.crt \
        verify required
    # 校验状态,0为成功
    http-request deny if !{ ssl_c_verify 0 }
    # 基于证书CN控制
    acl valid_client ssl_c_s_dn(cn) -m str client01
    http-request deny if !valid_client
    # 透传证书信息给后端
    http-request set-header X-Client-CN %[ssl_c_s_dn(cn)]
    default_backend app_servers

backend app_servers
    server app1 10.0.0.11:8080 check

客户端访问示例与命令解释

# 使用curl携带客户端证书
curl -k https://haproxy.example.com/ \
  --cert /etc/pki/client01.crt \
  --key  /etc/pki/client01.key \
  --cacert /etc/pki/demoCA/certs/ca.crt

# 预期:返回后端页面或200状态
  • --cert/--key:客户端证书与私钥
  • --cacert:用于校验服务端证书的 CA
  • -k:仅用于测试跳过服务端校验,生产不建议使用

常见排错与命令(原因→定位→修复)
1. 握手失败:alert certificate required
- 原因:客户端未携带证书或 verify required 强制校验
- 定位:openssl s_client 查看握手
- 修复:客户端补齐 --cert/--key

openssl s_client -connect haproxy.example.com:443 \
  -CAfile /etc/pki/demoCA/certs/ca.crt
  1. CA不匹配:unknown ca
    - 原因:ca-file 不包含签发客户端证书的 CA
    - 修复:将正确 CA 链合并到 ca-file

  2. 证书链不完整
    - 原因:客户端证书未包含中间链
    - 修复:将中间 CA 追加到客户端证书文件

  3. 时间漂移导致失效
    - 定位:openssl x509 -in client01.crt -noout -dates
    - 修复:校准系统时间(NTP)

运行与验证命令

# 校验配置语法
haproxy -c -f /etc/haproxy/haproxy.cfg

# 重载
systemctl reload haproxy

# 查看日志(需系统log配置)
tail -f /var/log/haproxy.log

运维实践要点
- 证书分级管理:生产/测试/外部合作方使用不同 CA
- 轮换与吊销:CRL/OCSP 联动,定期替换客户端证书
- 最小权限:仅对关键入口启用 mTLS
- 证书透传与审计:记录 X-Client-CN,用于后端鉴权与审计

练习
1. 为 client02 生成证书并仅允许其访问 /admin 路径。
2. 启用 verify optional,分别测试无证书与有证书访问结果。
3. 配置 ssl_c_serial 白名单,模拟吊销某一序列号客户端。
4. 用 tcpdump -i any port 443 验证握手阶段是否携带证书。