众所周知,cron 和 ssh-agent 不通信,因此 ssh 命令不能在 cron 下轻松运行。有一些涉及 keychain 等的解决方案可以做到这一点。但是就我而言,我需要在远程服务器(我通过带有代理转发的 ssh 登录)上运行一个 cron 作业,该作业将使用 ssh 在第二台远程服务器上执行。因此,我的密钥不存在于 ssh 命令发起的第一个远程服务器中。所有在 cron 下运行 ssh 的解决方案都假设原始服务器上存在 ssh 密钥。当没有 ssh 密钥时,如何使用 cron 运行 ssh 命令?我在想需要让 cron 知道 ssh-agent 变量。但我不确定如何做到这一点。有什么想法吗?
这是我在远程服务器 1 上的 crontab:
*/1 * * * * ssh root@remote2 "some command" >> /home/output.log
edit1:remote2 只是一个例子。这必须对大约 100 台服务器重复。
edit2:SSH_AUTH_SOCK 解决方案尝试
~/.ssh 文件夹的内容:
$ ls -lt /home/centos/.ssh
lrwxrwxrwx. 1 centos centos 31 24. Mär 15:13 ssh_auth_sock -> /tmp/ssh-59Ay2ronRN/agent.24129
crontab的内容:
*/1 * * * * SSH_AUTH_SOCK=/home/centos/.ssh/ssh_auth_sock;/bin/bash /home/test.sh
/home/test.sh 的内容:
#!/bin/bash
echo "`date +%FT%T` $0: test cronjob from centos .. $SSH_AUTH_SOCK" >> ${runlog}
/usr/bin/ssh remote2 "sudo less /var/log/program.log | grep 'NOT ENDING'" >>${runlog} 2>&1
${runlog} 的内容:
2022-03-24T15:22:01 /home/test.sh: test cronjob from centos ..
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
edit3:SSH_AUTH_SOCK 解决方案更新(已解决)
如下声明 SSH_AUTH_SOCK 有效:
…
SSH_AUTH_SOCK=/…
*/1 * * * * ssh …
…
答案1
解决方案
我认为需要让 cron 了解 ssh-agent 变量。
SSH_AUTH_SOCK
是相关变量。
cron 在远程服务器 1 上生成的变量ssh
不必与您在ssh
远程服务器 1 上看到的变量具有相同的值。它最终应该指向同一个套接字。继续如下:
登录到远程服务器 1(使用代理转发)后,在某个私有位置创建一个具有固定名称的符号链接。该链接应指向当前使用的套接字。由于这与 SSH 密切相关,因此您的
~/.ssh
位置很好;我假设它是私有的(权限700
)。ln -s "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
在远程服务器 1 ( ) 上的 crontab 中
crontab -e
放置以下行前行ssh
:SSH_AUTH_SOCK=/your/homedir/.ssh/ssh_auth_sock
注意 cron 既不扩展 也不
~/
扩展$HOME
,因此您需要/your/homedir/
明确指定。使用远程服务器 1 上主目录的实际路径。您的 crontab 将如下所示:… SSH_AUTH_SOCK=/… */1 * * * * ssh … …
在退出远程服务器1之前(即终止代理的转发之前),删除链接:
rm ~/.ssh/ssh_auth_sock
当您再次登录时,重复步骤 1;在注销之前,重复步骤 3;这些步骤可以自动化。您的 crontab 中的变量是静态的,无需重复步骤 2。
请注意,可靠地自动执行这些步骤通常不是一件容易的事。通常,您可以多次连接到远程服务器 1 并以任何顺序断开连接。手动处理符号链接非常简单:代码是静态的(变量部分在变量后面),您只需考虑是否、何时以及在哪个会话中使用它。
疑虑
如果没有
~/.ssh/ssh_auth_sock
或链接已断开,或者代理的转发不再,那么ssh
crontab 中的命令将失败。而且您将指定 ~100 个命令。命令打印到其 stderr 后,cron 会向您发送电子邮件吗?您重定向了 stdout,但没有重定向 stderr。命令将异步运行。s 的结果
some command
可能会在 中交错output.log
。除非您使用单独的日志;但那时(而且不仅仅是那时)……如果
some command
需要超过 1 分钟怎么办?
考虑一个检查代理是否响应的脚本(检查退出状态ssh-add -l >/dev/null 2>&1
就足够了)。考虑 GNU parallel
(比较这个答案)。问问自己,cron 是否真的适合这项任务。
脆弱性?
在您之前忽略rm ~/.ssh/ssh_auth_sock
或断开与远程服务器 1 的连接rm
将使我们的符号链接处于断开状态(套接字已移除,但链接仍然存在)。想象一下,攻击者(服务器 1 的另一个合法用户)设法预测或猜测您的悬空链接仍指向的路径。想象一下,他们在此确切路径上创建套接字并向您的用户授予权限。
在我使用 Kubuntu 中的 OpenSSH 进行的测试中,路径类似于/tmp/ssh-GjYJe19sjiAb/agent.372911
。攻击者可以ls /tmp
观察到ssh-GjYJe19sjiAb/
,我是所有者。他们无法知道下一个组件(agent.372911
)。但他们仍然可以注意到目录何时消失,他们可以将其重新创建为他们自己的。接下来,他们植入通向代理的套接字,并创建数百万个具有不同可能名称的符号链接。他们确保我的用户可以访问目录和套接字。
最终,ssh
crontab 会跟随我们的符号链接和攻击者的符号链接,使用攻击者的代理。这有多糟糕?
如果
ssh
来自 crontab 的某些程序(可能ssh
与所讨论的任务无关)使用存储在远程服务器 1 上的密钥文件,成功登录到某处并且其配置状态为AddKeysToAgent=yes
,那么该密钥将被添加到攻击者的代理中,他们将能够使用它进行身份验证。攻击者可以将其密钥添加到代理。crontab
ssh
中的 (s) 将很乐意尝试使用它们。似乎无害。如果攻击者root@remote2
通过密钥有效地识别用户(例如据我所知[email protected]
这样做),并且攻击者也是在 上识别其密钥的合法用户,则不会remote2
。通过使用ssh
他们的密钥,攻击者some command
在 上的他们的环境中执行remote2
,他们可以轻松地将任何内容打印到您的output.log
。也许在这种特殊情况下,它只是一个日志;但是一般来说,您可能希望根据获得的内容做出一些重要的决定。
希望这两种情况都不适用于你的情况。如果是这样,悬空符号链接就不是问题了。你可以在登录后、登录前执行此操作,而不是rm
在从远程服务器 1 注销之前执行此操作。rm
ln