前面关于 SPIRE 的内容中,介绍了使用 JOIN Token 证实节点身份的方法。这种方法比较简易,但是完全依赖 SPIRE Server/Agent 的“内循环”,并不利于外部管理,同时每次节点更新,都要照本宣科的重来一遍。对于动态集群来说,这种方式并不理想,SPIRE 包含了面向 OpenStack、几大公有云以及 TPM 等的花钱证实节点身份的方案;除了这些之外,还有个经济型的证实方法——使用 SSH。
我们一般使用的免密登录 SSH 方案通常是点对点的,总结来说就是服务器和客户端各自有各自的公私钥,互相进行信任操作:
- SSHD 会自动生成服务器端的公私钥
- 客户端通常使用
ssh-keygen
命令生成自己的公私钥 - 客户端将服务器端的证书脚印加到自己的
know_hosts
文件里面,代表信任该地址和证书的组合 - 服务器将客户端的公钥加到服务侧特定用户的
authorized_keys
文件之中,代表认可以该密钥作为特定用户的身份证明。
不难看出,这个过程实际上是跟前面的 JOIN Token 方式是对等的,并不会提升节点证实过程的可管理性。因此 SPIRE 的 SSH 插件要求使用基于 CA 的 SSH 方法。
用 CA 进行 SSH 认证
这种方式比上面的点对点认证方式稍微复杂一些。主要区别在于:
- 主机身份和用户身份都用 CA 进行签署
- 同样地,主机和用户身份的互信,也是通过对 CA 的信任完成
大概要完成几个工作:
- 创建节点 CA 证书,SSH 客户端信任该 CA 证书
- 用节点 CA 签发主机证书,并将服务端证书记录在 SSHD 的配置文件中。
- 创建客户端 CA 证书,SSH 服务端信任该 CA 证书
- 使用客户端 CA 签发用户证书,以此作为登录凭据。
例如 ChatGPT 告诉我的步骤是这样的:
几个关键的命令:
生成并配置 HostKey
下面的命令可以用于 SSHD 初始化,利用 CA 生成 HostKey:
ssh-keygen -s /etc/ssh/ca \
-I "$(hostname --fqdn) host key" \
-n "$(hostname),$(hostname --fqdn),$(hostname -I|tr ' ' ',')" \
-V -5m:+3650d \
-h \
/etc/ssh/ssh_host_rsa_key.pub \
/etc/ssh/ssh_host_dsa_key.pub \
/etc/ssh/ssh_host_ecdsa_key.pub
查看一下生成的证书内容:
$ ssh-keygen -L -f ssh_host_rsa_key-cert.pub
ssh_host_rsa_key-cert.pub:
Type: ssh-rsa-cert-v01@openssh.com host certificate
Public key: RSA-CERT SHA256:[...]
Signing CA: RSA SHA256:[...] (using rsa-sha2-512)
Key ID: "ssh"
Serial: 0
Valid: from 2022-12-16T08:12:02 to 2032-12-13T08:17:02
Principals:
ssh
ssh
10.211.55.9
fdb2:2c26:f4e4:0:21c:42ff:fe2a:18c4
Critical Options: (none)
Extensions: (none)
配置 HostKey
生成主机凭据之后,将证书和密钥信息加入 /etc/ssh/sshd_config
:
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
HostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub
让客户端信任主机 CA
和前面提到的 FingerPrint 方式类似,把 CA 证书公钥加入到客户端的 ~/.ssh/know_hosts
之中,例如:
@cert-authority * ssh-rsa ...AAAAB3NzaC1yc2EAAAADAQABAAABgQCb... someone@ssh
生成客户端证书
和前面生成主机身份证书的情况类似,这次去掉了 -h
参数:
ssh-keygen -s /etc/ssh/ca \
-I "$(whoami)@$(hostname --fqdn) user key" \
-n "$(whoami)" \
-V -5m:+3650d \
~/.ssh/id_rsa.pub
服务端信任客户端证书 CA
同样在 /etc/ssh/sshd_config
配置中加入 TrustedUserCAKeys
,具体取值为用户 CA 的公钥文件名。
完成这些内容之后,如果使用新的身份证书登录成功,则代表前置任务完成。否则可以参考以下材料:
- RHEL:
Using OpenSSH Certificate Authentication - Using CA With SSH
SPIRE 配置
前面的 SSH 配置只是个铺垫。SPIRE 使用 SSHPOP 实现了 Server 和 Agent 侧的节点证实插件,两个插件需要协同工作,官网的说明非常简明扼要:
稍稍延展说明一下需要注意的要点:
- SPIRE Agent 所在的节点实际上是作为 SSH 的服务端
- SPIRE Agent 联系 SPIRE Server 之后,SPIRE Server 要通过 SSH 来访问 SSH 服务端来确认身份。
因此上面语焉不详的配置就比较清楚了:
- SPIRE Server 的
cert_authorities
需要的是客户端证书内容,例如["ssh-rsa XXXX46IvQ+bDEXYvf8pM= someone@ssh"]
- SPIRE Server 的
cert_authorities_path
指向节点 CA 公钥,例如XXXX/ca.pub
- SPIRE Agent 的
host_cert_path
指向主机证书文件,例如XXXX_key-cert.pub
- SPIRE Agent 的
host_key_path
指向密钥文件,例如XXXX_key
配置完成之后,启动 SPIRE Server,获取并把 Trust Bundle 传递给 SPIRE Agent,启动 SPIRE Agent,可以看到生成了形如 "spiffe://spiffe.dom/spire/agent/sshpop/XXXX
的 SVID,说明这个证实过程已经成功完成。
后记
本以为这是个顺便完成的东西,结果从来没想过 SSH 还有个 CA 这样的玩意,卡了好些时间,轻敌了。
另,值此辞旧迎新之际,祝大家身体健康、事业稳定、学习进步、物资充足——最重要运气爆棚吧:)