# 在 Spire 中用 Ssh 证实节点身份

前面关于 SPIRE 的内容中，介绍了使用 JOIN Token 证实节点身份的方法。这种方法比较简易，但是完全依赖 SPIRE Server/Agent 的“内循环”，并不利于外部管理，同时每次节点更新，都要照本宣科的重来一遍。对于动态集群来说，这种方式并不理想，SPIRE 包含了面向 OpenStack、几大公有云以及 TPM 等的花钱证实节点身份的方案；除了这些之外，还有个经济型的证实方法——使用 SSH。

我们一般使用的免密登录 SSH 方案通常是点对点的，总结来说就是服务器和客户端各自有各自的公私钥，互相进行信任操作：

1. SSHD 会自动生成服务器端的公私钥
    
2. 客户端通常使用 `ssh-keygen` 命令生成自己的公私钥
    
3. 客户端将服务器端的证书脚印加到自己的 `know_hosts` 文件里面，代表信任该地址和证书的组合
    
4. 服务器将客户端的公钥加到服务侧特定用户的 `authorized_keys` 文件之中，代表认可以该密钥作为特定用户的身份证明。
    

不难看出，这个过程实际上是跟前面的 JOIN Token 方式是对等的，并不会提升节点证实过程的可管理性。因此 SPIRE 的 SSH 插件要求使用基于 CA 的 SSH 方法。

## 用 CA 进行 SSH 认证

这种方式比上面的点对点认证方式稍微复杂一些。主要区别在于：

1. 主机身份和用户身份都用 CA 进行签署
    
2. 同样地，主机和用户身份的互信，也是通过对 CA 的信任完成
    

大概要完成几个工作：

1. 创建节点 CA 证书，SSH 客户端信任该 CA 证书
    
2. 用节点 CA 签发主机证书，并将服务端证书记录在 SSHD 的配置文件中。
    
3. 创建客户端 CA 证书，SSH 服务端信任该 CA 证书
    
4. 使用客户端 CA 签发用户证书，以此作为登录凭据。
    

例如 ChatGPT 告诉我的步骤是这样的：

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745943799832/15ede645-3b60-4984-8755-8a0b690c5e81.png align="center")

几个关键的命令：

### 生成并配置 HostKey

下面的命令可以用于 SSHD 初始化，利用 CA 生成 HostKey：

```plaintext
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
```

查看一下生成的证书内容：

```bash
$ 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`：

```plaintext
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` 之中，例如：

```plaintext
@cert-authority * ssh-rsa ...AAAAB3NzaC1yc2EAAAADAQABAAABgQCb... someone@ssh
```

### 生成客户端证书

和前面生成主机身份证书的情况类似，这次去掉了 `-h` 参数：

```plaintext
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](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/sec-creating_ssh_ca_certificate_signing-keys)
    
* [Using CA With SSH](https://www.lorier.net/docs/ssh-ca.html)
    

## SPIRE 配置

前面的 SSH 配置只是个铺垫。SPIRE 使用 SSHPOP 实现了 Server 和 Agent 侧的节点证实插件，两个插件需要协同工作，[官网的说明](https://github.com/spiffe/spire/blob/main/doc/plugin_server_nodeattestor_sshpop.md)非常简明扼要：

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745943811064/74475837-6ffd-4a1a-a129-5a198f70b99e.png align="center")

稍稍延展说明一下需要注意的要点：

1. SPIRE Agent 所在的节点实际上是作为 SSH 的服务端
    
2. 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 这样的玩意，卡了好些时间，轻敌了。

另，值此辞旧迎新之际，祝大家身体健康、事业稳定、学习进步、物资充足——最重要运气爆棚吧：）
