Linux と Mac では ssh-agent の挙動が異なる

SSHした先のサーバでも、手元の ssh の認証情報を使いたい場合、 ssh-agent を使うのが一般的です。例えば、下記のような場合に有用です。

  • パブリックIPアドレスを持たない裏側にいるサーバに、一旦別サーバを経由してから ssh する場合
  • ssh したサーバで、 ssh を使った git clone をしたい場合

使用方法

Linux の場合

❯ eval `ssh-agent -s`
❯ ssh-add ~/path/to/key

として、同じターミナルの同じセッション内のみで、 ssh -A user@host で鍵転送が行える。ssh-agent の標準出力を eval することが必要。

Mac の場合

❯ ssh-agent
❯ ssh-add ~/path/to/key

とすると、別のターミナルを開いても ssh -A user@host として鍵転送が行われて ssh することが出来る。Linux と違って eval は不要。

何が違うのか?

ssh -A したとき、環境変数の SSH_AUTH_SOCK に入っているUNIXドメインソケットを見て、鍵情報の転送が行われます。もしその環境変数に値が入っていないと転送は行われません。

ssh-agent 自体はバックグラウンドで動作するプロセスで、 SSH_AUTH_SOCK のソケット経由でデータをやり取りしています。

ssh-agent コマンドを実行すると、下記のような出力になります。

❯ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-XXXXXXXXXXX/agent.1234; export SSH_AUTH_SOCK;
SSH_AGENT_PID=1234; export SSH_AGENT_PID;
echo Agent pid 1234;

これを実行すると、 SSH_AUTH_SOCKSSH_AGENT_PID が環境変数にセットされます。この状態で ssh-addssh -A を行うと、 SSH_AUTH_SOCK がどこにあるか分かるので実行できます。

これは Mac でも Linux でも同じような標準出力になります。

ただし、Mac の場合はなぜかターミナルの別のセッションでも ssh-addssh -A が実行できています。何故なのでしょうか?

SSH_AUTH_SOCK の扱い

Mac では、 echo $SSH_AUTH_SOCK すると、/private/tmp/com.cpple.launchd.XXXXXXXX/Listeners という文字列が表示されます。

ssh-agent のソケットはここで Listen して ssh-agent のプロセスに渡すということのようです。

このため、$SSH_AUTH_SOCK がどこからでも見えて透過的に使えます。

逆に複数のセッションで ssh-agent をしても、 $SSH_AUTH_SOCK はそのうちの1つということになります。ただし、 ssh-agent のプロセス自体はコマンド実行回数分だけ起動してしてしまいます。

Linux の挙動が良いか、Mac の挙動が良いか、は一長一短という感じに見えます🙂

いずれにしても、 ssh-agent が必要な作業が終わったら killall ssh-agent でプロセスを終了しておきましょう。

❯ killall ssh-agent

Linux でも Mac のような挙動にするなら

ソケットのパスを固定すれば同じ挙動にすることも出来ます。

例えばシェルの設定ファイル等 (.zshenv 等) で、SSH_AUTH_SOCK を固定してしまいます。

export SSH_AUTH_SOCK=/tmp/ssh-agent.sock

ssh-agent 起動時にソケットのパスを渡せるので、下記のようにして起動します。

❯ ssh-agent -a $SSH_AUTH_SOCK

あとは、他のセッションでも $SSH_AUTH_SOCK が固定されているので Mac のように使えます。

ただ、 ssh-add した鍵が全部転送されたりデメリットもあるので、僕は冒頭に書いたように eval してそのセッション内のみで使うような運用にしています。好みの問題ですね🙃


初めて ssh-agent を使ったのが Mac だったので、WSL2 で開発環境構築していたら期待通りに動かずはじめ困惑しました。細かい部分で差異があり、なかなか面白いです😎