85

当我重新连接断开的 tmux 会话时,我试图找到一种恢复 SSH 代理的好方法。

原因似乎是 SSH 代理会话更改但 tmux 会话中的环境变量未更新。

在附加会话本身之前,如何实现自动化?因为我要附加的会话并不总是有 bash 提示,所以我不能在里面输入一些东西。它必须在创建或附加 tmux 会话之前运行。

我正在运行的代码示例位于https://gist.github.com/ssbarnea/8646491——一个使用 tmux 创建持久 ssh 连接的小型 ssh 包装器。这工作得很好,但有时 ssh 代理停止工作,所以我不再能够使用它来连接到其他主机。

4

12 回答 12

85

Martijn Vermaat有一个很好的要点,它深入解决了您的问题,尽管它是为屏幕用户设计的,所以我在这里为 tmux 调整它。

总结一下:

  1. ~/.ssh/rc如果不存在则创建,并添加以下内容:

    #!/bin/bash
    
    # Fix SSH auth socket location so agent forwarding works with tmux
    if test "$SSH_AUTH_SOCK" ; then
      ln -sf $SSH_AUTH_SOCK ~/.ssh/ssh_auth_sock
    fi
    
  2. 让它在 tmux 中工作,将其添加到您的~/.tmux.conf

    # fix ssh agent when tmux is detached
    setenv -g SSH_AUTH_SOCK $HOME/.ssh/ssh_auth_sock
    

如果要启用 X11 转发,则需要额外的工作,请参阅要点

于 2014-04-20T20:07:23.790 回答
43

虽然默认tmux 更新 SSH变量,但无需

  • 更改/添加套接字路径
  • 改变SSH_AUTH_SOCKET变量

我喜欢Chris Down的解决方案,我将其更改为添加功能

fixssh() {
    eval $(tmux show-env    \
        |sed -n 's/^\(SSH_[^=]*\)=\(.*\)/export \1="\2"/p')
}

进入~/.bashrc. fixssh在附加会话之后或ssh/ scp/之前调用rsync

较新版本的tmux支持-s选项show-env,所以只有

eval $(tmux show-env -s |grep '^SSH_')

是可能的。

于 2016-01-08T18:21:16.143 回答
25

这是我用于SSH_AUTH_SOCKtmux窗口内更新的内容(基于 Hans Ginzel 的脚本):

alias fixssh='eval $(tmux showenv -s SSH_AUTH_SOCK)'

或者因为tmux没有showenv -s

alias fixssh='export $(tmux showenv SSH_AUTH_SOCK)'
于 2016-12-05T05:38:03.193 回答
6

这是我的解决方案,其中包括两种方法,当我重新连接到 tmux 会话时不需要额外输入

alias ssh='[ -n "$TMUX" ] && eval $(tmux showenv -s SSH_AUTH_SOCK); /usr/bin/ssh'
于 2017-06-09T20:18:50.997 回答
6

这里有很多很好的答案。但也有tmux show-environment看不到的情况SSH_AUTH_SOCK。在这种情况下,您可以使用find显式定位它。

export SSH_AUTH_SOCK=$(find /tmp -path '*/ssh-*' -name 'agent*' -uid $(id -u) 2>/dev/null | tail -n1)

这很长而且很复杂,所以我会分解它......

01  export SSH_AUTH_SOCK=$(
02    find /tmp \
03      -path '*/ssh-*'
04      -name 'agent*'
05      -uid $(id -u)
06      2>/dev/null
07    | tail -n1
08  )
  1. exportSSH_AUTH_SOCK环境变量设置为$()命令替换的输出
  2. find文件开始于/tmp
  3. 将结果限制/ssh-在路径中的那些
  4. 将结果限制为仅以名称开头的人agent
  5. 将结果限制为仅具有与当前用户匹配的用户 ID 的结果
  6. 静默所有(权限等)错误
  7. 如果有多个,只取最后一个结果

如果您知道只有 1 个结果并且您不关心 stderr 垃圾,您可能可以省略 6 和 7。

于 2018-06-26T20:28:58.630 回答
4

我使用先前答案的变体:

eval "export $(tmux show-environment -g SSH_AUTH_SOCK)"

假设您从外部环境启动了 ssh 代理。其他环境变量也是如此,例如DISPLAY.

于 2018-06-19T12:48:19.190 回答
2

我更喜欢避免配置 TMUX(等)并将所有内容都保留在~/.ssh/. 在远程系统上:

创建~/.ssh/rc

#!/bin/bash

# Fix SSH auth socket location so agent forwarding works within tmux
if test "$SSH_AUTH_SOCK" ; then
  ln -sf $SSH_AUTH_SOCK ~/.ssh/ssh_auth_sock
fi

添加以下内容,~/.ssh/config使其不再依赖$SSH_AUTH_SOCK,它在分离的终端中变得陈旧:

Host *
  IdentityAgent ~/.ssh/ssh_auth_sock

已知限制

  • ssh-add不使用~/.ssh/config,因此无法与ssh-agent. 像更新通过 SSH 访问的 git 遥控器一样ssh-add -l,即使工作正常,命令也会产生错误。ssh user@host
于 2019-01-01T18:03:32.763 回答
1

以防其他鱼壳用户在使用鱼时想知道如何处理这个问题(以及我未来的自己!)。在我的fish_prompt我添加了对以下函数的调用:

function _update_tmux_ssh
  if set -q TMUX
    eval (tmux show-environment SSH_AUTH_SOCK | sed 's/\=/ /' | sed 's/^/set /')
  end
end

我想更高级的 *nix 用户会知道如何sed用更好的东西替换,但这有效(tmux 3.0,fish 3.1)。

于 2021-04-29T08:34:23.927 回答
1

我可能已经制定了一个完全封装在~/.tmux.conf配置文件中的解决方案。这是与修改~/.bash_profileand不同的方法~/.ssh/rc

仅使用解决方案~/.tmux.conf

只需将以下代码剪切并粘贴到您的~/.tmux.conf

# ~/.tmux.conf

# SSH agent forwarding
#
# Ensure that SSH-Agent forwarding will work when re-attaching to the tmux
#   session from a different SSH connection (after a dropped connection).
#   This code will run upon tmux create, tmux attach, or config reload.
#
# If there is an SSH_AUTH_SOCK originally defined:
#   1) Remove all SSH related env var names from update-environment.
#      Without this, setenv cannot override variables such as SSH_AUTH_SOCK.
#      Verify update-environment with: tmux show-option -g update-environment
#   2) Force-set SSH_AUTH_SOCK to be a known location
#      /tmp/ssh_auth_sock_tmux
#   3) Force-create a link of the first found ssh-agent socket at the known location
if-shell '[ -n $SSH_AUTH_SOCK ]' " \
  set-option -sg update-environment \"DISPLAY WINDOWID XAUTHORITY\"; \
  setenv -g SSH_AUTH_SOCK /tmp/ssh_auth_sock_tmux; \
  run-shell \"ln -sf $(find /tmp/ssh-* -type s -readable | head -n 1) /tmp/ssh_auth_sock_tmux\" \
"

警告

当启动到同一台机器的多个连接时,上述解决方案以及其他解决方案容易受到竞争条件的影响。考虑一下:

  • 客户端 1 连接:SSH 到 machineX,启动/附加 tmux(写入ssh_auth_sock链接)
  • 客户端 2 连接:SSH 到 machineX,启动/附加 tmux(覆盖ssh_auth_sock链接)
  • 客户端 2 断开连接:客户端 1 留下一个陈旧的ssh_auth_sock链接,从而中断ssh-agent

但是,此解决方案的弹性稍强一些,因为它仅ssh_auth_sock在 tmux 启动/附加时覆盖链接,而不是在初始化 bash shell~/.bash_profile或 ssh 连接时~/.ssh/rc

为了覆盖最后一个竞争条件,可以添加一个键绑定以使用(Ctrl-b r)键序列重新加载 tmux 配置。

# ~/.tmux.conf

# reload config file
bind r source-file ~/.tmux.conf

在活动的 tmux 会话中,当ssh_auth_sock链接失效时执行此序列将刷新 ssh-agent 连接。

于 2020-04-30T16:15:22.220 回答
0

这是另一个简单的 Bash 解决方案,用于在生成每个提示之前PROMPT_COMMAND更新SSH_*内部的变量。tmux此解决方案的缺点是它在生成新提示之前不会在现有 shell 中生效,因为PROMPT_COMMAND仅在创建新提示之前运行。

只需将其添加到您的~/.bashrc

update_tmux_env () {
    # Only run for shells inside a tmux session.
    if [[ -n "$TMUX" ]]; then
        eval $(tmux show-env -s | grep '^SSH_')
    fi
}
export PROMPT_COMMAND=update_tmux_env
于 2021-01-21T09:22:24.977 回答
0

在遇到了这么多建议后,我终于想出了一个解决方案,可以让 TMUX 在附加后更新陈旧的 ssh 代理。基本上,本地和远程机器上的 zshrc 文件都需要修改。

将以下代码插入到本地zshrc中,基于此参考

export SSH_AUTH_SOCK=~/.ssh/ssh-agent.$(hostname).sock
ssh-add -l 2>/dev/null >/dev/null
# The error of executing ssh-add command denotes a valid agent does not
# exist. 
if [ $? -ge 1 ]; then
  # remove the socket if it exists
  if [ -S "${SSH_AUTH_SOCK}" ]; then
    rm "${SSH_AUTH_SOCK}"
  fi
  ssh-agent -a "${SSH_AUTH_SOCK}" >/dev/null
  # one week life time
  ssh-add -t 1W path-to-private-rsa-file
fi

将以下代码插入到远程 zshrc 中,tmux 会话将在其中附加。

alias fixssh='eval $(tmux showenv -s SSH_AUTH_SOCK)'

然后 ssh 进入远程机器。-A 选项是必需的。

ssh -A username@hostname

附加 TMUX 会话。检查 TMUX 环境变量

# run this command in the shell
tmux showenv -s
# or run this command after prefix CTRL+A or CTRL+B
:show-environment

在先前存在的窗格中运行fixssh以更新 ssh 代理。如果创建了新窗格,它将自动获取新的 ssh-agent。

于 2020-10-19T15:11:18.593 回答
0

跟进@pymkin 上面的回答,添加以下内容,在 macOS 11.5.3 上与 tmux 3.2a 一起使用:

  1. ~/.tmux.conf
# first, unset update-environment[SSH_AUTH_SOCK] (idx 3), to prevent
# the client overriding the global value
set-option -g -u update-environment[3]
# And set the global value to our static symlink'd path:
set-environment -g SSH_AUTH_SOCK $HOME/.ssh/ssh_auth_sock
  1. ~/.ssh/rc
#!/bin/sh
# On SSH connection, create stable auth socket path for Tmux usage
if test "$SSH_AUTH_SOCK"; then
    ln -sf "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
fi

这是怎么回事?Tmux 具有半有用的update-environment变量/功能,可以在客户端连接时获取某些环境变量。即,当您执行tmux newor时tmux attach,它将从您运行这些命令时更新 tmux 环境。这对于你之后在 tmux 中运行的新 shell 或命令来说很好,但它对你在最新附加之前启动的那些 shell 没有帮助。为了解决这个问题,您可以在这里使用其他一些答案让现有的 shell 选择这个更新的环境,但这不是我选择的路线。

相反,我们在 tmux 内部设置了一个静态值,即. tmux 中的所有 shell 都会选择它,并且以后无需更新。然后,我们配置 ssh,以便在连接时,它使用符号链接更新静态路径,指向 ssh 知道的最新真实套接字。SSH_AUTH_SOCK~/.ssh/ssh_auth_sock

@pymkin 的答案中缺少的部分是 Tmux 将使会话值覆盖全局值,因此这样做set-environment -g是不够的;每当您重新连接时,它都会被压扁。您还必须告诉 tmux 不要SSH_AUTH_SOCK在会话环境中更新,以便全局值可以通过。这就是它的意义set-option -g -u所在。

于 2021-12-14T01:02:15.003 回答