第十四章:证书颁发机构

正确使用SSH最困难的部分不是软件,也不是模糊或隐藏的复选框,甚至不是更模糊的命令行参数。而是验证密钥。

用户需要验证主机密钥,这是一个繁琐的过程,他们中的大多数人甚至不会费心。用户生成身份验证密钥,但随后需要在网络中复制它们。如果你正在自动化管理你的系统,你可以通过实施OpenSSH证书颁发机构来自动化部分验证过程并大大降低风险。

SSH CA与你可能熟悉的部署在网站上的TLS的X.509证书颁发机构不同。如果你必须为每台主机购买X.509证书才能使用SSH,你就不会费心了。请花点时间考虑一下证书颁发机构为你做了什么。

证书颁发机构是一种委派信任(delegating trust)的方法。每个web浏览器都有一个内置的受信任证书颁发机构列表。当你的浏览器调用使用证书的网站时,浏览器会检查该证书是否受信任的证书颁发机构签名。如果是,浏览器信任该网站上的证书。如果证书由受信任的证书颁发机构以外的任何机构签名,则用户会看到警告。

SSH公钥类似于自签名证书。服务器正在声明,“这就是我,你可以接受或离开。”通过向客户端和服务器提供他们信任的证书来创建SSH CA。在所有OpenSSH软件上安装此证书,它将信任由该CA密钥签名的任何公钥。

X.509证书之所以复杂,部分原因是它们是全球认证网络的一部分。组织使用TLS证书来保护网站、电子邮件和几乎任何其他任意TCP连接。证书包含一大堆我们大多数人永远不需要的东西的字段。

SSH证书只需要能够对数据进行数字签名并携带少量元数据。他们不是一个全球性的实体。OpenSSH CA完全是内部的。标准SSH密钥对具有签名密钥所需的所有功能。

一旦部署了OpenSSH CA,配置为信任CA密钥的客户端将自动信任用该密钥签名的主机密钥。配置为信任CA密钥的服务器将自动信任CA密钥签名的用户身份验证密钥。你的用户只有在连接到组织外部的主机或出现严重问题时才会看到警告。

证书颁发机构是OpenSSH扩展。其他客户尚未采用它们。即使你的所有客户端都运行OpenSSH以外的东西,SSH证书颁发机构在服务器之间连接时对于验证主机密钥也很有用。

如果你不能自动将文件分发到服务器并在远程重启sshd,不建议考虑部署OpenSSH CA。如果你没有自动化,看看Ansible或它的竞争对手。

SSH CA具有一大堆仅在边缘情况下有用的功能。像谷歌或脸书这样的组织需要一大堆我们大多数人不需要的功能。阅读ssh-keygen手册页,了解完整的CA选项。在这里,我们将为中型网络设置一个相对简单的CA,从更简单的主机证书开始,然后再到更复杂的用户证书。

证书过期

签名证书的一个关键部分是它们会过期。

是的,你可以将证书设置为四分之一个世纪(quarter-century)内有效,但密钥的安全性坚持不了那么久。

应当从一开始就计划使用你的自动化系统定期更新你的证书。

在没有缺陷实现的情况下,通过计算攻击公钥可能需要数十亿年的时间。不过,软件大多都有漏洞,漏洞可能会让入侵者在更短的时间内破解密钥。此外,计算破解密钥所需的时间是平均值。入侵者可能会很幸运。永久有效的证书增加了入侵者成功的机会。

证书的有效期是多久?每年左右滚动(rolling over)证书是最常见的。如果你有一个精心编排的网络,其中服务器通过自动部署的魔力出现和消失,你可能希望每周或每月重新生成主机密钥证书。

简而言之,除非在极少数情况下无法更改主机,否则不要计划使用证书超过一年。来自雇主的压力不属于“极少数情况”。

不过,不要将证书设置为一年后到期。记住,生活是会发生的。也许你把在52周内更新所有证书的计划放在日历上,但前一天你得了阑尾炎,并且你要休息三周。建议为这种紧急情况留出至少一个月的余地,所以这些例子假设我们在五十六周零五天到期所有证书。

最好的方法是使用自动化系统在所有证书到期日的一半时进行续订和替换。每六个月颁发和部署新的一年证书。当你对自动化充满信心并解决错误时,慢慢减少时间。每年自动创建新证书,然后每月,然后每周。然后减少使用CA密钥的时间。让证书续订变得轻而易举,可以将潜在的密钥泄露从灾难(disasters)变成琐碎(trivialities)的事情。

SSH CA 密钥

在考虑创建OpenSSH CA之前,请考虑如何处理和保护这些密钥。你的证书颁发机构是你王国的钥匙。像保护其他关键信息安全资产一样保护它。

入侵你的OpenSSH CA的入侵者可以创建所有服务器都信任的用户密钥。

我将OpenSSH CA保存在专用OpenBSD机器上,该机器仅在需要对密钥进行签名的时候启动。较大的组织将希望将他们的OpenSSH CA放在网络中与其他类似关键主机相同的部分。

最佳实践建议创建两个证书颁发机构:一个用于认证主机密钥,另一个用于用户密钥。不同的团队管理用户和主机,拥有两个不同的证书颁发机构允许每个人使用最适合这些任务的工作流。每个CA甚至可能位于网络的不同部分的不同机器上。虽然你可以在主机上安装任意数量的证书颁发机构,以便以后拆分CA,但很少有系统管理员会后悔从一开始就遵守如此简单的最佳实践。

随着网络的增长,你管理的公钥数量也会增加。从一开始就组织好你的CA,以尽量减少以后的挣扎。

我建议将CA放在/usr/local/sshca这样的目录中。为主机和用户密钥创建子目录,并使用明确的名称,如/usr/local/sshca/hosts和/usr/local/sshca/users。

每个主机和用户都应该在其中获得自己的子目录,例如/usr/local/sshca/hosts/sloth和/usr/local/sshca/users/mwl。

不要将CA放在/root中,尤其不要放在/root/.ssh中。这些目录用于根账户的信息,就像/etc/ssh是为这个特定主机的ssh服务保留的一样。

证书颁发机构是一个大型项目,应该有自己的目录。

为什么按目录而不是文件名分开?每个主机和用户都有同名文件。你可以在每个ssh服务器上找到/etc/ssh/ssh_hosts_ecdsa_key,而每个用户都有一个id_rsa.pub。虽然当然可以将主机sloth上的文件复制到sloth-ssh_host_ecdsa_key,为它生成一个证书,然后在将其发送回服务器时重命名证书,但这需要几个额外的步骤。为每个主机和用户提供唯一的目录可以降低脆弱性。

创建SSH CA密钥的方式与手动创建主机密钥的方式相同。我添加了-c标志,为密钥添加了一个特殊注释。SSH CA密钥看起来像其他SSH密钥,因此注释有助于识别它们。这是一个主机密钥签名密钥。

使用一个好的密码短语。你将能够使用SSH代理进行大规模签名,因此可以随意使其变得复杂。你将获得包含证书颁发机构私钥的文件host-mwlca-key,以及包含公钥的文件host-mwlca-key.pub。

创建用户认证CA完全相同,除了文件名和注释:

保护这些私钥。不过,你可以随意用公钥向整个网络发送垃圾邮件。

信任你的证书颁发机构

ssh客户端和sshd服务器配置证书颁发机构的方式完全不同。

sshd(8) 和证书

SSH服务器使用用户证书来验证用于身份验证的证书,使用TrustedUserCAKeys关键字在sshd_config中设置一个包含所有受信任证书的文件:

CA文件每行包含一个CA公钥,并接受用前导英镑符号(#)标记的注释。它看起来完全像一个authorized_keys文件。

重启sshd服务,它将信任此证书颁发机构签名的密钥。

ssh(1) 和证书

SSH客户端使用主机证书来验证主机公钥。在known_hosts中配置受信任的主机证书颁发机构。CA密钥最有效的位置是/etc/ssh/ssh_known_hosts,这样所有客户端都能立即识别CA,用户也无法篡改密钥。

不过,不要只是将CA的公钥文件复制到known_hosts。你必须将此密钥标记为证书颁发机构,并添加此密钥有效的主机名。将公钥复制到单独的文件中——永远不要弄乱原始密钥文件!

将标记@cert-authority添加到行首,然后为该密钥有效的主机添加SSH模式。此密钥对mwl.io域中的所有主机都有效:

如果密钥对多个域有效,请用逗号分隔它们:

将此行添加到每个主机上的/etc/ssh/ssh_known_hosts。它们将立即信任用此密钥签名的证书。

常见证书注意事项

用户证书和主机证书都有一大堆共同的细节,包括序列号、证书ID和到期日期格式。

证书序列号

每个X.509证书都应该有一个序列号。这些序列号对于证书颁发机构是唯一的。OpenSSH CA支持相同的功能,但SSH并不真正需要它。如果你的组织有一些单独的需求,请使用它们。

序列好不应单调递增(increase monotonically),也就是说,不要按顺序发布。随机序列号是最好的。如果你决定在SSH证书中使用序列号,则需要一种机制生成唯一的随机数。

我会提到如何在签名密钥时列出序列号,但不会花时间在它们上面。

证书ID

每个证书都有一个 certificate identity ,一个用于说明此密钥用途的文本字符串。我根据主机名或用户名分配身份,但如果你的组织对身份有其他需求,请随时使用它。你可能会决定将其用于库存标签、人员ID号或其他任何需要的东西。在我的示例中,我使用host_和主机名作为主机证书,使用user_加用户名作为用户证书。

每当用户使用证书进行身份验证时,日志消息都会包含证书标识。有些人使用证书身份让每个人都以root身份登录,但仍然保留用户责任。

证书档案

重新生成证书只需要一个命令。你可以在网络中自动更新和分发证书和密钥。随着人们发现证书有多有用,你拥有的证书数量将成倍增加。确保你的CA保留了每个证书和相应公钥的副本。

如果你发现私钥已经泄露,则需要吊销该密钥的证书。当你手头有一份证书副本时,撤销证书要容易得多。当我创建证书时,我会创建证书及其相应公钥的副本,两者都以ISO 8601格式(数字年-月-日)的日期作为前缀。这使得查找具有特定日期的证书变得容易。

当你创建新的CA密钥并淘汰旧密钥时,你可以丢弃使用该密钥创建的证书。

设置过期日期

过期日期采用标准的Unix相对日期格式分配。不能指定具体的结束日期,而是指定证书到期的时间。

到期日期以加号开头,加上你希望的未来时间。使用w表示周、d表示天、h表示小时、m表示分钟、s表示秒。要使密钥在56周5天12小时13秒后过期,可使用 +56w5d12h13s。

主机证书

OpenSSH客户端信任由公认的证书颁发机构签名的服务器公钥。部署此操作需要创建证书并在服务器上安装这些证书。

创建主机证书

使用你的证书颁发机构对服务器的公共主机密钥进行签名。如果你已经有一个系统可以保存每个服务器的公钥文件的副本,请考虑将你的证书颁发机构放置在该主机上,或者将需要这些文件的功能移动到你的CA服务器。

使用ssh-keygen对密钥进行签名。是的,有一个ssh-keygen命令,但它是用来用ssh替换rsh的,默认情况下是禁用的。使用-s标志给出CA密钥的文件名。-I标志定义证书标识。-h标志声明这是一个主机证书,-n标志标识此证书适用的主机。(多个主机名用逗号分开)。使用-V给出到期日期,然后给出要为其创建证书的密钥文件的文件名。

以下示例,我使用CA主机mwlca密钥为主机sloth创建密钥证书,它将在五十六周零五天后到期。我为当前目录中的每个公钥文件签名,必须给出CA密钥的完整路径,但为了清楚起见,这里对路径进行了修剪:

我向这个主机复制了四个公钥,所以我为每个公钥都获得了证书。每个证书都以其公钥文件命名,并在末尾的.pub之前插入-cert。ssh_host_rsa_key.pub的证书是ssh_host_rsa_key-cert.pub,ssh_host_ecdsa_key.pub获得ssh_host_exdsa-keycert.pub,以此类推。

将所有这些证书复制到SSH服务器的/etc/ssh目录中。

安装主机证书

在服务器上安装证书后,在sshd_config中使用HostCertificate关键字对其进行配置。为免混淆,可以考虑将HostCertificate关键字放在相关HostKey关键字旁:

重启sshd,你现在已经准备好使用主机证书了。

测试主机证书

在sshd中配置证书并设置客户端的/etc/ssh/ssh_known_hosts后,你就可以尝试基于证书的主机密钥验证了。

把你的$HOME/.ssh/known_hosts文件移到一边,或者如果你真的非常有信心,删除它。现在SSH进入服务器,你应该会收到登录提示,而不会被提示验证主机密钥。

如果主机证书不能正常工作,请在ssh命令行中添加一到两个-v。客户如果没有看到证书,请在调试模式下运行sshd,查看它是否正在加载证书。如果ssh看到了证书,但无法识别,大概是ssh_known_hosts条目出问题了。

撤销证书

证书很好,但要防止被盗。在ssh_config中,使用RevokedHostKeys关键字告诉客户端不要信任公钥:

吊销的主机密钥文件包含一个公钥列表,每行一个。客户端不会接受这些公钥,即使它们有附带的证书。

你的启用需要能够更新客户端的已撤销密钥文件,并且必须定期对其进行测试。虽然实时更新是最好的,但即使是登录脚本也总比没有好。

查看证书

你可以使用ssh-keygen -L查看证书的内容。使用-f给出证书文件。

也许这里最重要的细节是密钥ID(主机名为sloth)和有效日期。如果你有多个证书颁发机构,你可能会发现签名CA字段很有用。主体字段给出了此证书有效的实体,每行一个。如果你的密钥文件太混乱了,需要将密钥字段与服务器上的密钥进行比较,请重新开始。

定义关键选项和扩展的字段对于用户证书很有用。

用户证书

用户证书比主机证书更复杂。主要是因为用户比主机更复杂。SSH用户证书允许你复制authorized_keys中的所有内容,包第12章"自动化"中讨论的限制和约束。然而,这需要更深入地研究SSH证书。

SSH证书的一个关键概念是主体。主体定义此证书用于哪些实体。对于主机证书,主体是主机名。用户证书的主体通常是证书的用户名,但它也可能包含限制、约束和其他信息。没有主体的用户证书可用于作为任何用户进行身份验证。除非你确实需要通配符身份验证证书,否则必须为每个证书分配一个主体。

我们将在本书中提到负责人(principal)。在早期,你可以将其视为用户名,但随着逐渐深入,其含义将不断扩展。

创建和查看用户证书

获取用户的公共身份验证密钥,通常是id_rsa.pub,并将其复制到你的证书颁发机构计算机上。你需要它来生成证书。公钥不是机密的,因此通过网络发送它没有风险。

创建用户证书的命令与创建主机证书非常相似。使用-s为用户CA密钥指定路径。-I标志定义证书标识,-n表示证书主体。使用-V定义过期时间。最后一个参数是要签名的公钥文件。在这里,我让我的用户CA对我的一个用户的公钥进行签名,使其对用户名djm有效。我将有效期设置为52周:

输入CA密码并获得证书文件id_rsa-cert.pub。如果你以前从未这样做过,看看证书:

虽然顶部看起来很像主机证书,但用户在下面会得到不同的信息。我们的负责人是djm,所以这个证书只对这个用户有效。我们没有关键(critical)选项,但扩展列表中列出了几个关键字。这些是授予此用户的SSH权限,我们很快就会在“受限证书”中看到。

将此证书返回给用户。

使用用户证书

将证书复制到$HOME/.ssh中。用户的公共身份验证密钥应该已经存在。

你应该已经设置了TrustedUserCAKeys关键字,如本章前面的“信任你的证书颁发机构”中所述。如果是这样,请将用户的authorized_keys文件移到一边,让用户SSH进入服务器,如果服务器配置正确,用户应该在服务器没有任何关于此特定密钥的信息的情况下进入。

如果这就是你想要的,你就完了。接下来看看一些更难得东西。

撤销用户证书

通常,你不会吊销用户证书。你可以在sshd_config文件中使用RevokedKeys 关键字撤销与证书关联的公钥。

轮换(rotate)CA的一个原因是它限制了已吊销证书列表的长度。你应该不想让五年前被盗的笔记本电脑的证书仍然存在于你的吊销证书文件中。

如果你有一个复杂的已撤销密钥列表,请调查ssh-keygen中的密钥吊销列表(Key Revocation Lists——KRLs)。

受限证书

正如你可以限制账户和密钥的访问一样,你也有权限制证书。你可以使用-O标志来执行ssh-keygen。-O标志有一个完整的可能限制列表,与第十二章中authorized_keys的限制相同。这些选项包括no-agent-forwarding、no-port-forwarding、no-pty、no-user-rc、no-x11-forwarding。所有这些no-限制都有相应的permit-版本:permit-agent-forwarding、permit-port-forwarding、permit-pty、permt-user-rc、pertmit-x11-forwarding。此外,还有source-address限制,规定了可以使用此证书进行身份验证的IP地址。还有一个clear限制(就像authorized_keys中的restrict)关闭所有特权,允许你使用permit语句选择性地打开它们。最后,force-command迫使用户运行该特定命令。

自动化的一个常见情况是,当你有一个只能运行单个任务的密钥时。我想为只能用于运行命令/usr/local/scripts/backup.sh的密钥创建一个证书。在客户端上创建一个名为backup的密钥,并将backup.pub发送到CA进行签名。我想使用clear选项擦除此证书的所有权限,然后使用force命令强制运行备份脚本。我使用-O两次来分配这些权限。否则,它看起来像任何其他用户密钥签名。

这将生成证书backup-cert.pub。看看内容:

将此密钥与刚刚创建的常规用户证书进行比较。用户密钥没有关键选项,而此证书将强制命令语句列为关键选项。如果用户密钥在扩展下具有一堆特权,则此密钥没有。此证书仅授予使用一个命令的权限。

目前没有创建SSH隧道的特定权限。如果你的组织足够大,需要证书,它应该有声明可接受的VPN类型的标准。

禁用authorized_keys

一旦你完全部署了用于用户身份验证的SSH证书,你可能会决定禁用authorized_keys文件,在sshd_config中很容易做到:

但是,如果你的客户端不支持证书,你需要为这些客户端提供一种登录方式。一些组织要求所有系统管理员使用基于Unix的桌面,这样他们就可以支持证书。像Facebook这样的大型组织不允许从客户端进行SSH,除非是向持有用户私钥和证书的中央堡垒机进行SSH。

说到Facebook,让我们谈谈他们是如何管理SSH的。

大规模SSH

谷歌、脸书和亚马逊等组织拥有数万名系统管理员和数百台服务器。想象一下,他们的LDAP目录上仅用于管理账户的负载,以及他们拥有的用户组数量。

一旦你想象过,就忘了它。

脸书的工程团队善意地发布了一篇文章,介绍他们如何使用SSH证书允许每个人以root身份登录,但通过证书主体控制人们可以访问哪些服务器。在互联网上搜索“Facebook SSH certificates",你就会得到这份文件。下面是一些概述。

没有数百万服务器和团队的组织使用用户名作为主体。然而,主体不一定是用户。你可以使用AughozizedPrincipals sshd_config关键字设置可以访问主机的主体列表,并根据角色、位置或功能开发主体。

AuthorizedprincipalsFile关键字指向一个包含主体列表的文本文件,每行一个。以下是可能出现在这样一个系统中的三个原则:

此操作将告诉sshd接受来自包含任何主体root-everywhere、europe-root或europe-database的证书的身份验证。AuthorizedPrincipalsFile关键字接受常用的令牌,因此你可以按用户名进行细分。

当用户尝试以root身份登录时,sshd会检查/etc/ssh/principals/root中允许的主体列表。

创建证书时指定主体。此证书适用于用户mwl,将此证书分配给peasants和vermin。由于有许多系统管理员,其中一些名字相同,我将员工编号和姓名存储在密钥标识中:

这是有效的,因为每当使用密钥进行身份验证时,都会记录密钥身份。以这种方式使用主体可以满足问责制(accountability)的需要。

不过,如果你使用这么多服务器,每台服务器上都有文本文件,规定谁可以登录哪个账户,这很难扩展。你可以使用AuthorizedPrincipalsCommand和AuthorizedPrincipalsCommandUser关键字来运行一个命令,该命令获取此账户的授权主体列表。这可以让拥有数百万台服务器的全球企业继续使用Microsoft Access数据库获取账户信息,或者,如果你愿意,你可以使用LDAP或Postgres等现代数据库。

CA 密钥轮换(rotation)

通过轮换(rotating)OpenSSH证书颁发机构密钥,你可以实现更高级别的证书安全性。这涉及创建新的CA密钥对,重新创建所有证书,将这些证书分发给主机和用户,以及删除旧CA的密钥。

可以在没有自动化的情况下部署SSH证书——这很痛苦,但也是可能的。在没有自动化的情况下轮换证书颁发机构并非不可能,但部署Ansible来自动化该过程要容易得多。在没有自动化得情况下,设置不要尝试转换CA密钥。

首先,生成新的CA密钥并将公钥分发到每个主机。包含受信任CA密钥的文件(/etc/ssh/ssh_known_hosts或TrustedUserCAKeys sshd_config关键字给出的文件)可以同时包含多个CA密钥。不要删除旧的CA密钥;只添加新密钥。

一旦所有主机都有了新CA的公钥,请为所有主机和/或用户重新生成证书。根据需要分发这些证书,删除用旧CA创建的证书。您的自动化系统将报告哪些主机有新文件,哪些没有。 分发新证书后,禁用所有主机上的旧CA公钥。

自动化不仅可以简化密钥轮换,还可以频繁地轮换证书颁发机构密钥。如果你有一个系统管理员团队,忘记一年到期的证书;试试你每晚更新的为期一周的主机证书!即使入侵者设法窃取了证书和公钥,他们也不可能在证书到期之前暴力破解私钥。

这是证书的基础知识。如果您有合适的环境,证书有许多小功能可能会有所帮助;在ssh-keygen(8)中了解更多信息。接下来,最后一章将带我们了解一些OpenSSH片段。