CA 与证书

使用自签名 CA 或真实 HTTPS 证书

Pigsty 默认使用自签名证书颁发机构 (CA) 进行内部 SSL/TLS 加密。本文档包含:


自签名 CA

Pigsty 在基础设施初始化 (infra.yml) 时自动创建自签名 CA。该 CA 用于签发以下证书:

  • PostgreSQL 服务器/客户端 SSL
  • Patroni REST API
  • etcd 集群通信
  • MinIO 集群通信
  • Nginx HTTPS(备用)
  • 基础设施服务

PKI 目录结构

files/pki/
├── ca/
│   ├── ca.key                # CA 私钥(务必保管好!)
│   └── ca.crt                # CA 证书
├── csr/                      # 证书签名请求
├── misc/                     # 杂项证书(cert.yml 输出)
├── etcd/                     # ETCD 证书
├── pgsql/                    # PostgreSQL 证书
├── minio/                    # MinIO 证书
├── infra/                    # 基础设施证书
├── nginx/                    # Nginx 证书
└── mongo/                    # FerretDB 证书

CA 变量

变量默认值说明
ca_createtrue如果不存在则创建 CA,否则中止
ca_cnpigsty-caCA 证书通用名称
cert_validity7300d签发证书的默认有效期

证书有效期

证书类型有效期控制参数
CA 证书100 年硬编码(36500 天)
服务器/客户端20 年cert_validity(7300d)
Nginx HTTPS~1 年nginx_cert_validity(397d)

注意:浏览器厂商限制超过 398 天的证书信任。Nginx 使用较短有效期以保证浏览器兼容性。

使用外部 CA

如需使用企业自有 CA 而非自动生成的 CA:

  1. 在配置中设置 ca_create: false

  2. 在运行 playbook 之前放置 CA 文件:

    mkdir -p files/pki/ca
    cp /path/to/your/ca.key files/pki/ca/ca.key
    cp /path/to/your/ca.crt files/pki/ca/ca.crt
    chmod 600 files/pki/ca/ca.key
    chmod 644 files/pki/ca/ca.crt
    
  3. 运行 ./infra.yml

备份 CA 文件

CA 私钥至关重要,请安全备份:

# 带时间戳备份
tar -czvf pigsty-ca-$(date +%Y%m%d).tar.gz files/pki/ca/

警告:如果丢失 CA 私钥,由其签发的所有证书都将无法验证。您需要重新生成所有内容。


签发证书

使用 cert.yml 签发由 Pigsty CA 签名的额外证书。

基本用法

# 为数据库用户签发证书(客户端证书)
./cert.yml -e cn=dbuser_dba

# 为监控用户签发证书
./cert.yml -e cn=dbuser_monitor

默认情况下,证书生成在 files/pki/misc/<cn>.{key,crt}

参数说明

参数默认值说明
cnpigsty通用名称(必填)
san[DNS:localhost, IP:127.0.0.1]主题备用名称
orgpigsty组织名称
unitpigsty组织单位名称
expire7300d证书有效期(20 年)
keyfiles/pki/misc/<cn>.key私钥输出路径
crtfiles/pki/misc/<cn>.crt证书输出路径

高级示例

# 签发带自定义 SAN(DNS 和 IP)的证书
./cert.yml -e cn=myservice \
  -e '{"san":["DNS:myservice.local","DNS:myservice","IP:10.10.10.10"]}'

# 签发自定义有效期(10 年)的证书
./cert.yml -e cn=shortlived -e expire=3650d

# 签发到自定义路径
./cert.yml -e cn=custom \
  -e key=/tmp/custom.key \
  -e crt=/tmp/custom.crt

# 签发带自定义组织的证书
./cert.yml -e cn=external \
  -e org="My Company" \
  -e unit="IT Department"

使用场景

PostgreSQL 客户端证书

用于 SSL 客户端认证(pg_hba.conf 中的 cert 认证方式):

# 为 DBA 用户签发证书
./cert.yml -e cn=dbuser_dba

# 复制到客户端机器
scp files/pki/misc/dbuser_dba.{key,crt} user@client:~/.postgresql/
scp files/pki/ca/ca.crt user@client:~/.postgresql/root.crt

# 使用客户端证书连接
psql "host=pg-test port=5432 dbname=postgres user=dbuser_dba sslmode=verify-full sslcert=~/.postgresql/dbuser_dba.crt sslkey=~/.postgresql/dbuser_dba.key sslrootcert=~/.postgresql/root.crt"

服务间 TLS

用于需要双向 TLS 的内部服务:

./cert.yml -e cn=myapp -e '{"san":["DNS:myapp.service.local","IP:10.10.10.50"]}'

信任 CA 证书

在客户端机器上信任自签名 CA:

Linux (Debian/Ubuntu)

sudo cp files/pki/ca/ca.crt /usr/local/share/ca-certificates/pigsty-ca.crt
sudo update-ca-certificates

Linux (RHEL/Rocky/Alma)

sudo cp files/pki/ca/ca.crt /etc/pki/ca-trust/source/anchors/pigsty-ca.crt
sudo update-ca-trust

macOS

sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain files/pki/ca/ca.crt

Windows

Import-Certificate -FilePath files\pki\ca\ca.crt -CertStoreLocation Cert:\LocalMachine\Root

从 Nginx 下载

CA 证书也可通过 Nginx 在 http://<infra_ip>/ca.crt 获取:

curl -o ca.crt http://10.10.10.10/ca.crt

Let’s Encrypt

对于公网服务,您可以通过 Certbot 使用 Let’s Encrypt 的真实证书。

前置条件

  • 拥有公网域名
  • DNS 记录指向服务器的公网 IP
  • Nginx 已正确配置
  • 80 和 443 端口可访问

第一步:域名配置

infra_portal 中配置服务域名:

infra_portal:
  home: { domain: pigsty.cc }
  grafana: { domain: g.pigsty.cc, endpoint: "${admin_ip}:3000", websocket: true }
  prometheus: { domain: p.pigsty.cc, endpoint: "${admin_ip}:8428" }
  alertmanager: { domain: a.pigsty.cc, endpoint: "${admin_ip}:9059" }

第二步:DNS 配置

通过 A 记录将所有域名指向服务器的公网 IP:

nslookup g.pigsty.cc
dig +short g.pigsty.cc

第三步:申请证书

交互式方式:

certbot --nginx -d pigsty.cc -d g.pigsty.cc -d p.pigsty.cc -d a.pigsty.cc

非交互式方式:

certbot --nginx --agree-tos --email admin@pigsty.cc -n \
  -d pigsty.cc -d g.pigsty.cc -d p.pigsty.cc -d a.pigsty.cc

第四步:Nginx 配置

在 portal 条目中添加 certbot: true 参数,然后重新生成配置:

./infra.yml -t nginx_config,nginx_launch

第五步:自动续期

测试续期(预演模式):

certbot renew --dry-run

设置 cron 定时任务(每月 1 日凌晨 2 点):

0 2 1 * * certbot renew --quiet

或启用 systemd 定时器:

systemctl enable certbot.timer

管理命令

Certbot 命令

命令说明
certbot certificates列出所有证书
certbot renew --cert-name domain.com续期指定证书
certbot delete --cert-name domain.com删除证书
certbot revoke --cert-path /path/to/cert.pem吊销证书

OpenSSL 命令

# 查看证书详情
openssl x509 -in files/pki/ca/ca.crt -text -noout

# 查看证书过期时间
openssl x509 -in files/pki/pgsql/pg-meta-1.crt -enddate -noout

# 验证证书是否由 CA 签发
openssl verify -CAfile files/pki/ca/ca.crt files/pki/pgsql/pg-meta-1.crt

# 检查证书链
openssl s_client -connect 10.10.10.10:5432 -starttls postgres </dev/null

故障排查

问题解决方案
证书过期重新运行 playbook 重新生成,或使用 cert.yml
CA 不被信任在客户端安装 CA 证书(参见信任 CA 章节)
域名无法访问验证 DNS 传播是否完成
端口 80 被阻止确保 Let’s Encrypt 验证时端口 80 开放
请求频率限制避免短时间内多次申请 Let’s Encrypt 证书
权限被拒绝检查文件权限(密钥:0600,证书:0644)

最佳实践

  • 备份 CA 密钥:将 files/pki/ca/ca.key 安全地离线存储
  • 使用适当的有效期:nginx 用短期(浏览器兼容),内部服务用长期
  • 轮换证书:定期重新运行 playbook 刷新证书
  • 监控过期:设置证书过期告警
  • 公网用 Let’s Encrypt:内部用自签名,公网服务用真实证书
  • 记录配置:跟踪哪些服务使用哪些证书

最后修改 2026-01-09: add supabase asciinema demo (3104503)