什么是 SSH 证书认证?如何配置和管理 SSH 证书?
SSH 证书认证用 CA(证书颁发机构)对用户或主机公钥进行签名,生成带有效期和身份信息的证书,服务器只需信任 CA 公钥即可验证所有由该 CA 签发的证书。相比手动分发 authorized_keys,证书方式在大规模环境下管理成本更低、安全性更强。
为什么用证书而不是密钥?
传统 SSH 密钥认证的痛点在于:每台服务器都要维护 authorized_keys 文件,用户入职要在所有服务器上添加公钥,离职要逐台删除——服务器一多就是运维噩梦。证书认证从根本上解决了这个问题:
- 服务器不存用户公钥,只配置一条
TrustedUserCAKeys指向 CA 公钥 - 证书自带有效期,到期自动失效,不存在"永不过期的密钥"
- 撤销只需更新列表,不用逐台机器删 authorized_keys
- 可限制权限,比如只允许从特定 IP 连接、只能执行指定命令
Meta、Uber、Google 等公司内部都在用 SSH 证书方案管理数万台服务器的访问权限。
证书认证的工作原理
核心流程只有三步:
- 建立 CA:生成一对 CA 密钥,私钥严格保管(建议离线存储或用 HSM),公钥分发到所有需要信任该 CA 的服务器
- 签发证书:用 CA 私钥对用户的公钥签名,生成包含身份标识(Key ID)、授权主体(Principals)、有效期等信息的证书文件
- 验证连接:用户 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 中。
签发用户证书
bashssh-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 周,也可写+365d、20240101-20250101等-z:证书序列号,用于撤销时定位,每次签发应递增
签发后生成 zhangsan_key-cert.pub 文件,用户连接时需同时持有私钥和此证书文件。
签发主机证书
主机证书解决的是"首次连接时如何确认服务器身份"的问题,避免中间人攻击和 known_hosts 的手动维护。
bashssh-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 包含 admin 或 ops 的才能以 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)时无需额外配置:
bashssh 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 中配置:
bashRevokedKeys /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
输出示例:
shellType: 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参数的时区问题:服务器时间与签发机器时间不一致- 序列号冲突:同一序列号签发多张证书可能导致问题
最佳实践总结
- CA 私钥离线保管:只在签发时使用,日常不放在可达的网络中
- 用户证书有效期不超过 1 天:配合 Vault 等工具实现按需签发
- 主机证书有效期可设 1 年:主机证书变更频率低
- 用 AuthorizedPrincipalsFile 做细粒度控制:不同角色映射不同系统用户
- CI/CD 用 force-command 限定命令:防止部署密钥被滥用
- 保留密码登录作为兜底:直到确认证书方案完全跑通再关闭密码认证
- 证书签发流程自动化:手动签发容易出错且不可审计