开发人员经常需要访问某些服务器,做一些检查应用程序日志之类的工作。
一般来说,访问过程是使用公私钥加密来控制的,每位开发人员都会生成自己的公私钥对。并且,每个开发人员的公钥都会添加到他们有权访问的每台服务器上的 authorized_keys 文件中。
1. 痛苦的手动更改
到目前为止,这还没什么问题。但是,当一名开发人员离职时又会发生什么事情呢?
在这种情况下,应该从所有服务器上删除这位开发人员的公钥。根据他们有权访问的服务器数量,这可能会涉及很多工作。
更糟糕的是,如果这个环节都是手动操作的,那么操作员很有可能会忘了删除某些服务器上的公钥。也就是说,离职员工的访问权限仍然保持启用状态。
2. 替代解决方案
有一些商业和开源解决方案可以帮助我们解决这一问题。这里的基本思想是,你在这类服务上添加并维护一个密钥和访问权限列表,需要删除某个密钥时,该密钥将从所有服务器中删除。
这听起来不错,但这种方案有一个很大的缺陷:它是潜在的单一故障源。如果某人获取了对该服务的访问权限,那就意味着他可以访问你的所有服务器。而且,如果你无法访问这个服务,在最坏的情况下,甚至会无法访问所有服务器。
解决方案:签名密钥
当我遇到了这个问题时,我去 HackerNews 上问了问其他人是如何解决它的。
https://news.ycombinator.com/item?id=24157180
社区提供了一些很棒的建议和见解,而这个问题的最佳解决方案似乎是对密钥进行签名,本文会详细给大家介绍一下。
基本思想
这个方法的基本思想是:你还是要为每位开发人员生成一个公钥 - 私钥对。但是,不要把公钥上载到服务器上。
而是使用之前生成的,所谓的证书颁发机构(CA)密钥对公共密钥进行签名。这个签名就是生成了第三个证书文件,你将它还给开发人员,然后让他们放在.ssh/文件夹中,和私钥、公钥放在一起。
在服务器上,你只需告诉服务器你的 CA 的公钥,服务器就可以检测用户是否具有正确签名的证书,并且仅允许拥有这种签名证书的开发人员访问自己。
优点
签署证书时,可以定义这次签署有效的时间。因此,如果你签署的有效期为 3 个月,随后开发人员离开了公司,那么 3 个月后,他们肯定将无法访问任何服务器。
现在你会说:好吧,但我不想每 3 个月就对每个人的密钥签一次名,这个抱怨很合理。
一种办法是让这个流程自动化,例如,你可以构建服务,让用户在使用公司的电子邮件和密码授权时可以自动获得签名证书,但这不在本文的讨论范围之内。
另一种简单的替代方法是,你可以颁发有效期更长的证书。然后,如果有人离开公司,就可以撤消这个证书,也就是使其失效。你可以在服务器上放置一个无效证书列表,它们将不再接受用户访问。例如,可以通过 AWS S3 或其他存储来存放这个列表,并在每台服务器上定期创建一个 cronjob 来完成这一操作。
该怎么做?
了解了原理后,实际上做起来非常简单。
首先,你要生成一个证书颁发机构的公钥 - 私钥对,你应该把这个私钥放在非常安全的地方:
- umask 77 # you want it to be private
- mkdir ~/my-ca && cd ~/my-ca
- ssh-keygen -C CA -f ca -b 4096 # be sure to use a passphrase and store it securely
然后在你的服务器上,设置为允许由你的 CA 签名的所有用户访问该服务器:
将 CA 的公钥上传到服务器上,例如放在/etc/ssh/ca.pub
在/etc/ssh/sshd_config中添加一行,指示服务器允许访问由该证书签名的用户
- TrustedUserCAKeys /etc/ssh/ca.pub # Trust all with a certificate signed by ca.pub
为了使更改生效,你应该重新加载 ssh 服务:sudo service ssh reload。现在,如果一位开发人员生成了他的公钥 - 私钥对(例如ssh-keygen -t ecdsa -b 521),他们只需向你发送他们的公钥(请注意,你永远不需要发送任何私钥!)。然后,你只需签署他们的公钥就能生成他们的证书:
- # Inside your ~/my-ca folder, sign their public key (here: id_ecdsa.pub)
- ssh-keygen -s ca -I USER_ID -V +12w -z 1 id_ecdsa.pub
各个部分的简要说明:
- -s ca:你要使用 CA 进行签名
- -I USER_ID:你的用户 ID/ 用户名
- -V +12w:证书过期前的有效时间,这里有效期为 12 周
- -z 1:此证书的序列号,以后可用它来让这个证书无效,序列号应唯一
- id_ecdsa.pub:你要签名的开发人员的公钥
它将生成证书id_ecdsa-cert.pub,你可以将其发送给开发人员,然后将其放在〜/.ssh文件夹中的公钥 / 私钥对旁边。
改进一下
听起来不错,但是你还可以做得更好!
你的组织里可能有很多拥有不同经验水平、身处不同团队、承担不同职责的开发人员,并不是每个人都会访问相同的服务器。
这样的话,让我们在签名流程中添加角色吧。
这样,你可以在服务器上设置允许哪些角色访问服务器,并且在签名过程中可以指定要签名的开发人员的角色。
然后,这位开发人员就能访问与其角色匹配的所有服务器。
当你添加新的开发人员时,只需生成一个证书即可让他们获得授权,访问所有相关服务器,而无需在这些服务器上添加任何内容。
大致上是这样的:
带有角色的 ssh 证书签名
下面是在服务器上配置角色的方式:
首先,创建用于配置访问权限的文件夹:sudo mkdir /etc/ssh/auth_principals。在该文件夹中,你可以用允许登录服务器的用户名创建文件。例如,要对某些角色授予 root 访问权限,请添加文件/etc/ssh/auth_principals/root。
在/etc/ssh/auth_principals/root内部,你只需列出所有可以用 root 身份登录的角色,每行一个角色:
- admin
- senior-developer
最后,再在/etc/ssh/sshd_config中添加一行,在服务器上配置为使用角色:
- AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
为了使更改生效,你应该重新加载 ssh 服务:sudo service ssh reload。
下面是使用角色签署密钥的方式(它们已添加到证书中):
- ssh-keygen -s ca -I USER_ID -n ROLE1,ROLE2 -V +12w -z 2 id_ecdsa.pub
这里和之前是一样的,但带有-n ROLE1,ROLE2标志。重要提示:不同角色的逗号之间不能有空格!现在,这位开发人员可以登录 auth_principals 文件中有ROLE1或ROLE2的任何服务器,以获取他们尝试登录时使用的用户名。
注销密钥
最后,如果要使证书无效,可以通过用户名或证书的序列号(-z标志)来实现。建议你在 Excel 电子表格中列出生成的证书列表,或者根据你的具体情况来建立数据库。
- ssh-keygen -k -f revoked-keys -u -s ca list-to-revoke
当你已经有一个revoked-keys列表并想要更新它时(-u标志)就这样做。对于初始生成,请拿掉更新标志。list-to-revoke需要包含用户名(id)或序列号(生成期间为-z标志),如下所示:
- serial: 1
- id: test.user
这将撤消对序列号为 1 的证书以及 ID 为test.user的所有证书的访问权限。
为了让服务器知晓已注销的密钥,你需要将生成的 / 更新的revoked keys文件添加到/etc/ssh/revoked-keys,并在/etc/ssh/sshd_config中再次配置:
警告:确保revoked-keys文件可访问且可读,否则你可能无法访问服务器
RevokedKeys /etc/ssh/revoked-keys
3. 小结:ssh 密钥管理的好方法
我认为这种解决方案是最好用的。你可以选择通过 ssh 基于角色管理对服务器的访问权限。你只需配置一次服务器(允许哪些角色访问服务器)即可。对于新加入的开发人员,你只需要生成一个签名证书,他们就能立即访问与他们的角色 / 经验相匹配的所有相关机器。当他们离开公司时,你也可以通过一种简单的方式撤销他们的访问权限。
即使发生不幸事故,并且开发人员在未取消访问权限的情况下离开,他们的证书也会在一段时间后过期,因此他们也将自动失去访问权限。
对小型团队来说,你可以手动执行这些步骤,因为这些工作做起来非常快;然后随着你的成长,可以使用基于公司身份验证详细信息的登录服务来自动进行证书签名。