为啥 ssh 从 crontab 失败但从命令行执行时成功?

Posted

技术标签:

【中文标题】为啥 ssh 从 crontab 失败但从命令行执行时成功?【英文标题】:Why ssh fails from crontab but succedes when executed from a command line?为什么 ssh 从 crontab 失败但从命令行执行时成功? 【发布时间】:2010-10-26 13:21:52 【问题描述】:

我有一个 bash 脚本,它对远程机器执行 ssh 并在那里执行命令,例如:

ssh -nxv user@remotehost echo "hello world"

当我从命令行执行命令时,它工作正常,但在作为 crontab 的一部分执行时失败(错误代码=255 - 无法建立 SSH 连接)。详情:

...
Waiting for server public key.
Received server public key and host key.
Host 'remotehost' is known and matches the XXX host key.
...
Remote: Your host key cannot be verified: unknown or invalid host key.
Server refused our host key.
Trying XXX authentication with key '...'
Server refused our key.
...

在本地执行时,我以 root 身份运行,crontab 也以 root 身份运行。 从 crontab 和命令行执行 'id' 得到完全相同的结果:

$ id
> uid=0(root) gid=0(root) groups=0(root),...

我从一些本地机器 ssh 到运行 crond 的机器。我有 ssh 密钥和凭据,可以 ssh 到 crond 机器和脚本连接到的任何其他机器。

PS。请不要问/抱怨/评论以 root 身份执行任何操作是错误的/错误的/等等 - 这不是这个问题的目的。

【问题讨论】:

尝试使用 -v 而不是 -q --- 它仍然不起作用,但它会为您提供可能帮助您解决问题的诊断。 谢谢戴夫! - 使用 -v 执行时,我在输出中包含了有趣的部分 由于事实/问题的改变,我删除了我的答案。 【参考方案1】:

所以我遇到了类似的问题。我来到这里并看到了各种答案,但通过一些实验,我可以通过密码短语、ssh-agent 和 cron 使用 sshkey。

首先,我的 ssh 设置在我的 bash 初始化脚本中使用以下脚本。

# JFD Added this for ssh
SSH_ENV=$HOME/.ssh/environment

    # start the ssh-agent
    function start_agent 
        echo "Initializing new SSH agent..."
        # spawn ssh-agent
        /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "$SSH_ENV"
        echo succeeded
        chmod 600 "$SSH_ENV"
        . "$SSH_ENV" > /dev/null
        /usr/bin/ssh-add
    


    if [ -f "$SSH_ENV" ]; then
         . "$SSH_ENV" > /dev/null
         ps -ef | grep $SSH_AGENT_PID | grep ssh-agent$ > /dev/null || 
            start_agent;
        
   else
        start_agent;
   fi

当我登录时,我输入我的密码一次,然后它会使用 ssh-agent 自动对我进行身份验证。

ssh-agent 详细信息保存在 .ssh/environment 中。以下是该脚本的外观:

SSH_AUTH_SOCK=/tmp/ssh-v3Tbd2Hjw3n9/agent.2089; export SSH_AUTH_SOCK;
SSH_AGENT_PID=2091; export SSH_AGENT_PID;
#echo Agent pid 2091;

关于 cron,您可以通过多种方式将作业设置为普通用户。 如果您以 root 用户身份运行 crontab -e,它将设置一个 root 用户 cron。如果您以 crontab -u davis -e 运行,它将添加一个 cron 作业作为用户 ID davis。同样,如果您以用户 davis 运行并执行 crontab -e,它将创建一个以用户 davis 运行的 cron 作业。这可以通过以下条目进行验证:

30 *  *   *   *     /usr/bin/whoami

这将每 30 分钟将 whoami 的结果邮寄给用户 davis。 (我以用户 davis 的身份执行了 crontabe -e。)

如果您尝试查看哪些键被用作用户 davis,请执行以下操作:

36 *  *   *   *     /usr/bin/ssh-add -l

会失败,邮件发送的日志会说

To: davis@xxxx.net
Subject: Cron <davis@hostyyy> /usr/bin/ssh-add -l

Could not open a connection to your authentication agent.

解决方案是获取上述 ssh-agent 的 env 脚本。这是生成的 cron 条目:

55 10  *   *   *     . /home/davis/.ssh/environment; /home/davis/bin/domythingwhichusesgit.sh

这将在 10:55 运行脚本。注意领先。在脚本中。它说要在我的环境中运行此脚本,类似于 .bash 初始化脚本中的内容。

【讨论】:

【参考方案2】:

不要在没有密码的情况下公开您的 SSH 密钥。请改用ssh-cron,它允许您使用 SSH 代理安排任务。

【讨论】:

这与上面关于keychain 的答案有何不同?还是 netskink 下面的hard-coded answer? 它只是开箱即用,但在底层,它们的作用基本相同。【参考方案3】:

昨天我遇到了类似的问题...

我在一台服务器上有 cron 作业,它使用 ssh 在另一台服务器上启动一些操作...问题是用户权限和密钥...

在我的 crontab 中

* * * * * php /path/to/script/doSomeJob.php

而且它根本不起作用(没有权限)。 我尝试以特定用户身份运行 cron,该用户已连接到其他服务器

* * * * * user php /path/to/script/doSomeJob.php

但是没有效果。

最后,我导航到脚本,然后执行 php 文件,它工作了..

* * * * * cd /path/to/script/; php doSomeJob.php

【讨论】:

【参考方案4】:

keychain

以一种无痛的方式解决这个问题。它在 Debian/Ubuntu 的存储库中:

sudo apt-get install keychain

也许对于许多其他发行版(看起来它起源于 Gentoo)。

如果没有运行ssh-agent,该程序将启动一个ssh-agent,并提供可以是sourced 的shell 脚本并将当前shell 连接到这个特定的ssh-agent

对于bash,使用名为id_rsa 的私钥,将以下内容添加到您的.profile

keychain --nogui id_rsa

这将启动ssh-agent 并在重新启动后首次登录时添加id_rsa 密钥。如果密钥受密码保护,它也会要求输入密码。 不再需要使用不受保护的密钥!对于后续登录,它会识别代理并且不再要求输入密码。

另外,在.bashrc 的最后一行添加以下内容:

. ~/.keychain/$HOSTNAME-sh

这将使 shell 知道到哪里可以访问由keychain 管理的 SSH 代理。确保.bashrc 来自.profile

但是,cron 的工作似乎仍然没有看到这一点。作为补救措施,在crontab 中包含上面的行,就在您的实际命令之前:

* * * * * . ~/.keychain/$HOSTNAME-sh; your-actual-command

【讨论】:

我不认为涉及钥匙链可以以任何可靠的方式使这更容易。 @Dan-Kegel - 实际上,keychain 所做的正是下面的netskink's answer。除了这里,您不必自己编写代码。【参考方案5】:

我猜通常当您从本地机器 ssh 到运行 crond 的机器时,您的私钥会加载到 ssh-agent 中并通过连接转发。所以当你从命令行执行命令时,它会在 ssh-agent 中找到你的私钥,并用它来登录远程机器。

crond 执行命令时,它没有 ssh-agent 的访问权限,所以不能使用你的私钥。

您必须在运行 crond 的计算机上为 root 创建一个新的私钥,并将其公共部分复制到您希望 crond 登录的远程计算机上的相应 authorized_keys 文件中。

【讨论】:

Dave,如何列出在 ssh-agent 中注册的密钥?这将有助于实际检查该案例。 尝试运行 ssh-add -l(即破折号小写-L)。 嗯,很奇怪,这似乎不是真的。在我的 cron 脚本中,我在底部添加了一个简单的:ssh-agent,它似乎返回了一个有效的代理。 BUT 即使这样,我也无法使用奇怪的 crontab 进行 ssh 身份验证!我尝试了您的解决方案,但它也不起作用 顺便说一句,当我在脚本中执行 whoami 时,我的用户名不是 root。 @apouche ssh-agent 不会返回现有代理,它会尝试设置一个新代理。

以上是关于为啥 ssh 从 crontab 失败但从命令行执行时成功?的主要内容,如果未能解决你的问题,请参考以下文章

从测试视图运行时测试成功,但从测试列表编辑器或命令行运行时失败

Jenkins Maven Build Step失败但从命令行运行

Crontab could not create directory .ssh

该脚本从命令行运行,但 crontab 失败

如果分配了伪 tty,为啥通过 ssh 运行后台任务会失败?

Crontab执行脚本中的ssh命令访问被拒绝