签名的 SSH 证书

就设置复杂性和平台无关性而言,签名的 SSH 证书是最简单和最强大的。通过利用 Vault 强大的 CA 能力和 OpenSSH 内置的功能,客户端可以使用自己的本地 SSH 密钥通过 SSH 连接到目标主机。

本节中的“客户端”指的是执行 SSH 操作的人或者机器。“服务端”指的是目标机器。客户端也可以看作“用户”。

本节仅演示如何快速使用该机密引擎。要获取所有可用路径的详细文档,请使用 vault path-help 命令,命令后方输入机密引擎的挂载地点。

客户端密钥签名

在客户端可以请求对他们的 SSH 密钥进行签名之前,必须配置 Vault SSH 机密引擎。通常由 Vault 管理员或安全团队执行这些步骤。也可以使用 Chef、Puppet、Ansible 或 Salt 等配置管理工具来自动化这些操作。

签名密钥 & 配置角色

以下步骤由 Vault 管理员、安全团队或配置管理工具预先执行。

  1. 挂载机密引擎。就像 Vault 中所有的机密引擎一样,SSH 机密引擎在使用前必须先挂载:
$ vault secrets enable -path=ssh-client-signer ssh
Successfully mounted 'ssh' at 'ssh-client-signer'!

该命令在路径 ssh-client-signer 上启用了 SSH 机密引擎。可以通过使用不同的 -path 参数多次挂载同一个机密引擎。这里的名字 ssh-client-signer 并没有什么特殊的——它可以是任何名字,但本节假设挂载点是 ssh-client-signer

  1. 透过 /config/ca 端点给 Vault 配置一个 CA ,用以对客户端密钥进行签名。如果我们没有内部 CA,Vault 会为我们生成一个密钥对(keypair)。
$ vault write ssh-client-signer/config/ca generate_signing_key=true
Key             Value
---             -----
public_key      ssh-rsa AAAAB3NzaC1yc2EA...

如果我们已经有了密钥对,请将公钥和私钥部分指定为有效载荷的一部分:

$ vault write ssh-client-signer/config/ca \
    private_key="..." \
    public_key="..."

无论是生成的还是上传的,用以对客户端进行签名的公钥都可以通过 API 的 /public_key 端点访问到。

  1. 将公钥添加到所有目标主机的 SSH 配置中。该进程可以手工完成,或是借助于配置管理工具。该公钥可以通过 API 得到,不需要经过身份验证:
$ curl -o /etc/ssh/trusted-user-ca-keys.pem http://127.0.0.1:8200/v1/ssh-client-signer/public_key
$ vault read -field=public_key ssh-client-signer/config/ca > /etc/ssh/trusted-user-ca-keys.pem

将存有公钥的文件路径保存在 SSH 配置文件中的 TrustedUserCAKeys 配置项上:

# /etc/ssh/sshd_config
# ...
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem

重启 SSH 服务以加载变更。

  1. 创建用于签署客户端密钥的命名 Vault 角色。

重要提示:在 Vault 1.9 之前,如果角色没有配置 "allowed_extensions" 或是配置为空白,Vault 会采用允许的默认值:分配给该角色的任何用户都可以指定任意扩展值作为对 Vault 服务器的证书请求的一部分。这可能会对依赖 extensions 字段来获取安全关键信息的第三方系统产生重大影响。在这些情况下,请考虑使用模板来指定默认扩展,并在字段为空或未设置时将 allowed_extensions 显式设置为任意非空字符串。

由于一些 SSH 证书功能实现的方式,选项以字典的方式传递。下面的例子中为证书添加了 permit-pty 扩展,并允许用户在请求证书时为 permit-ptypermit-port-forwarding 自行设置值:

$ vault write ssh-client-signer/roles/my-role -<<"EOH"
{
  "algorithm_signer": "rsa-sha2-256",
  "allow_user_certificates": true,
  "allowed_users": "*",
  "allowed_extensions": "permit-pty,permit-port-forwarding",
  "default_extensions": [
    {
      "permit-pty": ""
    }
  ],
  "key_type": "ca",
  "default_user": "ubuntu",
  "ttl": "30m0s"
}
EOH

客户端 SSH 身份验证

希望通过身份验证连接到由 Vault 管理的机器的客户端(用户)需要执行以下步骤。这些命令通常从过年客户端运行的本地工作站运行:

  1. 定位或生成 SSH 公钥。通常公钥在 ~/.ssh/id_rsa.pub。如果我们没有 SSH 密钥对,可以生成一个:
$ ssh-keygen -t rsa -C "user@example.com"
  1. 请求 Vault 签名公钥。该文件通常以 .pub 为后缀名,内容的开头为 ssh-rsa ...
$ vault write ssh-client-signer/sign/my-role \
    public_key=@$HOME/.ssh/id_rsa.pub

Key             Value
---             -----
serial_number   c73f26d2340276aa
signed_key      ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1...

返回结果包含了序列号以及签名后的密钥。该密钥也是一个公钥。

要自定义签名参数,请使用 JSON 载荷:

$ vault write ssh-client-signer/sign/my-role -<<"EOH"
{
  "public_key": "ssh-rsa AAA...",
  "valid_principals": "my-user",
  "key_id": "custom-prefix",
  "extensions": {
    "permit-pty": "",
    "permit-port-forwarding": ""
  }
}
EOH
  1. 将经过签名的公钥保存到磁盘上。可以根据需要限制权限:
$ vault write -field=signed_key ssh-client-signer/sign/my-role \
    public_key=@$HOME/.ssh/id_rsa.pub > signed-cert.pub

如果我们将证书直接保存在 SSH 密钥旁边,在文件名后添加 -cert.pub 后缀(!/.ssh/id_rsa-cert.pub)。如果按照这种规范命名,OpenSSH 会自动在进行身份验证时使用经签名的公钥。

  1. (可选)查看经签名的密钥的已启用的扩展(extnsions)、主体(principals)以及元数据:
$ ssh-keygen -Lf ~/.ssh/signed-cert.pub
  1. 使用经签名的密钥 SSH 连接至主机。我们需要同时提供从 Vault 获取的经签名的公钥以及相对应的私钥作为身份验证信息来发起 SSH 调用:
$ ssh -i signed-cert.pub -i ~/.ssh/id_rsa username@10.0.23.5

对主机密钥签名

为了增加额外的安全性,我们建议启用主机密钥签名。与客户端密钥签名结合使用可以提供额外的完整性层。启用后,SSH 代理将在尝试 SSH 之前验证目标主机是否有效且受信任。这将减少用户通过 SSH 意外连接到非托管或恶意机器的可能性。

签名密钥配置

  1. 挂载机密引擎。为了最大程度的安全,请挂载到与签名客户端时不同的路径上:
$ vault secrets enable -path=ssh-host-signer ssh
Successfully mounted 'ssh' at 'ssh-host-signer'!
  1. 通过 Vault 的 /config/ca 端点配置一个对主机密钥进行签名用的 CA 证书。如果我们没有内部的 CA,Vault 可以为我们生成一个密钥对。
$ vault write ssh-host-signer/config/ca generate_signing_key=true
Key             Value
---             -----
public_key      ssh-rsa AAAAB3NzaC1yc2EA...

如果我们已经有了密钥对,请将公钥和私钥部分指定为有效载荷的一部分:

$ vault write ssh-host-signer/config/ca \
    private_key="..." \
    public_key="..."

无论是生成的还是上传的,用以对主机进行签名的公钥都可以通过 API 的 /public_key 端点访问到。

  1. 延展主机密钥证书的 TTL:
$ vault secrets tune -max-lease-ttl=87600h ssh-host-signer
  1. 创建用于对主机密钥进行签名的角色。一定要填写 allowed domains 列表,或设置 allow_bare_domains,或同事设置两者:
$ vault write ssh-host-signer/roles/hostrole \
    key_type=ca \
    ttl=87600h \
    allow_host_certificates=true \
    allowed_domains="localdomain,example.com" \
    allow_subdomains=true
  1. 对主机的 SSH 公钥进行签名:
$ vault write ssh-host-signer/sign/hostrole \
    cert_type=host \
    public_key=@/etc/ssh/ssh_host_rsa_key.pub
Key             Value
---             -----
serial_number   3746eb17371540d9
signed_key      ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1y...
  1. 将返回的经签名的证书保存到磁盘:
$ vault write -field=signed_key ssh-host-signer/sign/hostrole \
    cert_type=host \
    public_key=@/etc/ssh/ssh_host_rsa_key.pub > /etc/ssh/ssh_host_rsa_key-cert.pub

将证书文件的权限设置为 0640

$ chmod 0640 /etc/ssh/ssh_host_rsa_key-cert.pub

将主机密钥及证书添加到 SSH 配置文件中:

# /etc/ssh/sshd_config
# ...

# For client keys
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem

# For host keys
HostKey /etc/ssh/ssh_host_rsa_key
HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub

重启 SSH 服务以加载变更。

客户端对主机进行验证

  1. 获取主机使用的 CA 公钥来验证目标机器的签名:
$ curl http://127.0.0.1:8200/v1/ssh-host-signer/public_key
$ vault read -field=public_key ssh-host-signer/config/ca
  1. 将返回的公钥添加到 known_hosts 文件的 authority 中:
# ~/.ssh/known_hosts
@cert-authority *.example.com ssh-rsa AAAAB3NzaC1yc2EAAA...
  1. 像往常一样 SSH 连接到目标机器。

故障排查

当开始配置此类密钥签名时,开启 SSH 登录的 VERBOSE 级别日志可以帮助在日志中定位各种错误:

# /etc/ssh/sshd_config
# ...
LogLevel VERBOSE

重启 SSH 服务以加载变更。

默认情况下,SSH 将日志记录到 /var/log/auth.log,但还有其他许多程序也会将日志记录于此。如果要把 SSH 的日志提取出来,可以这样做:

$ tail -f /var/log/auth.log | grep --line-buffered "sshd"

如果我们无法连接到主机,服务端的 SSH 日志可能可以提供指引和洞见。

name is not a listed principal

如果 auth.log 显示一下信息:

# /var/log/auth.log
key_cert_check_authority: invalid certificate
Certificate invalid: name is not a listed principal

该证书不允许将用户名作为列出的主体对系统进行身份验证。这很可能是由于 OpenSSH 错误(有关更多信息,请参阅已知问题)。此错误导致 allowed_users 选项值如果为“*”将不起作用。以下是解决此问题的方法:

  1. 设置角色中的 default_user。如果我们只使用同一个用户进行身份验证,可以将角色的 default_user 设置为 SSH 到目标机器时使用的用户名。
$ vault write ssh/roles/my-role -<<"EOH"
{
  "default_user": "YOUR_USER",
  // ...
}
EOH
  1. 在签名过程总设置 valid_principals。如果有多个用户需要通过 Vault 进行 SSH 身份验证,在密钥签名期间设置合法的主体列表,其中要包括当前用户名:
$ vault write ssh-client-signer/sign/my-role -<<"EOH"
{
  "valid_principals": "my-user"
  // ...
}
EOH

登录后没有提示(prompt)

如果我们在通过登录到目标机器后没有看到提示,可能是因为经签名的证书没有配置 permit-pty 扩展。有两种方法为证书添加该扩展:

  • 在创建角色时添加:
$ vault write ssh-client-signer/roles/my-role -<<"EOH"
{
  "default_extensions": [
    {
      "permit-pty": ""
    }
  ]
  // ...
}
EOH
  • 在签名时添加:
$ vault write ssh-client-signer/sign/my-role -<<"EOH"
{
  "extensions": {
    "permit-pty": ""
  }
  // ...
}
EOH

没有端口转发

如果从客户端到主机的端口转发不起作用,经签名的证书可能没有配置 permit-port-forwarding 扩展。可以参考上面没有提示部分的流程,在创建角色时添加该扩展:

{
  "default_extensions": [
    {
      "permit-port-forwarding": ""
    }
  ]
}

没有 X11 转发

如果从客户端到主机的 X11 转发不起作用,经签名的证书可能没有配置 permit-X11-forwarding 扩展。可以参考上面没有提示部分的流程,在创建角色时添加该扩展:

{
  "default_extensions": [
    {
      "permit-X11-forwarding": ""
    }
  ]
}

没有代理(agent)转发

如果从客户端到主机的代理转发不起作用,经签名的证书可能没有配置 permit-agent-forwarding 扩展。可以参考上面没有提示部分的流程,在创建角色时添加该扩展:

{
  "default_extensions": [
    {
      "permit-agent-forwarding": ""
    }
  ]
}

已知的问题

  • 在启用了 SELinux 的系统上,我们可能需要调整相关类型使得 SSH 守护进程可以读取到它。流入,将经签名的主机证书设置成 sshd_key_t 类型。
  • 在某些版本的 SSH 上我们可能会看到以下错误:
no separate private key for certificate

这是一个 OpenSSH 7.2 版本引入的 bug,在 7.5 被修复了。请阅读 OpenSSH bug 2617获取详细信息。

  • 在某些版本的 SSH 上,我们可能会在目标主机上看到以下错误:
userauth_pubkey: certificate signature algorithm ssh-rsa: signature algorithm not supported [preauth]

可以通过向 etc/ssh/sshd_config 追加以下内容来修复该错误:

CASignatureAlgorithms ^ssh-rsa

OpenSSH 8.2开始不再支持 ssh-rsa 算法。

results matching ""

    No results matching ""