diff options
| author | 2026-05-22 10:41:24 +0100 | |
|---|---|---|
| committer | 2026-05-22 10:41:24 +0100 | |
| commit | 982d180f9b9a2f8a90d454816474dce8d4b4b8e2 (patch) | |
| tree | f21a97bbb74eca554775b37fb9faed23bbbfd315 | |
| parent | 6b7e057bd4fc2cbdffe5fc8b3095810db1ccf9e3 (diff) | |
| download | dotfiles-982d180f9b9a2f8a90d454816474dce8d4b4b8e2.tar.gz dotfiles-982d180f9b9a2f8a90d454816474dce8d4b4b8e2.tar.bz2 dotfiles-982d180f9b9a2f8a90d454816474dce8d4b4b8e2.zip | |
fix(ssh): make agent.sock symlink concurrent-connection-safe
Previously every new login retargeted ~/.ssh/agent.sock to its own
per-connection forwarded socket. That broke a multi-connection setup
when the most-recent connection (which 'won' the symlink) dropped:
all surviving connections' panes would point at a dead socket until a
fresh login from a surviving connection re-ran zprofile.
zprofile: only retarget when the existing symlink target is dead
(sshd unlinks the per-connection socket on disconnect, so [[ -S ]] on
the resolved path is a reliable liveness probe). First connection
seeds the symlink, subsequent logins keep using it.
ssh-agent-refresh: scan /tmp/ssh-*/agent.* for any live forwarded
socket and retarget to the first that responds to ssh-add. Lets the
surviving connection recover without waiting for a new login shell.
| -rw-r--r-- | dot_config/zsh/dot_zprofile | 11 | ||||
| -rw-r--r-- | dot_config/zsh/dot_zshrc | 34 |
2 files changed, 32 insertions, 13 deletions
diff --git a/dot_config/zsh/dot_zprofile b/dot_config/zsh/dot_zprofile index b0f7089..7b79af0 100644 --- a/dot_config/zsh/dot_zprofile +++ b/dot_config/zsh/dot_zprofile @@ -64,11 +64,18 @@ unset SSH_AGENT_PID # git-fetch keep working without any per-pane re-export. if [[ -n "$SSH_CONNECTION" && -S "$SSH_AUTH_SOCK" ]]; then stable_sock="$HOME/.ssh/agent.sock" - if [[ "$SSH_AUTH_SOCK" != "$stable_sock" ]]; then + # Only retarget if the current symlink target is dead. Sshd unlinks + # the per-connection socket file on disconnect, so [[ -S ]] on the + # resolved path is a reliable liveness probe. Avoiding gratuitous + # retargets keeps multi-connection setups stable: the first + # connection seeds the symlink, subsequent logins keep using it, + # and only if that connection drops does the next login retarget. + current_target="$(readlink "$stable_sock" 2>/dev/null)" + if [[ ! -S "$current_target" ]]; then ln -sfn "$SSH_AUTH_SOCK" "$stable_sock" fi export SSH_AUTH_SOCK="$stable_sock" - unset stable_sock + unset stable_sock current_target else # Local login: route ssh auth through gpg-agent. SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)" diff --git a/dot_config/zsh/dot_zshrc b/dot_config/zsh/dot_zshrc index b1021eb..8b2bd4f 100644 --- a/dot_config/zsh/dot_zshrc +++ b/dot_config/zsh/dot_zshrc @@ -413,18 +413,30 @@ reload-env() { # (claude-code, etc.) must still be restarted: env is inherited, not # observed. ssh-agent-refresh() { - local sock="$HOME/.ssh/agent.sock" - if [[ ! -e $sock ]]; then - echo "ssh-agent-refresh: $sock missing; reconnect over ssh first to seed it" >&2 - return 1 - fi - export SSH_AUTH_SOCK="$sock" - if ssh-add -l >/dev/null 2>&1; then - print -r -- "ssh-agent: live → $(readlink -f -- "$sock")" - else - echo "ssh-agent-refresh: stable socket exists but ssh-add -l failed; agent forwarding off?" >&2 - return 1 + local stable="$HOME/.ssh/agent.sock" + local current sock + current="$(readlink "$stable" 2>/dev/null)" + # Healthy path: existing target still responsive. + if [[ -S "$current" ]] && SSH_AUTH_SOCK="$current" ssh-add -l >/dev/null 2>&1; then + export SSH_AUTH_SOCK="$stable" + print -r -- "ssh-agent: live → $current" + return 0 fi + # Symlink dead — scan all forwarded sockets from any concurrent ssh + # session and retarget to the first one that responds to ssh-add. + # Handles the case where the connection that originally seeded the + # symlink has dropped but another session is still alive. + for sock in /tmp/ssh-*/agent.*(N); do + [[ -S $sock ]] || continue + if SSH_AUTH_SOCK="$sock" ssh-add -l >/dev/null 2>&1; then + ln -sfn "$sock" "$stable" + export SSH_AUTH_SOCK="$stable" + print -r -- "ssh-agent: re-pointed → $sock" + return 0 + fi + done + print -r -- "ssh-agent-refresh: no live forwarded agent found; reconnect over ssh with -A first" >&2 + return 1 } # Just |
