5月28日 00:49

什么是 SSH 证书认证?如何配置和管理 SSH 证书?

SSH 证书认证用 CA(证书颁发机构)对用户或主机公钥进行签名,生成带有效期和身份信息的证书,服务器只需信任 CA 公钥即可验证所有由该 CA 签发的证书。相比手动分发 authorized_keys,证书方式在大规模环境下管理成本更低、安全性更强。

为什么用证书而不是密钥?

传统 SSH 密钥认证的痛点在于:每台服务器都要维护 authorized_keys 文件,用户入职要在所有服务器上添加公钥,离职要逐台删除——服务器一多就是运维噩梦。证书认证从根本上解决了这个问题:

  • 服务器不存用户公钥,只配置一条 TrustedUserCAKeys 指向 CA 公钥
  • 证书自带有效期,到期自动失效,不存在"永不过期的密钥"
  • 撤销只需更新列表,不用逐台机器删 authorized_keys
  • 可限制权限,比如只允许从特定 IP 连接、只能执行指定命令

Meta、Uber、Google 等公司内部都在用 SSH 证书方案管理数万台服务器的访问权限。

证书认证的工作原理

核心流程只有三步:

  1. 建立 CA:生成一对 CA 密钥,私钥严格保管(建议离线存储或用 HSM),公钥分发到所有需要信任该 CA 的服务器
  2. 签发证书:用 CA 私钥对用户的公钥签名,生成包含身份标识(Key ID)、授权主体(Principals)、有效期等信息的证书文件
  3. 验证连接:用户 SSH 连接时出示证书,服务器用本地配置的 CA 公钥验证签名,检查有效期和 Principals 后放行

搭建 CA 并签发用户证书

生成 CA 密钥对

bash
# 用户 CA(用于签发用户证书) ssh-keygen -t ed25519 -f /etc/ssh/ca_user_key -C "User CA" # 主机 CA(用于签发主机证书,可选) ssh-keygen -t ed25519 -f /etc/ssh/ca_host_key -C "Host CA"

CA 私钥权限必须设为 600,且只在签发证书时使用。生产环境建议将 CA 私钥存放在离线机器或 HSM 中。

签发用户证书

bash
ssh-keygen -s /etc/ssh/ca_user_key \ -I "user_zhangsan" \ -n "zhangsan" \ -V +52w \ -z 1 \ ~/.ssh/zhangsan_key.pub

参数含义:

  • -I:证书的身份标识,用于日志审计,建议用 user_用户名 格式
  • -n:Principals,允许登录的系统用户名,多个用逗号分隔
  • -V:有效期,+52w 表示 52 周,也可写 +365d20240101-20250101
  • -z:证书序列号,用于撤销时定位,每次签发应递增

签发后生成 zhangsan_key-cert.pub 文件,用户连接时需同时持有私钥和此证书文件。

签发主机证书

主机证书解决的是"首次连接时如何确认服务器身份"的问题,避免中间人攻击和 known_hosts 的手动维护。

bash
ssh-keygen -s /etc/ssh/ca_host_key \ -I "host_web01" \ -h \ -n "web01.example.com,10.0.1.50" \ -V +52w \ /etc/ssh/ssh_host_ed25519_key.pub

-h 标志区分主机证书和用户证书。

服务器端配置

信任用户 CA

/etc/ssh/sshd_config 中添加:

bash
# 信任用户 CA,所有由该 CA 签发的用户证书均被接受 TrustedUserCAKeys /etc/ssh/ca_user_key.pub # 可选:限制每个用户可用的 Principals AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u # 确保公钥认证开启 PubkeyAuthentication yes

AuthorizedPrincipalsFile 的作用是限制哪些 Principal 可以映射到当前系统用户。比如 /etc/ssh/auth_principals/root 文件内容为 admin ops,那么只有证书中 Principals 包含 adminops 的才能以 root 登录。

部署主机证书

bash
# 确认主机证书文件在位 ls /etc/ssh/ssh_host_ed25519_key-cert.pub # 在 sshd_config 中指定主机证书 HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub # 重启 SSH 服务 systemctl restart sshd

重要:修改 sshd_config 后务必在另一个终端保留当前连接,先用新终端测试证书登录成功再关闭旧连接,防止配置错误锁死自己。

客户端配置

使用证书连接

证书文件和私钥放在同一目录,文件名遵循 OpenSSH 约定(私钥 id_ed25519,证书 id_ed25519-cert.pub)时无需额外配置:

bash
ssh zhangsan@web01.example.com

如果证书文件名不是默认约定,可以手动指定:

bash
# 命令行指定 ssh -i ~/.ssh/zhangsan_key -o CertificateFile=~/.ssh/zhangsan_key-cert.pub zhangsan@web01 # 或写入 ~/.ssh/config Host web01 HostName web01.example.com User zhangsan IdentityFile ~/.ssh/zhangsan_key CertificateFile ~/.ssh/zhangsan_key-cert.pub

信任主机 CA

~/.ssh/known_hosts 中添加一行:

shell
@cert-authority *.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...

这表示所有 *.example.com 域名下由该 CA 签发的主机证书都被信任,不再需要逐台确认指纹。

证书撤销

撤销列表(RevokedKeys)

SSH 证书的撤销不像 TLS 那样有 OCSP/CRL 协议,而是使用一个简单的密钥列表文件。

bash
# 创建撤销列表文件,每行一个要撤销的公钥 cat > /etc/ssh/revoked_keys << 'EOF' ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... compromised_key ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... terminated_user EOF # 也可以直接从证书文件生成撤销条目 ssh-keygen -k -f /etc/ssh/revoked_keys ~/.ssh/zhangsan_key-cert.pub

然后在 sshd_config 中配置:

bash
RevokedKeys /etc/ssh/revoked_keys

注意RevokedKeys 文件中存放的是公钥或证书的原始内容,不是序列号。这是常见的误区——OpenSSH 的撤销列表不是 serial:reason 格式,而是标准的公钥列表格式。每次撤销后需重启或 reload sshd 才能生效。

更实用的做法:缩短有效期

与其维护撤销列表,不如一开始就把用户证书有效期设短(如 24 小时),配合自动化签发服务(如 HashiCorp Vault),用户每次连接前自动获取新证书。证书天然过期,撤销列表的维护压力就小很多。

高级权限控制

限制证书能力

bash
# 签发受限证书:只能执行部署脚本 ssh-keygen -s /etc/ssh/ca_user_key \ -I "deploy_ci" \ -n "deploy" \ -V +1d \ -O clear \ -O no-port-forwarding \ -O no-X11-forwarding \ -O force-command=/usr/local/bin/deploy.sh \ -O source-address=10.0.0.0/8 \ ~/.ssh/deploy_key.pub

关键选项:

  • -O clear:清除所有默认权限(包括 pty、port-forwarding 等),之后再逐项添加需要的权限
  • -O force-command=...:限制只能执行指定命令,适合 CI/CD 场景
  • -O source-address=...:限制来源 IP 段
  • -O no-pty:禁止分配终端,适合自动化脚本

按角色签发不同证书

bash
# 管理员:完整权限,有效期较长 ssh-keygen -s $CA_KEY -I "admin_lisi" -n "root,lisi" -V +4w \ -O permit-pty admin_key.pub # 只读巡检:无 pty,只能看 ssh-keygen -s $CA_KEY -I "readonly_wangwu" -n "readonly" -V +1w \ -O no-pty readonly_key.pub # CI/CD 部署:限定命令和 IP ssh-keygen -s $CA_KEY -I "cicd_gitlab" -n "deploy" -V +1h \ -O clear -O force-command=/usr/local/bin/deploy.sh \ -O source-address=10.1.0.0/16 cicd_key.pub

证书查看与审计

bash
# 查看证书详细信息 ssh-keygen -L -f ~/.ssh/zhangsan_key-cert.pub

输出示例:

shell
Type: ssh-ed25519-cert-v01@openssh.com user certificate Public key: ED25519-CERT SHA256:abc123... Signing CA: ED25519 SHA256:def456... (using ssh-ed25519) Key ID: "user_zhangsan" Serial: 1 Valid: from 2024-01-15T10:00:00 to 2025-01-13T10:00:00 Principals: zhangsan Critical Options: (none) Extensions: permit-X11-forwarding permit-agent-forwarding permit-port-forwarding permit-pty permit-user-rc

通过 Key ID 和 Serial 可以追踪证书签发记录,配合日志系统实现访问审计。

与 HashiCorp Vault 集成

手动签发证书在小型环境可行,但用户多了需要自动化。Vault 的 SSH Secrets Engine 可以按需签发短期证书:

bash
# 启用 SSH secrets engine vault secrets enable ssh # 配置 CA vault write ssh/config/ca generate_signing_key=true # 配置角色:开发环境,1 小时有效期 vault write ssh/roles/dev \ key_type=ca \ allowed_users="*" \ default_user="dev" \ ttl="1h" # 用户申请证书 vault write -field=signed_key ssh/sign/dev \ public_key=@$HOME/.ssh/id_ed25519.pub \ > $HOME/.ssh/id_ed25519-cert.pub

Vault 的优势:证书有效期短(通常 1 小时到 1 天),每次按需签发,自动记录审计日志,无需手动维护 CA 私钥的安全。

常见问题排查

连接时提示 Permission denied (publickey)

  • 检查证书是否过期:ssh-keygen -L -f cert.pub 查看 Valid 字段
  • 检查 Principals 是否匹配:证书中的 -n 值必须出现在目标用户的 AuthorizedPrincipalsFile 中
  • 检查证书是否被撤销:查看服务器 RevokedKeys 文件
  • 检查 sshd 是否加载了 CA 公钥:sshd -T | grep trustedusercakeys

首次连接仍提示确认指纹

  • 客户端 known_hosts 中的 @cert-authority 行未正确配置,或域名通配符不匹配
  • 主机证书未在 sshd_config 中用 HostCertificate 指定

证书签发后立即失效

  • -V 参数的时区问题:服务器时间与签发机器时间不一致
  • 序列号冲突:同一序列号签发多张证书可能导致问题

最佳实践总结

  1. CA 私钥离线保管:只在签发时使用,日常不放在可达的网络中
  2. 用户证书有效期不超过 1 天:配合 Vault 等工具实现按需签发
  3. 主机证书有效期可设 1 年:主机证书变更频率低
  4. 用 AuthorizedPrincipalsFile 做细粒度控制:不同角色映射不同系统用户
  5. CI/CD 用 force-command 限定命令:防止部署密钥被滥用
  6. 保留密码登录作为兜底:直到确认证书方案完全跑通再关闭密码认证
  7. 证书签发流程自动化:手动签发容易出错且不可审计
标签:SSH