快速、安全地打补丁:工具和方法

生成企业级 SSH 密钥并将其加载到代理中,以控制各种 Linux 主机。使用并行分布式 Shell (pdsh) 编写代理脚本,以便在您的服务器群上快速进行更改。

服务器,操作指南,安全,系统管理

计算机科学界对最近与 EternalBlue 相关的漏洞利用表示难以置信,这些漏洞利用已经摧毁了大量易受攻击的系统。SMB 漏洞利用层出不穷(最近的是在上次 DEF CON 上提出的 SMBLoris,它影响多个 SMB 协议版本,并且微软将不会发布修正补丁。使用这些工具进行的攻击 使关键基础设施瘫痪,甚至到了病人被英国国家医疗服务系统拒之门外的地步。

令人非常遗憾的是,在这次 SMB 灾难中,我们还了解到著名的 Samba 服务器在公共互联网上呈现出可利用的攻击面,其数量足以让蠕虫成功传播。我之前在 Linux Journal讨论过 SMB 安全,并且我不再认为 SMB 服务器进程应该在 Linux 上运行。

无论如何,所有架构的系统管理员都必须能够关闭易受攻击的网络服务器并快速为其打补丁。在处理大量 Linux 服务器时,通常需要速度和能力。无论是由于安全情况还是其他问题,这都无关紧要——最需要的时候不是开始构建管理工具的时候。请注意,如果发生敌对方的积极入侵,取证分析 可能是法律要求,并且在没有仔细的计划和文档的情况下,不应在受攻击的服务器上采取任何步骤。尤其是在这个黑客的新时代,计算机专业人员必须加把劲,并能够快速保护易受攻击的系统。

安全的 SSH 密钥对

对异构 UNIX 环境的严格控制必须从最佳实践使用 SSH 身份验证密钥开始。我将以一个简单的要求开始本节。SSH 私钥必须是以下三种类型之一:Ed25519、使用 E-521 曲线的 ECDSA 或 3072 位的 RSA 密钥。任何不符合这些要求的密钥都应停用(特别是,必须立即停止使用 DSA 密钥)。

Ed25519 密钥格式与 Daniel J. Bernstein 相关联,他在现代密码学领域享有盛誉,以至于该领域正变成 DJB 单一文化。Ed25519 格式旨在实现速度、安全性和尺寸经济性。如果您的所有 SSH 服务器都足够新以支持 Ed25519,那么请使用它,不要考虑其他任何东西。

关于创建 Ed25519 密钥的指南 建议在 "-o" 安全格式中使用 100 轮作为工作因子。增加轮数会提高加密密钥抵抗暴力攻击的强度(如果私钥的文件副本落入敌手),但代价是在执行 ssh-add 时解密密钥需要更多的工作和时间。虽然安全进步总是伴随着 争议和讨论,但我将在此重复该指南,并建议新创建的 SSH 密钥的最佳格式是这样的


ssh-keygen -a 100 -t ed25519

您的系统可能太旧而无法支持 Ed25519——Oracle/CentOS/Red Hat 7 存在这个问题(7.1 版本引入了支持)。如果您无法升级旧的 SSH 客户端和服务器,那么您的下一个最佳选择可能是 E-521,它在 ECDSA 密钥格式中可用。

ECDSA 曲线来自美国政府的国家标准与技术研究院 (NIST)。所有 NIST 曲线中最著名和实现最多的曲线是 P-256、P-384 和 E-521。所有三条曲线都已被各种政府实体批准用于秘密通信,但许多密码学家 越来越怀疑 P-256 和 P-384 曲线已被污染。著名密码学家 Bruce Schneier 评论道:“我不再相信这些常数。我相信 NSA 通过他们与行业的关系操纵了它们。” 然而,DJB 表示 对 E-521 曲线的有限赞扬:“公平地说,我应该提到有一个使用漂亮素数的标准 NIST 曲线,即 2521 – 1;但这个素数的大小使得它比 NIST P-256 慢得多。” 所有 NIST 曲线在“侧信道”攻击方面都比 Ed25519 存在更大的问题——P-521 当然是退步了,许多人断言没有一条 NIST 曲线是安全的。总而言之,存在一个轻微的风险,即一个强大的对手在 P-256 和 P-384 曲线方面具有优势,因此人们略微倾向于避开它们。请注意,即使您的 OpenSSH(源代码)版本能够支持 E-521,它也可能因专利问题而 被您的供应商禁用,因此 E-521 在这种情况下不是一个选项。如果您不能使用 DJB 的 2255 – 19 曲线,则此命令将在能够支持的系统上生成 E-521 密钥


ssh-keygen -o -a 100 -b 521 -t ecdsa

然后,在 SSH 服务器既不支持 ECDSA 也不支持 Ed25519 的不幸情况下。在这种情况下,您必须回退到密钥尺寸更大的 RSA。绝对最小值是现代默认值 2048 位,但 3072 位是更明智的选择


ssh-keygen -o -a 100 -b 3072 -t rsa

然后在所有最可悲的情况下,当您必须使用无法使用使用 -o 选项创建的私钥的旧 SSH 客户端时,您可以删除 id_rsa 上的密码并创建一个裸密钥,然后使用 OpenSSL 以 PKCS#8 格式使用 AES256 对其进行加密,正如 Martin Kleppmann 首次记录的那样。为下面的密钥生成实用程序提供一个空白的新密码,然后在 OpenSSL 重新处理密钥时提供一个新密码


$ cd ~/.ssh

$ cp id_rsa id_rsa-orig

$ ssh-keygen -p -t rsa
Enter file in which the key is (/home/cfisher/.ssh/id_rsa):
Enter old passphrase:
Key has comment 'cfisher@localhost.localdomain'
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.

$ openssl pkcs8 -topk8 -v2 aes256 -in id_rsa -out id_rsa-strong
Enter Encryption Password:
Verifying - Enter Encryption Password:

mv id_rsa-strong id_rsa
chmod 600 id_rsa

在较新的系统上创建所有这些密钥后,您可以比较文件大小


$ ll .ssh
total 32
-rw-------. 1 cfisher cfisher  801 Aug 10 21:30 id_ecdsa
-rw-r--r--. 1 cfisher cfisher  283 Aug 10 21:30 id_ecdsa.pub
-rw-------. 1 cfisher cfisher  464 Aug 10 20:49 id_ed25519
-rw-r--r--. 1 cfisher cfisher  111 Aug 10 20:49 id_ed25519.pub
-rw-------. 1 cfisher cfisher 2638 Aug 10 21:45 id_rsa
-rw-------. 1 cfisher cfisher 2675 Aug 10 21:42 id_rsa-orig
-rw-r--r--. 1 cfisher cfisher  583 Aug 10 21:42 id_rsa.pub

尽管它们相对庞大,但我使用过的所有 OpenSSH 版本都与 PKCS#8 格式的 RSA 私钥兼容。Ed25519 公钥现在足够小,可以容纳在 80 列中而不会换行,并且它既方便又高效且安全。

请注意,PuTTY 在使用这些密钥的各种版本时可能会遇到问题,您可能需要删除密码才能成功导入到 PuTTY 代理中。

这些密钥代表了各种 OpenSSH 修订版可用的最安全格式。它们实际上并非旨在用于 PuTTY 或其他通用交互活动。虽然人们希望所有用户在所有情况下都创建强密钥,但这些是用于主要系统活动的企业级密钥。然而,明智的做法可能是重新生成您的系统主机密钥以符合这些指南。

这些密钥格式可能很快就会改变。量子计算机越来越令人担忧,因为它们有能力运行 Shor 算法,该算法可用于在合理的时间内找到素因子来破解这些密钥。最大的商用量子计算机 D-Wave 2000Q,实际上 为这项活动提供了不到 200 个量子比特,这(还)不够强大,无法成功攻击。NIST 宣布了一项竞赛,征集一种新的抗量子公钥系统,截止日期为 2017 年 11 月。作为回应,包括 DJB 在内的一个团队发布了 NTRU Prime 的源代码。看起来我们很可能会在未来两年内看到 OpenSSH(以及可能还有 TLS 1.3)的后量子公钥格式发布,因此现在就采取措施以简化迁移。

此外,对于 SSH 服务器来说,限制其允许的密码、MAC 和密钥交换也很重要,以免强密钥浪费在损坏的加密算法上(3DES、MD5 和 arcfour 应该早已禁用)。我之前关于这个主题的 指南 涉及 SSH 客户端和服务器配置中的以下(三)行(请注意,sshd_config 文件中的格式要求所有参数都在同一行,选项中没有空格;此处添加了换行符以提高清晰度)


Ciphers chacha20-poly1305@openssh.com,
        aes256-gcm@openssh.com,
        aes128-gcm@openssh.com,
        aes256-ctr,
        aes192-ctr,
        aes128-ctr

MACs    hmac-sha2-512-etm@openssh.com,
        hmac-sha2-256-etm@openssh.com,
        hmac-ripemd160-etm@openssh.com,
        umac-128-etm@openssh.com,
        hmac-sha2-512,
        hmac-sha2-256,
        hmac-ripemd160,
        umac-128@openssh.com

KexAlgorithms curve25519-sha256@libssh.org,
              diffie-hellman-group-exchange-sha256

自上次发布以来,RIPEMD160 可能不再安全,应将其删除。然而,较旧的系统可能仅支持 SHA1、MD5 和 RIPEMD160。当然要删除 MD5,但 PuTTY 用户可能希望在没有更新的 MAC 可用时保留 SHA1。当与现代系统一起工作时,较旧的服务器在找到合理的 Cipher/MAC/KEX 时可能会带来挑战。

至此,您应该拥有用于安全客户端和服务器的强密钥。现在让我们将它们投入使用。

编写 SSH 代理脚本

现代 OpenSSH 发行版包含 ssh-copy-id shell 脚本,以便轻松分发密钥。下面是在远程帐户中安装特定命名密钥的示例


$ ssh-copy-id -i ~/.ssh/some_key.pub person@yourserver.com
ssh-copy-id: INFO: Source of key(s) to be installed:
   "/home/cfisher/.ssh/some_key.pub"
ssh-copy-id: INFO: attempting to log in with the new key(s),
   to filter out any that are already installed
ssh-copy-id: INFO: 1 key(s) remain to be installed --
   if you are prompted now it is to install the new keys
person@yourserver.com's password:

Number of key(s) added: 1

Now try logging into the machine, with:
   "ssh 'person@yourserver.com'"
and check to make sure that only the key(s) you wanted were added.

如果您没有 ssh-copy-id 脚本,您可以使用以下命令手动安装密钥


$ ssh person@yourserver.com 'cat >> ~/.ssh/authorized_keys' < \
      ~/.ssh/some_key.pub

如果启用了 SELinux,您可能必须使用安全类型标记新创建的 authorized_keys 文件;否则,sshd 服务器守护进程将无法读取密钥(系统日志可能会报告此问题)


$ ssh person@yourserver.com 'chcon -t ssh_home_t
 ↪~/.ssh/authorized_keys'

安装密钥后,使用 -i 选项对其进行一次性使用测试(请注意,您输入的是本地密钥密码,而不是远程身份验证密码)


$ ssh -i ~/.ssh/some_key person@yourserver.com
Enter passphrase for key '/home/v-fishecj/.ssh/some_key':
Last login: Wed Aug 16 12:20:26 2017 from 10.58.17.14
yourserver $

一般的交互用户可能会使用代理缓存他们的密钥。在下面的示例中,在上一节中创建的所有三种类型的密钥上都使用了相同的密码


$ eval $(ssh-agent)
Agent pid 4394

$ ssh-add
Enter passphrase for /home/cfisher/.ssh/id_rsa:
Identity added: ~cfisher/.ssh/id_rsa (~cfisher/.ssh/id_rsa)
Identity added: ~cfisher/.ssh/id_ecdsa (cfisher@init.com)
Identity added: ~cfisher/.ssh/id_ed25519 (cfisher@init.com)

上面的第一个命令启动了一个用户代理进程,该进程将环境变量(名为 SSH_AGENT_SOCKSSH_AGENT_PID)注入到父 shell 中(通过 eval)。shell 意识到代理并将这些变量传递给它从那时起运行的程序。

启动后,ssh-agent 没有凭据,无法促进 SSH 活动。它必须通过添加密钥来准备就绪,这可以通过 ssh-add 完成。在没有参数的情况下调用时,将读取所有默认密钥。也可以调用它来添加自定义密钥


$ ssh-add  ~/.ssh/some_key
Enter passphrase for /home/cfisher/.ssh/some_key:
Identity added: /home/cfisher/.ssh/some_key
 ↪(cfisher@localhost.localdomain)

请注意,代理不会保留密钥上的密码。ssh-add 使用您在运行时输入的所有密码来解密它找到的密钥,但是当 ssh-add 终止时,密码将从内存中清除(它们不会发送到 ssh-agent)。这允许您以最小的不便升级到新的密钥格式,同时保持密钥的合理安全。

当前缓存的密钥可以使用 ssh-add -l 列出(从中您可以推断出 "some_key" 是 Ed25519)


$ ssh-add -l
3072 SHA256:cpVFMZ17oO5n/Jfpv2qDNSNcV6ffOVYPV8vVaSm3DDo
     /home/cfisher/.ssh/id_rsa (RSA)
521 SHA256:1L9/CglR7cstr54a600zDrBbcxMj/a3RtcsdjuU61VU
     cfisher@localhost.localdomain (ECDSA)
256 SHA256:Vd21LEM4lixY4rIg3/Ht/w8aoMT+tRzFUR0R32SZIJc
     cfisher@localhost.localdomain (ED25519)
256 SHA256:YsKtUA9Mglas7kqC4RmzO6jd2jxVNCc1OE+usR4bkcc
     cfisher@localhost.localdomain (ED25519)

当“准备就绪”的代理正在运行时,SSH 客户端可以流畅地使用(信任)远程服务器,而无需进一步提示输入凭据


$ sftp person@yourserver.com
Connected to yourserver.com.
sftp> quit

$ scp /etc/passwd person@yourserver.com:/tmp
passwd                              100% 2269    65.8KB/s   00:00

$ ssh person@yourserver.com
   (motd for yourserver.com)
$ ls -l /tmp/passwd
-rw-r--r--  1 root  wheel  2269 Aug 16 09:07 /tmp/passwd
$ rm /tmp/passwd
$ exit
Connection to yourserver.com closed.

OpenSSH 代理可以锁定,从而阻止进一步使用它持有的凭据(这可能适用于挂起笔记本电脑时)


$ ssh-add -x
Enter lock password:
Again:
Agent locked.

$ ssh yourserver.com
Enter passphrase for key '/home/cfisher/.ssh/id_rsa': ^C

当解锁时,它将再次提供凭据


$ ssh-add -X
Enter lock password:
Agent unlocked.

您还可以使用 -t 选项设置 ssh-agent 在时间限制后过期密钥,这对于必须在设定的每日轮班后清除密钥的长期运行的代理可能很有用。

通用 shell 用户可以使用许多不同的代理实现缓存多种类型的密钥。除了标准的 OpenSSH 代理之外,用户可能还依赖 PuTTY 的 pageant.exe、GNOME 密钥环或 KDE Kwallet 等(PUTTY 代理的使用可能会单独写一篇文章)。

然而,这里的目标是为关键服务器控制创建“企业”密钥。您可能不希望使用长期运行的代理,以限制暴露风险。当使用“企业”密钥编写脚本时,您将仅在活动期间运行代理,然后在完成时将其终止。

有一些特殊选项可用于使用 OpenSSH 访问 root 帐户——可以将 PermitRootLogin 参数添加到 sshd_config 文件中(通常位于 /etc/ssh 中)。它可以设置为简单的 yesnoforced-commands-only,这将只允许执行显式授权的程序,或者等效的选项 prohibit-passwordwithout-password,这两个选项都允许访问此处生成的密钥。

许多人认为不应允许 root 访问。 Michael W. LucasSSH Mastery 中解决了这个问题

有时,似乎您需要允许用户以 root 身份 SSH 进入系统。在几乎所有环境中,这都是一个非常糟糕的主意。当用户必须以普通用户身份登录,然后更改为 root 时,系统日志会记录用户帐户,从而提供问责制。以 root 身份登录会破坏该审计跟踪... 可以覆盖安全预防措施,并使 sshd 允许直接以 root 身份登录。这是一个如此糟糕的主意,以至于如果我告诉您如何操作,我会认为自己犯了渎职罪。通过 SSH 以 root 身份登录几乎总是意味着您正在解决错误的问题。退后一步,寻找其他方法来实现您的目标。

当需要在多台服务器上快速执行 root 操作时,上述建议可能会造成痛苦的延迟。Lucas 的直接批评可以通过仅允许有限的一组“堡垒”服务器通过 SSH 发出 root 命令来解决。应强制管理员使用非特权帐户登录堡垒,以建立问责制。

然而,远程“更改为 root”的一个问题是 Viterbi 算法的统计使用。短密码、su - 命令和使用密码建立三元网络配置的远程 SSH 调用都特别容易受到用户键盘移动的定时攻击。那些具有最高安全顾虑的人将需要进行补偿。

对于我们其他人,我建议为所有目标机器设置 PermitRootLogin without-password

最后,您可以使用 -k 选项以交互方式轻松终止 ssh-agent


$ eval $(ssh-agent -k)
Agent pid 4394 killed

考虑到这些工具及其预期用途,这是一个完整的脚本,该脚本在用户列表中的一组服务器上运行一组命令期间运行代理,用于常见的命名用户(不一定是 root)


# cat artano

#!/bin/sh

if [[ $# -lt 1 ]]; then echo "$0 - requires commands"; exit; fi

R="-R5865:127.0.0.1:5865" # set to "-2" if you don't want
 ↪port forwarding

eval $(ssh-agent -s)

function cleanup { eval $(ssh-agent -s -k); }

trap cleanup EXIT

function remsh { typeset F="/tmp/${1}" h="$1" p="$2";
 ↪shift 2; echo "#$h"
 if [[ "$ARTANO" == "PARALLEL" ]]
 then ssh "$R" -p "$p" "$h" "$@" < /dev/null >>"${F}.out"
  ↪2>>"${F}.err" &
 else ssh "$R" -p "$p" "$h" "$@"
 fi }    # HOST                                          PORT CMD

if ssh-add ~/.ssh/master_key
then remsh yourserver.com                                  22 "$@"
     remsh container.yourserver.com                      2200 "$@"
     remsh anotherserver.com                               22 "$@"
     # Add more hosts here.
else echo Bad password - killing agent. Try again.
fi

wait

#######################################################################
# Examples:           # Artano is an epithet of a famous mythical being
# artano 'mount /patchdir'      # you will need an fstab entry for this
# artano 'umount /patchdir'
# artano 'yum update -y 2>&1'
# artano 'rpm -Fvh /patchdir/\*.rpm'
#######################################################################

默认情况下,此脚本按顺序在一组主机上运行所有命令。如果 ARTANO 环境变量设置为 PARALLEL,它将改为同时将它们全部作为后台进程启动,并将其 STDOUTSTDERR 附加到 /tmp 中的文件中(当处理合理服务器上的少于一百台主机时,这应该没有问题)。PARALLEL 设置不仅对于更快地推送更改很有用,而且对于收集审计结果也很有用。

下面是使用 yum update 代理的示例。此特定调用的源必须遍历防火墙,并且依赖于 /etc/yum.conf 文件中的代理设置,该文件使用了上面的端口转发选项 (-R)


# ./artano 'yum update -y 2>&1'
Agent pid 3458
Enter passphrase for /root/.ssh/master_key:
Identity added: /root/.ssh/master_key (/root/.ssh/master_key)
#yourserver.com
Loaded plugins: langpacks, ulninfo
No packages marked for update
#container.yourserver.com
Loaded plugins: langpacks, ulninfo
No packages marked for update
#anotherserver.com
Loaded plugins: langpacks, ulninfo
No packages marked for update
Agent pid 3458 killed

该脚本可用于更通用的维护功能。运行 XFS 文件系统的 Linux 安装应定期“碎片整理”。虽然这通常可以通过 cron 完成,但它可以是一个集中式活动,存储在一个单独的脚本中,该脚本仅包含在适当的主机上


&1'
Agent pid 7897
Enter passphrase for /root/.ssh/master_key:
Identity added: /root/.ssh/master_key (/root/.ssh/master_key)
#yourserver.com
#container.yourserver.com
#anotherserver.com
Agent pid 7897 killed

收集所有用户的所有 authorized_keys 文件内容的一种简单方法是以下 artano 脚本(这对于系统审计很有用,并且被编码为删除文件重复项)


artano 'awk -F: {print\$6\"/.ssh/authorized_keys\"} \
     /etc/passwd | sort -u | xargs grep . 2> /dev/null'

为远程节点配置 NFS 挂载以进行文件分发非常方便。请记住,NFS 是明文的,敏感内容不应在未加密的情况下遍历不受信任的网络。在主机 1.2.3.4 上配置 NFS 服务器后,我在所有客户端的 /etc/fstab 文件中添加以下行并创建 /patchdir 目录。更改后,如果网络配置正确,则可以使用 artano 脚本批量挂载目录


# tail -1 /etc/fstab
1.2.3.4:/var/cache/yum/x86_64/7Server/ol7_latest/packages
 ↪/patchdir nfs4 noauto,proto=tcp,port=2049 0 0

假设 NFS 服务器已挂载,则可以从存储在其上的映像升级 RPM(请注意,Oracle Spacewalk 或 Red Hat Satellite 可能是更强大的补丁方法)


# ./artano 'rpm -Fvh /patchdir/\*.rpm'
Agent pid 3203
Enter passphrase for /root/.ssh/master_key:
Identity added: /root/.ssh/master_key (/root/.ssh/master_key)
#yourserver.com
Preparing...                          ########################
Updating / installing...
xmlsec1-1.2.20-7.el7_4                ########################
xmlsec1-openssl-1.2.20-7.el7_4        ########################
Cleaning up / removing...
xmlsec1-openssl-1.2.20-5.el7          ########################
xmlsec1-1.2.20-5.el7                  ########################
#container.yourserver.com
Preparing...                          ########################
Updating / installing...
xmlsec1-1.2.20-7.el7_4                ########################
xmlsec1-openssl-1.2.20-7.el7_4        ########################
Cleaning up / removing...
xmlsec1-openssl-1.2.20-5.el7          ########################
xmlsec1-1.2.20-5.el7                  ########################
#anotherserver.com
Preparing...                          ########################
Updating / installing...
xmlsec1-1.2.20-7.el7_4                ########################
xmlsec1-openssl-1.2.20-7.el7_4        ########################
Cleaning up / removing...
xmlsec1-openssl-1.2.20-5.el7          ########################
xmlsec1-1.2.20-5.el7                  ########################
Agent pid 3203 killed

我假设我的听众已经熟悉他们首选平台的软件包工具。然而,为了避免批评我很少实际讨论补丁工具,以下是 RPM 操作命令的快速参考,这是企业系统上最常见的软件包格式

  • rpm -Uvh package.i686.rpm — 安装或升级软件包文件。

  • rpm -Fvh package.i686.rpm — 如果安装了旧版本,则升级软件包文件。

  • rpm -e package — 删除已安装的软件包。

  • rpm -q package — 列出已安装的软件包名称和版本。

  • rpm -q --changelog package — 打印已安装软件包的完整变更日志(包括 CVE)。

  • rpm -qa — 列出系统上所有已安装的软件包。

  • rpm -ql package — 列出已安装软件包中的所有文件。

  • rpm -qpl package.i686.rpm — 列出软件包文件中包含的文件。

  • rpm -qi package — 打印已安装软件包的详细描述。

  • rpm -qpi package — 打印软件包文件的详细描述。

  • rpm -qf /path/to/file — 列出安装了特定文件的软件包。

  • rpm --rebuild package.src.rpm — 解包并在 /usr/src/redhat 下构建二进制 RPM。

  • rpm2cpio package.src.rpm | cpio -icduv — 解包当前目录中的所有软件包文件。

编写 SSH 代理脚本的另一个重要考虑因素是限制授权密钥的功能。有一个 特定的语法 用于此类限制。特别感兴趣的是 from="" 子句,它将密钥上的登录限制为有限的主机集。明智的做法可能是声明一组“堡垒”服务器,这些服务器将记录非 root 登录,这些登录会升级为使用企业密钥的受控用户。

一个示例条目可能是以下内容(请注意,我已断开此行,这是不允许的语法,但此处是为了清晰起见)


from="*.c2.security.yourcompany.com,4.3.2.1" ssh-ed25519
 ↪AAAAC3NzaC1lZDI1NTE5AAAAIJSSazJz6A5x6fTcDFIji1X+
↪svesidBonQvuDKsxo1Mx

可以对 authorized_keys 条目施加许多其他有用的约束。command="" 将密钥限制为单个程序或脚本,并将 SSH_ORIGINAL_COMMAND 环境变量设置为客户端尝试的调用——如果该变量不包含批准的内容,脚本可以设置警报。restrict 选项也值得考虑,因为它禁用了大量 SSH 功能,这些功能可能既多余又危险。

虽然可以在 known_hosts 文件中将服务器标识密钥设置为 @revoked 状态,但这不能使用 authorized_keys 的内容来完成。但是,可以使用 RevokedKeys 在 sshd_config 中设置系统范围的禁用密钥文件。此文件会覆盖任何用户的 authorized_keys。如果设置了此选项,则此文件必须存在并且可由 sshd 服务器进程读取;否则,将根本不接受任何密钥(因此,如果在物理访问存在障碍的机器上配置它,请谨慎使用)。设置此选项后,当应禁止从网络访问密钥时,使用 artano 脚本快速将禁用的密钥附加到文件中。一个清晰且方便的文件位置是 /etc/ssh/revoked_keys。

还可以为 OpenSSH 建立本地证书颁发机构 (CA),这将 允许密钥在权威机构注册 并带有到期日期。这些 CA 在控制企业方面可以 变得非常复杂。虽然 SSH CA 的维护超出了本文的范围,但此类 CA 颁发的密钥应通过遵守 Ed25519/E-521/RSA-3072 的要求而变得强大。

pdsh

存在许多用于控制服务器集合的更高级别工具,这些工具比我在此处介绍的脚本复杂得多。最著名的是 Puppet,它是一个基于 Ruby 的配置管理系统,用于企业控制。Puppet 支持的操作系统列表相对较短。如果您正在寻找对 Android、Tomato、Linux 智能终端或其他“异构”POSIX 的底层控制,则 Puppet 可能不是合适的工具。另一个流行的基于 Ruby 的工具是 Chef,它以其复杂性而闻名。Puppet 和 Chef 都需要在客户端和服务器上安装 Ruby,并且它们都会编目它们找到的任何 SSH 密钥,因此此密钥强度讨论完全适用于它们。

有几个类似的基于 Python 的工具,包括 AnsibleBcfg2FabricSaltStack。在这些工具中,只有 Ansible 可以通过裸 SSH 连接“无代理”运行;其余工具将需要运行在目标节点上的代理(并且这可能包括 Python 运行时)。

另一个流行的配置管理工具是 CFEngine,它用 C 语言编写,并声称具有非常高的性能。 Rudder 从 CFEngine 的一部分发展而来,并拥有一个规模虽小但不断增长的用户社区。

大多数先前提及的软件包都以商业方式获得许可,有些是闭源的。

与此处介绍的活动最接近的底层工具是并行分布式 Shell (pdsh),可以在 EPEL 存储库 中找到。pdsh 实用程序从 IBM 开发的名为 dsh 的软件包发展而来,该软件包专为控制计算集群而设计。从存储库安装以下软件包以使用 pdsh


# rpm -qa | grep pdsh
pdsh-2.31-1.el7.x86_64
pdsh-rcmd-ssh-2.31-1.el7.x86_64

在使用带有加密密钥的 pdsh 时,必须运行 SSH 代理,并且没有明显的方法可以像使用 artano 脚本那样控制每个主机的目标端口。下面是使用 pdsh 在三台远程服务器上运行命令的示例


# eval $(ssh-agent)
Agent pid 17106

# ssh-add  ~/.ssh/master_key
Enter passphrase for /root/.ssh/master_key:
Identity added: /root/.ssh/master_key (/root/.ssh/master_key)

# pdsh -w hosta.com,hostb.com,hostc.com uptime
hosta: 13:24:49 up 13 days,  2:13, 6 users, load avg: 0.00, 0.01, 0.05
hostb: 13:24:49 up  7 days, 21:15, 5 users, load avg: 0.05, 0.04, 0.05
hostc: 13:24:49 up  9 days,  3:26, 3 users, load avg: 0.00, 0.01, 0.05

# eval $(ssh-agent -k)
Agent pid 17106 killed

上面的 -w 选项定义了主机列表。它允许有限的算术扩展,如果参数为破折号 (-),则可以从标准输入中获取主机列表。PDSH_SSH_ARGSPDSH_SSH_ARGS_APPEND 环境变量可用于将自定义选项传递给 SSH 调用。默认情况下,将并行启动 32 个会话,并且将通过在现有连接完成并关闭时启动新的主机调用来维护此“扇出/滑动窗口”。您可以使用 -f 选项或 FANOUT 环境变量来调整“扇出”的大小。有趣的是,有两个文件复制命令:pdcprpdcp,它们类似于 scp

即使像 pdsh 这样的底层实用程序也缺乏通过编写 OpenSSH 脚本可获得的一些灵活性,因此当引入更复杂的工具时,请准备好感受到更大的约束。

结论

现代 Linux 在各种平台上以多种方式触及我们。当这些系统的安全性未得到维护时,其他人也可能会触及我们的平台并将其转而反对我们。当您向环境中添加任何 Linux 平台时,意识到维护义务非常重要。这种义务始终存在,并且在未满足时会产生后果。

在安全紧急情况下,简单、开放且易于理解的工具是最好的。随着工具复杂性的增加,平台可移植性肯定会下降,有能力的管理员数量也会减少,这可能会影响执行速度。这在许多其他方面可能是合理的权衡,但在安全上下文中,它需要更仔细的分析。紧急措施必须记录在案,并且比正常操作需要更广泛的受众理解,而使用更通用的工具可以促进这种讨论。

我希望此处介绍的技术将促使那些尚未面临这种情况的人进行讨论。

免责声明

本文中表达的观点和意见是作者的观点和意见,不一定反映 Linux Journal 的观点和意见。

注意

最近演示了一个 损害 Ed25519 的漏洞,该漏洞依赖于自定义硬件更改来派生可用部分的密钥。物理硬件安全是加密完整性的基本要求,许多常用算法更容易受到缓存定时或其他侧信道攻击的影响,这些攻击可以由其他用户的非特权进程执行。在授予访问处理敏感数据的系统时,请务必谨慎。

Charles Fisher 拥有爱荷华大学电气工程学位,并在一家财富 500 强矿业和制造公司担任系统和数据库管理员。

加载 Disqus 评论