我一直在努力使用 crontab 自动执行使用 ssh 的 git push 过程,事实证明使用 ssh 代理设置密钥非常棘手。从测试代理的基本脚本开始:
# set paths (all examples)
source /etc/profile
export PATH=$PATH:/Users/myusername/.ssh
eval "$(ssh-agent)" > ssh_agent.txt
/usr/bin/ssh-add -l >> ssh_agent.txt
..输出以下内容:
Agent pid 16062
The agent has no identities.
因此代理处于活动状态。现在添加密钥并检查它是否已在代理中注册。在 iTerm2 中手动执行此操作即可:
/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
2048 [key fingerprint] /Users/myusername/.ssh/id_rsa (RSA)
我尝试使用以下脚本在 crontab 作业中复制此操作:
/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
/usr/bin/ssh-add -l >> ssh_agent.txt
..报告The agent has no identities
给文件,并通过以下内容"You have mail in /var/mail/myusername"
:
[email metadata]
Enter passphrase for /Users/myusername/.ssh/id_rsa:
因此密钥正在尝试加载,但在密码提示符处卡住了。在这里我想使用来expect
给它设置密码,如下所述unix.stackexchange 答案(看起来它应该可以在 OSX 上运行):
#!/usr/bin/expect -f
/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
expect "Enter passphrase for /Users/myusername/.ssh/id_rsa:"
send "mypassphrase\n";
interact
邮件错误响应是:
Enter passphrase for /Users/myusername/.ssh/id_rsa:
couldn't read file "Enter passphrase for /Users/myusername/.ssh/id_rsa:": no such file or directory
/Users/myusername/test/crontest.sh: line 17: send: command not found
/Users/myusername/test/crontest.sh: line 18: interact: command not found
所以我尝试了基于此的替代配置所以回答:
#!/usr/bin/expect -f
expect -c "
/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
expect \"Enter passphrase for /Users/myusername/.ssh/id_rsa: \"
send \"mypassphrase\n\";
interact
"
..并收到以下邮件错误报告:
invalid command name "/usr/bin/ssh-add"
while executing
"/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa"
Could not open a connection to your authentication agent.
我不明白这里的问题 - ssh 代理可以工作,密钥似乎可以访问,那么这个“连接”问题是什么?我尝试了其他配置,但expect
没有成功。非常感谢您的帮助。
答案1
我不建议在 cron 作业中运行ssh-add
它,expect
因为在 cron 用于执行其作业的有限环境中运行它过于复杂,并且我不想在 crontab 中存储密码。
我不会使用常规 SSH 密钥,而是创建一个新密钥,该密钥仅用于运行您希望由 cron 运行的命令。我会配置没有密码短语的密钥,并更改其所有权/权限 (0600),以便只有 cron 作业运行时的有效用户 ID(即正在处理其 crontab 的用户的 ID)才能访问它。
在您的特定情况下,特定命令是git push
将新密钥上传到您的 git 服务器。大多数 git 服务器应该能够接受多个公共 SSH 密钥。
一般来说,对于自动化流程,最好创建没有密码短语的特定密钥。然后通过在文件中的行前面添加选项对,在远程 SSH 服务器上配置一组受限制的允许命令
authorized_keys
,例如,
command="automated-task.sh",client=my.local.host,no-pty,no-agent-forwarding,no-port-forwarding ssh-rsa AAA…public…part…of…specific…keypair automated_user@server
免责声明:我对 GNU/Linux 的经验比 OS X 丰富,但据我所知,上述信息应该适用于所有 *nix 系统。
答案2
如果您正在ssh-add
将密钥添加到 ssh 代理,则可以将SSH_AUTH_SOCK
环境变量添加到 crontab,然后 ssh 命令将能够使用已加载和解锁的密钥。
- 在常规终端会话中,加载您的 ssh-agent(这留给读者练习;我认为在 macOS 上它默认运行,或者您可以运行
ssh-add -t 24h ~/path/to/your.key
)。 - 使用以下命令获取 SSH_AUTH_SOCK 位置
env | grep SSH_AUTH_SOCK
- 将其添加到你的 crontab 的顶部,例如
SSH_AUTH_SOCK=/Users/USER/.ssh/ssh_auth_sock
。
如果您尚未加载或验证密钥,crontab ssh 命令仍将失败,但对我来说没问题,因为 99% 的时间我都已加载密钥。
答案3
替代答案
我只是想仔细看看你的expect
脚本,我发现它们缺少spawn
命令:
#!/usr/bin/expect -f
spawn /usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
expect "Enter passphrase for /Users/myusername/.ssh/id_rsa:"
send "mypassphrase\n";
interact
来自expect
手册:
生成 [参数] 程序 [参数]
创建运行程序 args 的新进程。其 stdin、stdout 和 stderr 连接到 Expect,以便其他 Expect 命令可以读取和写入它们。如果 close 或进程本身关闭任何文件标识符,则连接将被中断。