OpenSSH/Cookbook/基于证书的身份验证
证书是经过另一个密钥签名的密钥[1]。 用于此类签名的密钥称为证书颁发机构。 它是在预先创建并预留的,专门用于签名。 其他方使用签名密钥的公钥部分来验证用于服务器身份验证的已签名密钥的真实性,在主机证书的情况下[2],或用于登录,在用户证书的情况下[3] 。
为了实现特权分离,如果要使用主机证书和用户证书,请为它们创建单独的证书颁发机构。 在撰写本文时,椭圆曲线算法中的任何一种都是不错的选择。
使用证书时,客户端或服务器预先配置为接受由另一个密钥签名的密钥,该密钥专门指定并预留,仅用于对实际用于工作的密钥进行签名。 该另一个密钥称为证书颁发机构,简称 CA。 与普通密钥一样,证书用于生成用于身份验证的签名。 但是,不是在文件中查找匹配的公钥,而是将公钥与签名一起存档,并且使用签名验证公钥,然后使用公钥确保协商正在与拥有匹配私钥的客户端进行。 因此,使用证书的先决条件至少是对普通 SSH 有基本的了解。 请参阅有关 公钥身份验证 的章节。 普通基于密钥的身份验证机制在引言中简要描述。 SSH 使用其自己的更简单的证书格式,而不是 X.509 证书格式。
证书相对于密钥的两个主要优势是,它们可以使用过期日期,甚至有效期范围,以及它们消除了对首次使用信任或复杂密钥验证方法的需要。 大多数情况下,它们通过简化密钥审批和分发过程来促进大规模部署,并提供比 将相同的主机密钥复制到多个目标 更好的选择。
用户证书将用户身份验证到服务器或其他远程设备上的帐户。 主机证书将服务器身份验证到客户端,证明客户端正在连接到正确的系统。 使用 principals 字段来指定用户与主机之间的区别是主机证书和用户证书之间的主要区别。 在主机证书中,principals 字段指的是证书表示的服务器名称。 在用户证书中,该字段指的是允许使用证书登录的帐户。 用户证书还可以添加其他限制,例如特定源地址和强制命令。 有效日期和时间对于两者都是可能的。 主机证书和用户证书应使用单独的证书颁发机构。 有关更权威的资源,请参阅 ssh-keygen(1) 中的“证书”部分。
用户证书将用户身份验证到服务器或其他远程设备,换句话说,它们允许用户和脚本登录。 通过用户证书将客户端身份验证到服务器意味着服务器上的 authorized_keys 文件不再需要。 相反,用户证书(实际上是已签名的密钥)会根据证书颁发机构进行检查,以验证签名是否有效。 如果有效,则可以继续登录过程。 另一个优势是,可以将签名指定为仅在一定日期范围内有效。 因此,在实践中,可以设置过期日期。 用户证书也与特定帐户绑定,称为“主体”。 允许使用已签名密钥的主体必须指定,否则即使证书已正确签名,服务器也不会接受使用身份验证密钥。
甚至可以使用类似于普通 SSH 公钥的 force-command 和 source-address 选项,来限制传入连接的来源或强制执行特定命令。 这些限制是在签名时设置的。 如果需要更改这些选项,则必须重新签名密钥,否则它将变得无效。
与用户证书相关的文件有五个。 证书颁发机构必须妥善保管并进行脱机存储。 如果丢失,则无法将新用户帐户添加到池中,并且必须建立新的证书颁发机构。 如果落入坏人之手,攻击者可以使用它将其帐户冒充为合法用户。
- 证书颁发机构 - 用于签名其他密钥的专用 SSH 密钥
下一个保存在服务器本身
- 证书公钥 - 证书颁发机构的公钥部分
最后三个文件保存在客户端系统上
- 专用 SSH 密钥 - 用于向服务器或远程设备进行身份验证的私钥
- 公钥 SSH 密钥 - 已被证书颁发机构签名的匹配公钥
- 用户证书 - 使用证书颁发机构对用户公钥生成的签名
可选地,可以使用 | ssh_config 文件将用户证书及其密钥永久地与远程服务器或设备关联,可以全局关联,也可以通过 CertificateFile 和 IdentityFile 指令逐帐户关联。
使用用户证书有四个步骤。 第一步,创建证书颁发机构,每个用户或脚本池只执行一次。 第二步,调整服务器配置,可以根据需要针对任意数量的服务器系统或远程设备重复执行。 第三个步骤,签名用户密钥,每个用户帐户只执行一次。 第四步,部署用户证书,可以针对 principals 和任何可能包含的 source-address 限制允许的任意数量的客户端执行。
为主机和用户保留单独的证书颁发机构是一个非常好的主意。 因此,为了实现特权分离,即使您已经为主机证书创建了一个证书颁发机构,也请为用户证书创建一个单独的证书颁发机构。
$ ssh-keygen -t ed25519 -f ~/.ssh/user_ca_key \
-C 'User Certificate Authority for *.example.com'
此处生成的私钥应保存在服务器以外的地方。但是,服务器将能够访问公钥组件,以便能够验证客户端提供的签名。
服务器需要能够查找匹配的证书,以验证用户密钥上的签名。这在 SSH 守护程序的配置中设置。将主机证书复制到主机密钥存储的相同位置。然后使用 |sshd_config(5) 中的 TrustedUserCAKeys 指令将 OpenSSH 服务器指向证书颁发机构的公钥组件。
TrustedUserCAKeys /etc/ssh/user_ca_key.pub
仔细检查以确保文件权限正确。
$ ls -lhn /etc/ssh/user_ca_key.pub
-rw-r--r-- 1 0 0 114B May 4 16:38 /etc/ssh/user_ca_key.pub
然后可以使用任意数量的帐户使用该证书颁发机构。
用户必须已经创建了密钥对,然后提交密钥对的公钥组件进行签名。签署它并将签署的副本传回正确的人员。立即删除此过程的任何工件,因为多余的公钥散布在周围只会造成混乱。这里一个名为 server01.ed25519.pub 的公钥已被接受,并使用它制作了一个证书。
$ ssh-keygen -s user_ca_key -I 'edcba' -z '0002' -n fred \
server01.ed25519.pub
生成的证书将被命名为 server01.ed25519-cert.pub,并将具有内部 ID "edcba" 和内部序列号 "2"。ID 和序列号必须在外部计算。对于成功的登录,证书的 id 和 serial 字段将包含在日志中。有关该主题的更多详细信息,请参见部分 日志记录和故障排除。为证书列出主体是一个非常好的主意,即使是用户证书也是如此。证书中列出的主体需要与它将登录的帐户匹配。
即使在客户端侧登录后不再严格需要公钥本身,但最好让客户端保留它。但是,如果公钥丢失,可以从私钥重新生成一个新的公钥,但反之则不行。私钥一旦丢失,就无法找回。因此,请保持适当的备份计划。如果存在一个与公钥应具有的名称相同的文件,那么它最好是公钥本身,否则登录尝试将失败。
获取公钥和交付证书的物流超出了本书的范围。但在这一点上,生成的证书应传回使用提交的密钥的人员。
在客户端侧,登录需要用户证书及其对应的私钥。
$ ssh -o CertificateFile=server01.ed25519-cert.pub -i server01.ed25519 \
[email protected]
一旦事情手动运作起来,就可以使用客户端上的 ssh_config(5) 创建一个快捷方式。如果使用代理并且代理中有多个密钥,则可能还需要使用 IdentitiesOnly。
Host server01 server01.example.org
Hostname server01.example.org
User fred
IdentitiesOnly yes
IdentityFile /home/fred/.ssh/server01.ed25519
CertificateFile /home/fred/.ssh/server01.ed25519-cert.pub
使用这些设置,在该客户端上运行 ssh server01
将尝试应用指定的密钥及其对应的用户证书和指定的主体。
OpenSSH 使用密钥的主要用途之一是将远程主机认证到客户端,以便客户端可以验证它是否直接连接到正确的系统,而不是通过中间的入侵者进行模仿。为此,一旦确认,known_hosts 文件通常会在主机密钥注册表中保留公钥的副本,与主机名或 IP 地址配对。
主机密钥通常的难点在于,为大量客户端机器填充客户端上的 known_hosts 文件,或者在首次连接到新系统时填充它。在数据中心、实验室或物联网部署中,机器始终处于不断加入和离开的状态。这意味着每次都会有新的 SSH 主机密钥。如果涉及大量主机,那么这就会加起来,在注册表中会有很多密钥。相反使用主机证书,使用相同证书颁发机构的大量任意主机池只需要在 known_hosts 注册表中进行一次条目,即使向池中添加了新主机也是如此。通过签署新服务器或设备用来标识自身的那些主机密钥,仍然可以在首次尝试时使用唯一的密钥推出新系统,但可以确保客户端能正确且安全地识别它们,而不会冒中间人攻击的风险。
通过使用主机证书,这些识别主机密钥被签署,并且签名可以根据商定的证书颁发机构进行验证,从而极大地简化了在建立第一个连接时收集和验证主机公钥的本来繁琐的过程。
使用主机证书涉及五个文件。与用户证书一样,证书颁发机构必须保密。与用户证书相同,但针对主机而不是主机上的帐户。
- 证书颁发机构 - 为签署其他密钥而生成的私钥
接下来的三个文件保存在服务器本身。
- 证书公钥 - 证书颁发机构的公钥组件
- 主机公钥 - SSH 守护程序用来标识自己到客户端的实际密钥
- 主机证书 - 使用证书颁发机构为主机公钥生成的签名
然后在客户端,要么在客户端的注册表中,要么在系统范围内的已识别主机注册表中
- known_hosts - 包含对主机证书及其主体的引用
当客户端找到并使用有效的主机证书时,不会为单个主机在 known_hosts 注册表中添加任何条目。
使用主机证书通常有四个阶段。步骤一,创建证书颁发机构,每台服务器或服务器池或设备仅执行一次。步骤二,签署主机密钥,每台服务器设备仅执行一次,步骤三也是如此。步骤四,配置客户端,可以根据需要为任意数量的客户端机器或单个登录帐户重复执行。
在每一步中,都要注意路径。没有万能的解决方案,因此需要决定文件应该放在哪里。
再说一次,证书颁发机构或 CA 只是另一个 SSH 密钥。但是,它不是直接用于对服务器或客户端进行身份验证,而是用于签署然后验证其他密钥,这些密钥实际上用于身份验证。
$ ssh-keygen -t ecdsa -f ~/.ssh/ca_key \
-C 'Host Certficate Authority for *.example.com'
此处生成的私钥应保存在将使用它们的服务器以外的地方。
此步骤中的公钥必须通过带外方式分发到客户端,然后客户端将在首次连接时使用它来验证主机身份。
只有公钥被签署。通过可靠的方法获取远程主机的公钥主机密钥,签署它,然后将生成的证书上传到服务器。-h 选项表示这将是主机证书,-s 选项指向用于签署的密钥。这里,主机密钥 ssh_host_ecdsa_key.pub 的副本已从其服务器获取,并将本地处理
$ ssh-keygen -h -s ~/.ssh/ca_key -V '+1d' -I abcd -z 00001 \
-n server.example.com ./ssh_host_ecdsa_key.pub
Enter passphrase:
Signed host key /etc/ssh/ssh_host_ecdsa_key-cert.pub: id "abcd" serial 1 for server.example.com valid from 2020-05-05T09:51:00 to 2020-05-06T09:52:01
由-V选项设置的有效期区间是相对于密钥签发日期和时间的相对时间跨度。因此,可以使用公式-V '+1d2h:+1d3h'
使证书在明天的某个时间段内有效。如果未设置开始时间,则该值将被解释为结束时间。如果需要特定的结束时间或日期,最好使用脚本计算该时间,然后调用ssh_key-gen(1)。如果根本没有提供时间,则证书将被视为无限期有效。
-I选项为证书分配一个标签,以便于识别。
-z选项手动为证书分配一个序列号。该序列号必须从旧证书中提取,然后在需要保持顺序的情况下递增。默认情况下不使用序列号。-n选项分配一组主体,即哪些主机可以在主机证书的上下文中使用该证书。
可以使用-L选项查看证书的内容。
$ ssh-keygen -L -f ssh_host_ecdsa_key-cert.pub
ssh_host_ecdsa_key-cert.pub:
Type: [email protected] host certificate
Public key: ECDSA-CERT SHA256:kVSFLH5MP/3uJWU57JxD8xVFs7ia8Pww8/ro+pq4S50
Signing CA: ECDSA SHA256:INewUSvbnfVbgUhtLBhh+XKL0uN99qbXjsi0jvD/IGI (using ecdsa-sha2-nistp256)
Key ID: "abcd"
Serial: 1
Valid: from 2020-05-05T09:51:00 to 2020-05-06T09:52:01
Principals:
server.example.com
Critical Options: (none)
Extensions: (none)
公钥主机证书必须传输到服务器可以使用的目录。这通常意味着与常规公钥主机证书相同的目录,默认情况下为/etc/ssh/。将证书复制到适当位置后,请确保其具有正确的权限。
$ ls /etc/ssh/ssh_host_ecdsa_key*.pub
ls -nlh /etc/ssh/ssh_host_ecdsa_key*.pub
-rw-r--r-- 1 0 0 653 May 4 16:49 /etc/ssh/ssh_host_ecdsa_key-cert.pub
-rw-r--r-- 1 0 0 172 Feb 21 16:09 /etc/ssh/ssh_host_ecdsa_key.pub
一旦主机证书就位,远程主机上的SSH守护进程就必须指向该主机证书。有关更多信息,请参见sshd_config(5)。
HostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub
然后必须指示SSH守护进程重新加载其配置文件。确切的方法因系统而异,但最终守护进程将收到一个HUP信号。
最后,在客户端的known_hosts文件中添加对证书颁发机构的引用
@cert-authority *.example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFJQHK0uoOpBfynyKrjF/SjsLMFewUAihosD6UL3/HkaFPI1n3XAg9D7xePyUWf8thR2e0QVl5TeGLdFiGyCgt0=
就是这样。但是,重要的是要意识到,上述每个步骤都将或多或少地静默失败,并且客户端将退回到通常的验证主机身份的方法,而不会报告任何错误。任何有用的调试信息都将记录在守护进程的日志中,即使这样也仅限于一定程度。有关示例,请参见关于日志记录和故障排除的章节。
自动部署证书(用户证书或主机证书)超出了本书的范围。有很多方法可以做到,并且涉及很多因素。具体细节不仅取决于使用哪种编排软件,还取决于端点上使用的是哪个特定发行版或操作系统。简而言之,弄清楚如何手动操作,然后弄清楚如何使用部署脚本或软件允许的工作流来自动化该过程。
但是,重要的是要注意,证书颁发机构可以保存在代理中。调查有关签署的-U选项。
可以在证书本身内部对用户证书施加各种限制。这些限制主要是在签署密钥时使用-O选项指定的,包括有效期区间、强制命令、源地址范围、禁用伪终端分配等。有关权威列表,请参见ssh-keygen(1)的手册页。这些限制可以组合使用,也可以单独使用。以下示例将为了清晰起见分别说明它们。
证书可以设置为在计划的时间段内有效,这里称为有效期区间。有效期区间可以有特定的开始日期和时间、结束日期和时间,或者两者都有。但是,每个证书可能只有一个有效期区间。
有效期区间使用-V选项指定,可以使用绝对日期和时间范围或相对时间范围。使用上面用户证书部分的密钥示例,以下将限制证书的有效期。具体而言,它在2020年6月24日下午1:55至下午2点的五分钟时间段内有效。
$ ssh-keygen -V '202006241355:202006241400' \
-s user_ca_key -I 'edcba' -z '0003' -n fred \
server01.ed25519.pub
请务必仔细查看生成的输出,以确保范围符合预期。
也可以使用相对区间。以下是一个仅在五分钟内有效的证书,立即开始
$ ssh-keygen -V ':+5m' \
-s user_ca_key -I 'edcba' -z '0004' -n fred \
server01.ed25519.pub
请注意,秒也被包含在内,并且从签署开始时开始计算,而不是从最终输入密码并完成签署时开始计算。因此,如果证书签署在分钟开始后的35秒开始,则到期时间也将是第五分钟后的35秒。并且,再次强调,请仔细查看生成的输出。
通过在创建用户证书时使用-O选项,可以将用户证书与服务器上的特定命令绑定。在本示例中,该证书仅在用于连接到SSH服务器时显示时间和日期
$ ssh-keygen -O force-command='/bin/date +"%T %F"' \
-s user_ca_key -I 'edcba' -z '0005' -n fred \
server01.ed25519.pub
如果证书和sshd_config(5)中都存在强制命令,则后者优先。任何作为运行时参数传递的命令都将被覆盖,但可以在SSH_ORIGINAL_COMMAND环境变量中找到。来自证书内部的命令不会影响SSH_ORIGINAL_COMMAND变量,必须从证书本身解析,证书将保存在SSH_USER_AUTH环境变量指向的临时文件中。
$ awk '/^publickey/ {print $2,$3}' ${SSH_USER_AUTH} \
| ssh-keygen -Lf -
该文件仅在会话仍处于打开状态时存在。SSH_USER_AUTH变量本身仅在SSH服务器的配置将ExposeAuthInfo设置为'yes'时才设置,默认值为'no'。
证书可以限制为特定的CIDR范围。
$ ssh-keygen -O source-address='198.51.100.0/24,203.0.113.0/26' \
-s user_ca_key -I 'edcba' -z '0006' -n fred \
server01.ed25519.pub
CIDR范围必须有效。
如果证书在手,则可以详细查看它,并查看哪些限制适用(证书端)。以下是一个用于帐户'fred'、两个LAN范围和仅一个多小时访问权限的证书示例。
$ ssh-keygen -Lf server01.ed25519-cert.pub
server01.ed25519-cert.pub:
Type: [email protected] user certificate
Public key: ED25519-CERT SHA256:hSy7QrAApIU1LgDCUrtBK2F2TZxhvincnJ0djSDow7I
Signing CA: ED25519 SHA256:dVgTW1INbhvHjHbeAe10R9Niu8BpejifO286RZ7/niU (using ssh-ed25519)
Key ID: "edcba"
Serial: 7
Valid: from 2020-06-24T15:17:00 to 2020-06-24T16:23:47
Principals:
fred
Critical Options:
force-command /bin/date +"%T",source-address=192.168.0.0/16,10.11.9.0/26
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
显然,证书本身无法显示SSH服务器配置中进行的任何其他服务器端限制。
如果用户证书不在手,但用于身份验证,则可以使用sshd_config(5)中的ExposeAuthInfo选项提供的SSH_USER_AUTH变量从服务器获取证书,从而了解限制和所有其他嵌入式特性。证书本身将保留SSH_ORIGINAL_COMMAND变量,因此临时文件将是查看证书中实际内容的唯一方法。再次强调,SSH_USER_AUTH变量指向的证书文件仅在会话处于打开状态时存在。
- ↑ Stephen Harris (2016-10-30). "使用 SSH 证书". Retrieved 2020-05-07.
- ↑ Maggie Danger (2014-08-07). "如何使用身份和证书强化 SSH". Magnus Achim Deininger. Retrieved 2020-05-07.
- ↑ Mike Malone (2019-09-11). "如果你没有使用 SSH 证书,你就没有正确使用 SSH". Smallstep Labs, Inc. Retrieved 2020-05-07.