代码编织梦想

开发人员经常需要访问某些服务器,做一些检查应用程序日志之类的工作。
一般来说,访问过程是使用公私钥加密来控制的,每位开发人员都会生成自己的公私钥对。并且,每个开发人员的公钥都会添加到他们有权访问的每台服务器上的 authorized_keys 文件中。

在这里插入图片描述


1. 痛苦的手动更改

到目前为止,这还没什么问题。但是,当一名开发人员离职时又会发生什么事情呢?

在这种情况下,应该从所有服务器上删除这位开发人员的公钥。根据他们有权访问的服务器数量,这可能会涉及很多工作。

更糟糕的是,如果这个环节都是手动操作的,那么操作员很有可能会忘了删除某些服务器上的公钥。也就是说,离职员工的访问权限仍然保持启用状态。

2. 替代解决方案

有一些商业和开源解决方案可以帮助我们解决这一问题。这里的基本思想是,你在这类服务上添加并维护一个密钥和访问权限列表,需要删除某个密钥时,该密钥将从所有服务器中删除。

这听起来不错,但这种方案有一个很大的缺陷:它是潜在的单一故障源。如果某人获取了对该服务的访问权限,那就意味着他可以访问你的所有服务器。而且,如果你无法访问这个服务,在最坏的情况下,甚至会无法访问所有服务器。

2.1 解决方案:签名密钥

当我遇到了这个问题时,我去 HackerNews 上问了问其他人是如何解决它的。

https://news.ycombinator.com/item?id=24157180

社区提供了一些很棒的建议和见解,而这个问题的最佳解决方案似乎是对密钥进行签名,本文会详细给大家介绍一下。

2.2 基本思想

这个方法的基本思想是:你还是要为每位开发人员生成一个公钥 - 私钥对。但是,不要把公钥上载到服务器上。

使用ssh-keygen生成CA密钥对(ca,ca.pub)。ssh客户端的密钥对的公钥交由CA签名,而无需发给ssh服务端。签名后的公钥放在客户端~/.ssh/中即可。ca.pub发送给ssh服务端保存在/etc/ssh中并在sshd_config配置中开启功能。

而是使用之前生成的,所谓的证书颁发机构(CA)密钥对公共密钥进行签名。这个签名就是生成了第三个证书文件,你将它还给开发人员,然后让他们放在.ssh/文件夹中,和私钥、公钥放在一起。

在服务器上,你只需告诉服务器你的 CA 的公钥,服务器就可以检测用户是否具有正确签名的证书,并且仅允许拥有这种签名证书的开发人员访问自己。

2.3 优点

签署证书时,可以定义这次签署有效的时间。因此,如果你签署的有效期为 3 个月,随后开发人员离开了公司,那么 3 个月后,他们肯定将无法访问任何服务器。

现在你会说:好吧,但我不想每 3 个月就对每个人的密钥签一次名,这个抱怨很合理。

一种办法是让这个流程自动化,例如,你可以构建服务,让用户在使用公司的电子邮件和密码授权时可以自动获得签名证书,但这不在本文的讨论范围之内。

另一种简单的替代方法是,你可以颁发有效期更长的证书。然后,如果有人离开公司,就可以撤消这个证书,也就是使其失效。你可以在服务器上放置一个无效证书列表,它们将不再接受用户访问。例如,可以通过 AWS S3 或其他存储来存放这个列表,并在每台服务器上定期创建一个 cronjob 来完成这一操作。

2.4 该怎么做?

了解了原理后,实际上做起来非常简单。

首先,你要生成一个证书颁发机构的公钥 - 私钥对,你应该把这个私钥放在非常安全的地方:

umask 77                        # you want it to be private
mkdir ~/my-ca && cd ~/my-ca
# drwx------  2 root root   30 Dec 12 11:50 my-ca
ssh-keygen -C CA -f ca -b 4096  # be sure to use a passphrase and store it securely
# 命令执行完后会在当前目录下生成ca和ca.pub文件

然后在你的服务器上,设置为允许由你的 CA 签名的所有用户访问该服务器:

将 CA 的公钥上传到服务器上,例如放在/etc/ssh/ca.pub

-rw-r--r--  1 root root        728 Dec 12 15:27 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 +16w -z 1 id_ecdsa.pub

Enter passphrase: 
Signed user key id_ecdsa-cert.pub: id "test02" serial 2 valid from 2020-12-12T15:21:00 to 2021-04-03T15:22:27

各个部分的简要说明:

  • -s ca:你要使用 CA 进行签名

  • -I USER_ID:你的用户 ID/ 用户名(test02)

  • -V +16w:证书过期前的有效时间,这里有效期为 16 周

  • -z 1:此证书的序列号,以后可用它来让这个证书无效,序列号应唯一

  • id_ecdsa.pub:你要签名的开发人员的公钥

它将生成证书id_ecdsa-cert.pub,你可以将其发送给开发人员,然后将其放在〜/.ssh文件夹中的公钥 / 私钥对旁边。


或者(不考虑角色的简单情况)

ssh-keygen -s /root/ssh/CA/server_ca -I bee -n root -V +52w id_rsa.pub

Enter passphrase: 
Signed user key id_ecdsa-cert.pub: id "bee" serial 0 for root valid from 2020-12-12T20:36:00 to 2021-12-11T20:37:09

说明:

  • -s:CA 证书私钥
  • -I:识别证书的名称。 当证书用于认证时,它用于日志记录
  • -n:识别与此证书关联的名称(用户或主机)
  • -V:指定证书的有效期。 在这种情况下,我们指定证书将在一年(52周)过期
  • -O option:source-address=address_list :允许用户证书使用的客户端的地址,多个地址用逗号分隔,可以时候CIDR, 我们将设置这个来限制用户证书的使用范围。

最后生成id_rsa-cert.pub公钥签名文件。

客户端可以使用如下形式登陆

ssh -i id_dsa root@ip  
#即可登陆。
#默认会自动加载:id_dsa + id_dsa-cert.pub 登陆服务器

客户端的Python 实现

# -*- coding: utf-8 -*-
'''
依赖:
    paramiko==2.3.3 或以上版本
'''

import paramiko

from StringIO import StringIO


def test_ssh_ca_key():
    key_file = """<PRIVATE KEY>"""
    cert_pub_file = """<*-cert.pub>"""
    ip = "<ip>"
    port = 22
    pkey = paramiko.DSSKey.from_private_key(StringIO(key_file))
    pkey.load_certificate(cert_pub_file)

    # Client setup
    ssh = paramiko.SSHClient()
    ssh.load_system_host_keys()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname=ip, port=port, username='root', pkey=pkey)

    stdin, stdout, stderr = ssh.exec_command("pwd")
    print stdin, stdout, stderr


if __name__ == '__main__':
    test_ssh_ca_key()

还可参考这里

2.5 改进一下

听起来不错,但是你还可以做得更好!

你的组织里可能有很多拥有不同经验水平、身处不同团队、承担不同职责的开发人员,并不是每个人都会访问相同的服务器。

这样的话,让我们在签名流程中添加角色吧。

这样,你可以在服务器上设置允许哪些角色访问服务器,并且在签名过程中可以指定要签名的开发人员的角色。

然后,这位开发人员就能访问与其角色匹配的所有服务器。

当你添加新的开发人员时,只需生成一个证书即可让他们获得授权,访问所有相关服务器,而无需在这些服务器上添加任何内容。

大致上是这样的:
在这里插入图片描述
下面是在服务器上配置角色的方式:

首先,创建用于配置访问权限的文件夹: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的任何服务器,以获取他们尝试登录时使用的用户名。

2.6 注销密钥

最后,如果要使证书无效,可以通过用户名或证书的序列号(-z标志)来实现。建议你在 Excel 电子表格中列出生成的证书列表,或者根据你的具体情况来建立数据库。

ssh-keygen -k -f revoked-keys -u -s ca list-to-revoke

当你已经有一个revoked-keys列表并想要更新它时(-u标志)就这样做。对于初始生成,请拿掉更新标志 -u(将在当前目录下生成revoked-keys文件)。list-to-revoke需要包含用户名(id)或序列号(生成期间为-z标志),如下所示:

serial: 1

或者

id: test.user

一条记录一行。
这将撤消对序列号为 1 的证书以及 ID 为test.user的所有证书的访问权限。

为了让服务器知晓已注销的密钥,你需要将生成的 / 更新的revoked keys文件添加到/etc/ssh/revoked-keys,并在/etc/ssh/sshd_config中再次配置:

RevokedKeys /etc/ssh/revoked-keys

警告:确保revoked-keys文件可访问且可读,否则你可能无法访问服务器

-rw-r--r--  1 root root        607 Dec 12 14:36 revoked-keys

3. 小结:ssh 密钥管理的好方法

我认为这种解决方案是最好用的。你可以选择通过 ssh 基于角色管理对服务器的访问权限。你只需配置一次服务器(允许哪些角色访问服务器)即可。对于新加入的开发人员,你只需要生成一个签名证书,他们就能立即访问与他们的角色 / 经验相匹配的所有相关机器。当他们离开公司时,你也可以通过一种简单的方式撤销他们的访问权限。

即使发生不幸事故,并且开发人员在未取消访问权限的情况下离开,他们的证书也会在一段时间后过期,因此他们也将自动失去访问权限。

对小型团队来说,你可以手动执行这些步骤,因为这些工作做起来非常快;然后随着你的成长,可以使用基于公司身份验证详细信息的登录服务来自动进行证书签名。

祝你 ssh 快乐!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/beeworkshop/article/details/111058162

Ubuntu下SSH无法连接root账户-爱代码爱编程

  以前遇到过这个问题,还是说一下,给新学的一个指引。   环境:Ubuntu20.10   1.对于初学者来说,ssh连接不上,考虑是不是Ubuntu是不是没有安装ssh,查看一下ssh是否安装。 ssh -V 如果没有版本信息,则是ssh没有安装,安装ssh apt install ssh 2.安装ssh后,一般来说,客户端ssh就能连上服

记一次ssh连接失败的问题-爱代码爱编程

前言 最近在做ci和cd,cd的时候使用ssh连接到服务器的时候,出现问题ssh read: Connection reset by peer,这是咋回事前几分钟都是ok的,之后就出现这样的问题了 解题思路 检查服务器是否能ping通ping 192.186.2.33 ping是通的,因为ping是底3层网络协议,这只能证明底3层网络协议是ok的

Ansible批量化管理-爱代码爱编程

Ansible批量化管理 技能展示: 理解Ansible工作原理 配置Ansible配置文件 执行Ansible命令 编写Playbook配置文件简介 随着移动互联网、物联网、互联网+、大数据、云计算等大规模的应用,以及人们日常生活中的互联网化,互联网也逐渐地普及干家万户,互联网的发展不仅影响我们的生活,同时也影响了整个经济体。在体验互联网带来便利的同时

shell expect脚本使用ssh遍历配置文件进行远程登录执行命令-爱代码爱编程

参数说明: set:可以设置超时,也可以设置变量timeout:expect超时等待时间,默认10Sspawn:执行一个命令expect “”:匹配输出的内容exp_continue:继续执行下面匹配\r:可以理解为回车$argc:统计位置参数数量[lindex argv0]:脚本后第一个参数,类似于shell中argv0]:脚本后第一个参数,类似于sh

阿里云ECS服务器使用密钥对无密码登入-爱代码爱编程

1. 首先自己的电脑运行如下命令 ssh-keygen -t rsa //一直回车即可 执行完成后笔记本这个C:\Users\你的用户名\.ssh目录会生成一个密钥:id_rsa 与 公钥:id_rsa.pub 两个文件 2. 阿里云ECS控制台添加密钥对 阿里云ECS → 网络和安全 → 密钥对 填写 公钥:id_rsa.pub 文件内的内

树莓派的frp开机自动启动-爱代码爱编程

树莓派的frp开机自动 方法之一:使用systemctl控制开机自动启动,这个方法比较方便好用。 增加两个文件,frps.service和frpc.service,并在开机时自动运行。 1、 #sudo nano /lib/systemd/system/frps.service 在frps.service里写入以下内容: (代码/var/frp/是frp