从脚本启动 ssh-agent 以供多个脚本使用(由 git 调用)

从脚本启动 ssh-agent 以供多个脚本使用(由 git 调用)

我有一些主页,每个主页都在一个 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

相关内容