我有一些主页,每个主页都在一个 git 存储库中。它们是由 Jekyll 构建的。当我更改某些内容时,我会使用 git 的pre-push
钩子让 Jekyll 构建页面并通过 rsync 将其同步到我的 HTTP 服务器。
这使得我需要输入两次 SSH 密钥的密码:一次用于执行推送,一次用于运行 rsync。
现在我尝试使用 ssh-agent 和 keychain 来解决这个问题。我尝试使用类似这样的方法
#!/bin/bash
if [ -z "$SSH_AUTH_SOCK" ]; then
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519
fi
在自己的脚本和 git 的pre-commit
钩子中。结果总是一样的:我被要求输入密钥,但代理似乎在自己的 shell 中启动了——脚本一退出,ssh 密钥就会再次被锁定,我必须再次输入密码,无论是 git push 还是 rsync 调用。
那么:是否可以以某种方式启动 ssh-agent(或钥匙串),以便它既可以用于 git push 又可以用于预推送钩子?
答案1
环境变量只能“向下”继承 – 无法eval $(ssh-agent)
让它们在运行此特定脚本的 /bin/bash 进程之外持续存在。因此,如果 SSH_AUTH_SOCK 变量不是通过第一个命令从 shell 继承的,则第二个命令也无法使用该变量。
因此,在本地 GUI 登录期间,ssh-agent 通常会很早就启动,以便整个 GUI 继承环境。通过 SSH 工作时,您必须直接eval...
在交互式 shell 中使用,以便 SSH_AUTH_SOCK 可用于您从中运行的所有命令。
$ env | grep ^SSH_AU
$ ssh-add
Could not open a connection to your authentication agent.
$ eval $(ssh-agent)
$ env | grep ^SSH_AU
SSH_AUTH_SOCK=/tmp/ssh-XXXXXXRM6v3r/agent.3047987
$ ssh-add
Enter passphrase for:
这样做会导致您注销时无用的 ssh-agent 进程累积,因此实现相同目的的更好方法是使用ssh-agent bash
或运行嵌套 shell ssh-agent tmux
– 然后,只要它运行的命令退出,ssh-agent 就会退出。
$ echo "Shell PID=$$, socket=$SSH_AUTH_SOCK"
Shell PID=3047611, socket=
$ ssh-agent bash
$ echo "Shell PID=$$, socket=$SSH_AUTH_SOCK"
Shell PID=3047922, socket=/tmp/ssh-XXXXXXRM6v3r/agent.3047987
$
另一种方法是用包装脚本替换 eval 中的“ssh-agent”,该脚本将套接字路径存储在某处(例如在 ~/.ssh/agent 中),并且在第二次运行时直接返回存储的路径,而不是一次又一次地启动 ssh-agent。
#!/bin/bash
agent_ok() { ssh-add -l > /dev/null 2>&1 || [ $? -eq 1 ]; }
if ! agent_ok; then
if [ -e ~/.ssh/agent.info ]; then
eval $(< ~/.ssh/agent.info)
fi
fi
if ! agent_ok; then
eval $(ssh-agent | tee ~/.ssh/agent.info)
fi
cat ~/.ssh/agent.info
还可以要求 ssh-agent 监听特定路径而不是随机生成的路径,以便例如可以通过 systemd 用户服务启动它:
if [ -S $XDG_RUNTIME_DIR/agent.sock ]; then
export SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/agent.sock
else
eval $(ssh-agent -a $XDG_RUNTIME_DIR/agent.sock)
fi