偏执企鹅 - 管理脚本和 cron 作业的 SSH
使用不安全的协议会使您的数据和连接的机器容易受到攻击。远程服务器管理要求安全被置于首要地位。本文解释了我如何在无人值守的脚本和 cron 作业中使用 OpenSSH 的过程。
大多数读者都熟悉安全外壳 (SSH) 协议,该协议创建了一个安全隧道,用于在网络上传输命令、数据和密码。最近,我的工作场所面临着通过 cron 安全地设置脚本的一些挑战。我们使用 SSH 是因为它解决了 rsh 的主要问题;rsh 在网络上发送明文,并且具有弱主机身份验证。但我们的挑战变成了如何在无人值守的作业中使用 SSH 时处理密码提示。例如,我们运行df -k, top和swapon -s以获取远程服务器统计信息,并在出现问题时提醒我们的团队。
如果您仍然在使用 rsh,则 SSH 客户端 ssh 是脚本中的完美替代品。修改通常是次要的。例如
for host in $servers do rsh $host df -k done
简单地变为
for host in $servers do ssh $host df -k done
SSH 支持广泛的身份验证系统,最常见的是 Kerberos、Rhosts、公钥和密码。因为我们没有部署 Kerberos 基础设施,所以我们解决此问题的现成选项是在脚本中回显密码、使用 Rhosts、使用 ssh-agent 或使用公钥。
我们的首选方案存在一些挑战和弱点。首先,简单地在脚本中回显密码不是一项简单的任务,因为 SSH 不从标准输入读取。要使其这样做将需要高级脚本编写技巧。更重要的是,您需要将密码放在脚本本身或文件系统上的文件中。虽然您可以设置正确的权限,但对于有决心的入侵者来说,访问密码将是一项相对容易的任务。它可能像从备份中恢复数据,甚至在屏幕上查看密码一样简单。这种方法对我们来说不是一个选项。
其次,我们考虑了基于主机的身份验证,这是执行 rsh 命令的用户被授予访问权限的方式。由于用户是根据他们登录的主机被授予或拒绝访问权限,因此不需要密码。这种解决方案可能适用于安全问题较轻的某些情况,但确实存在冒充另一台主机、IP 欺骗和破坏 DNS 的能力。此外,由于一旦主机被成功冒充,远程主机上的所有用户都已受到威胁,因此我们决定我们需要更安全的东西。
我们的第三个选择是使用 ssh-agent。不过,在我们在此处讨论此选项之前,我们需要介绍公钥及其使用。SSH 具有使用公钥密码学的能力,而不是使用纯文本密码。这意味着当客户端连接到服务器时,它会与服务器进行对话,并根据高级数学计算证明其身份。
让我们逐步完成设置,以生成一组公钥和私钥,以允许名为 scripts 的用户从 hostA 登录到 hostB,假设该用户在两台主机上都存在
1) 生成密钥
[scripts@hostA]$ssh-keygen -t dsa Generating public/private dsa key pair. Enter file in which to save the key (/home/scripts/.ssh/id_dsa): Created directory '/home/scripts/.ssh'. Enter passphrase (empty for no passphrase): XXXX Enter same passphrase again: XXXX Your identification has been saved in /home/scripts/.ssh/id_dsa. Your public key has been saved in /home/scripts/.ssh/id_dsa.pub. The key fingerprint is: 41:03:aa:dc:cc:b9:39:50:65:bc:ee:7b:36:d2:64:7a scripts@hostA
2) 从 hostA 将公钥复制到 hostB
scp /home/scripts/.ssh/id_dsa.pub \ hostB:/home/scripts/.ssh/authorized_keys
scp 是 rcp 的加密替代品,只是以安全的方式复制文件。
authorized_keys 文件是一个文件,其中包含可以使用公钥身份验证登录到您帐户的用户的公共身份或公钥。所有用户都维护自己的 authorized_keys 文件,该文件通常位于用户主目录中的隐藏 .ssh 目录中。用户也可以在此处配置对公钥的安全限制,我们将在下面回顾这些限制。
当您首次运行 ssh-keygen 来创建公钥和私钥时,不会创建 authorized_keys 文件。作为最佳实践,我们建议此文件的权限为 600。
此时,userA 应该能够使用公钥技术在没有密码的情况下登录到 hostB。当然,现在我们仍然遇到将密码短语回显到脚本中的相同问题。正如我提到的,SSH 不从标准输入获取输入,因此这与以前的脚本编写挑战相同。为了消除不断重新输入密码的需要,SSH 配备了 ssh-agent。您可以按如下方式结合 ssh-add 使用 ssh-agent
[scripts@hostA scripts]$ ssh-agent bash [scripts@hostA .ssh]$ ssh-add id_dsa Identity added: id_dsa (id_dsa)
我们将我们的 shell 传递给 ssh-agent,它继承了我们使用 ssh-add 添加的密钥。现在我们只需要输入一次密码短语,我们就可以使用我们的密钥默认密钥 id_dsa.pub 进行身份验证。关于在与 SSH 的交互式会话中使用多个密钥的一个重要注意事项是您需要如何调用 SSH。例如,如果您创建了三个私钥——您的默认密钥 id_dsa 和另外两个名为 backup 和 monitor 的密钥,用于不同的任务——您只需使用 -i 参数调用 SSH。这样做是为了确保您在登录到远程 SSH 服务器时使用新密钥
[scripts@hostA]$ssh -i backupkey hostB
当您使用 ssh-agent 时,您可能认为您只需输入ssh -i backup即可使用您的备份身份。但这并非完全如此,因为 ssh-agent 通常使用其密钥列表顶部的密钥。要获取您在 ssh-agent 中加载的所有密钥的列表,请运行ssh-add -l以获取当前在代理中加载的所有身份的指纹列表
[scripts@hostA scripts]$ssh-add -l 1024 df:ab:8e:d7:e4:bd:35:f6:b3:2e:76:6b:dd:71:2f:fe monitor (DSA) 1024 4e:4c:00:ba:1e:5d:60:08:f2:b8:2e:d4:59:1e:ff:2f id_dsa (DSA) 1024 0a:72:24:9e:0a:cd:e2:e4:5f:93:cb:ac:66:78:03:f6 backup (DSA)
由于 ssh-agent 通常偏爱首先列出的密钥,因此它偏爱 monitor 密钥。为了能够使用 backup 密钥,您需要取消设置 shell 变量 SSH_AUTH_SOCK,然后将 SSH 指向您要使用的身份,如下所示
[scripts@hostA scripts]env -u SSH_AUTH_SOCK \ ssh -i backup hostB
完成此操作后,您将按预期使用正确的密钥。
当然,对于交互式使用,使用 ssh-agent 可以节省大量时间。但是,当在脚本中使用时,即使使用脚本来自动化大部分过程,机器启动时仍然需要人工输入密码短语至少一次。这最终成为我们使用 ssh-agent 可以实现的最佳结果。有关该主题的更多信息,请参阅 SSH,安全外壳:权威指南。最终,ssh-agent 选项也未能满足我们部署安全批处理作业的需求,因为我们的目标是完全自动化作业。
这使我们只剩下使用没有密码的公钥的选项。本文的其余部分重点介绍该设置、如何进一步保护它以及使用此设置时需要考虑的一些选项。在任何环境中,在部署或修改安全配置之前,都应进行彻底的规划和安全策略审查。
保护我们设置的第一种方法是使用from=authorized_keys 文件中的指令。语法如下所示
from="host1,host2" KEY
这意味着仅允许来自 host1 或 host2 的用户,并根据与 KEY 匹配的公钥对他们进行身份验证。例如,要将登录限制为仅来自 hostA 和 hostB 的用户脚本,authorized_keys 将如下所示
from="hostA,hostB" ssh-dss AAAAB..Aqbcw= scripts@hostA
这绝不是一个万无一失的限制。正如我之前提到的,有可能冒充另一台主机并欺骗 IP。但是,此限制增加了一层安全性,并增加了破坏我们主机所需的努力。请注意,我故意缩短了密钥(密钥很长),这是由于空间限制。请注意,from= 语法对短 DNS 名称和长 DNS 名称敏感。HostA 与 HostA.somewhere 不同。
我们保护脚本设置的第二道防线是使用command =""指令,也在 authorized_keys 文件中指定。此语法如下所示
command ="command", KEY
这告诉 SSH 运行 command 然后退出。它有效地限制了您在远程服务器上运行命令的能力。正如您可能期望的那样,您可以将这两者都组合在您的 authorized_keys 文件中;只需确保用逗号分隔选项
from="hostA,hostB",command="/bin/df -k" ssh-dss AAAAB3N...Aqbcw= userA@hostB
现在,如果有人破坏了此用户和密钥,最坏的情况是检索远程主机上磁盘空间的列表。事实上,这是您可以使用此密钥运行的唯一命令。为了安全地运行多个命令,您有几个选项。首先,考虑调用脚本而不是命令。例如,运行top, df -k和hostname来自名为 myscript.sh 的 shell 脚本,并设置command="/path/to/myscript.sh"。其次,如果您需要在一天中的不同时间对同一主机运行多个命令,您可以为您的用户创建另一个密钥。这次,使用 -f 选项来指定默认文件以外的文件
[scripts@hostA]$ssh-keygen -t dsa -f backupkey Generating public/private dsa key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in backupkey. Your public key has been saved in backupkey.pub. The key fingerprint is: 14:ac:c5:5f:65:69:2f:8d:cf:0a:70:9e:5c:4e:c7:84 scripts@hostA
您将新公钥 backupkey.pub 的内容复制到您希望它们访问的主机上的用户的 authorized_keys 文件中,就像默认密钥一样。确保为您的新密钥和您要运行的新命令设置新的command=""。
现在,您将使用 -i 参数来确保您在登录到远程 SSH 服务器时使用新密钥
[scripts@hostA]$ssh -i backupkey hostB
提示
要在远程主机上添加另一个公钥而不覆盖原始 authorized_keys 文件,您可以运行此命令
$cat ~/.ssh/newkey.pub | ssh -l user host "cat >> .ssh/authorized_keys"
最后,您可以为此任务创建一个单独的用户。例如,您可以创建一个用户来监控磁盘,一个用户来自动化备份。每种配置都有其优点和缺点。此处要回答的问题是,“您是想管理密钥还是用户帐户?”我更喜欢使用不同的密钥,并在注释中记录它们。
我们尚未考虑的 SSH 密钥的一个部分是注释字段。在 HOSTA 上创建的用户 userA 的公钥的默认注释是 userA@HOSTA。基本上,密钥之后的所有内容都作为注释被忽略。因此,为了跟踪密钥及其用途,我在远程服务器的 authorized_keys 文件中添加注释。例如
ssh-dss AAAAB3NzaC1kc3MAAA......Jw= scripts@hostC-Key used for disk monitoring
我们的第三道防线是限制我们转发的流量的能力。我们在这里有三个主要选项值得讨论。首先,no-port-forwarding 选项的含义正如其字面意思。当此密钥登录时,转发 TCP/IP 端口的能力将被拒绝。转发端口是绕过防火墙的好方法;因此,用于运行脚本的帐户应仅被授予运行必要命令的能力。转发 TCP/IP 端口的能力是一个潜在的安全问题,因此我们对其进行了限制。
其次,no-X11-forwarding 告诉 SSH 进程在登录时不要转发任何 X11 连接。任何尝试这样做都会返回错误。我们看到这只是入侵者利用我们主机的另一种方式,因此我们禁用了它。同样,我们尝试锁定登录帐户可以执行的操作,但我们也允许它执行其工作。
第三,authorized_keys 中的 no-agent-forwarding 拒绝此密钥将其 ssh-agent 和存储的密钥转发到另一台主机的能力。这降低了复杂性,也消除了潜在入侵者侵入的另一条途径。
我们要使用的 authorized_keys 文件中的最后一个选项是 no-pty 选项,它表示在登录时不要分配伪终端。非交互式命令继续使用关联的密钥工作;但是,您无法再通过交互式会话发出命令。如果入侵者获得对您的私钥的访问权限并以某种方式规避其他选项,则此选项可有效地确保他或她无法发出交互式命令来造成任何损害。
通过上述选项,我们获得了一个受到合理限制的密钥,该密钥仍然可以执行其工作。我们最终的 authorized_keys 文件如下所示
from="hostA,hostB",command="/bin/myscript.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-dss AAAAB3....o9M9qz4xqGCqGXoJw= scripts@hostA
在我们结束关于选项的讨论之前,让我们再看看两个与安全性没有直接关系的选项。在脚本中运行 SSH 时,我们使用 -q 和 -o “BatchMode=yes” 命令行选项。-q 代表静默模式。sshd 的手册页很好地总结了这一点:“没有任何内容发送到系统日志。通常,每个连接的开始、身份验证和终止都会被记录。”这对于抑制否则会被解释为命令输出的警告很有用。-o “BatchMode yes” 确保 SSH 不会提示用户。所以我们的脚本稍作更改
for host in $servers do ssh -q -o "BatchMode=yes" $host df -k done
因为我们在命令行上指定了一个选项,所以我们确信这些选项不会被覆盖,因为它们具有优先权。通常,首先查看全局客户端配置,通常是 /etc/ssh_config;然后是本地客户端配置,通常是 ~/.ssh/config;最后是命令行。由于有多个版本的 SSH 可用,请始终查阅手册页以了解正确的位置和语法。
确保为每个特定密钥设置正确的选项并使用分层安全方法,对于使您的服务器不易受到攻击大有帮助。设置尽可能少的权限可以减少成功攻击期间造成的潜在损害。使用这些方法,您的数据和网络变得更加安全,并且仍然可以高效运行。
本文的资源: /article/8400。
John Ouellette 是一名系统管理员,在 NT 和 UNIX 方面拥有九年的经验。他认为命令行是王者,并且喜欢鸡肉帕尔马干酪。可以通过 john_ouellette@yahoo.com 与他联系。