彷徨えるフジワラ

年がら年中さまよってます

Open SSH 5.9p1 での ssh-agent プロセス終了問題

普段使いのデスクトップ/ノートPCは共に Windows 環境なので、win-ssh-agent を使用して SSHパスフレーズ入力を省力化。「スタートアップ」メニューに win-ssh-agent のショートカットを登録しておいて、起動時に勝手に立ち上がるようにしてある。

ところが、ある日ノートPCを起動したら、突然:

俺は ssh-add なんだけど、ssh-agent と繋がらねーよ?(意訳)

というダイアログが表示されるように。

なんじゃそりゃ?と思いつつ、ターミナルを開いて ssh-add してみると、確かに ssh-agent への接続に失敗する。

タスクマネージャとか cygwin の ps 出力とかで確認しても、やっぱり ssh-agent は立ち上がっていない模様。

試しにターミナルから ssh-agent を起動してみると、今度はちゃんと起動される。もっとも、起動されたとは言え、ターミナル経由での起動だと、SSH_AUTH_SOCK 相当がレジストリに記録されないので、別ターミナルから SSH を使用する場合は、手動で環境変数を設定する必要がある。

それは流石に勘弁して欲しいので、ターミナル経由で起動した ssh-agent を停止させた上で、手動で win-ssh-agent を起動。

.... あれ?また ssh-agent と繋がらないと言ってくるぞ?

で、今度もやっぱり ssh-agent プロセスが居なくなっている。

さぁ、ここでスイッチが入りました!(笑)
とりあえず、状況を整理するために、ssh-agent の3つの動作モードの確認を。

(1) コマンドラインから普通に ssh-agent を起動した場合は、単独でバックグラウンド動作するモードになる。

$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-AKLHvwXY1716/agent.1716;
export SSH_AUTH_SOCK;
SSH_AGENT_PID=4400;
export SSH_AGENT_PID;
echo Agent pid 4400;
$

但し、以後の SSH 関連操作において、起動された ssh-agent と連携したい場合は、出力される環境変数が設定されている必要があるので、ssh-agent の出力結果を eval してやるのが一般的な使い方。

$ eval `ssh-agent`
Agent pid 4400
$ env | grep SSH
SSH_AUTH_SOCK=/tmp/ssh-AKLHvwXY1716/agent.1716
SSH_AGENT_PID=4400
$

このモードでの動作では、明示的にプロセス終了させない限り ssh-agent は動作し続ける。

(2) ssh-agent 起動の際に引数を指定すると、それをコマンドとみなして実行し、当該コマンドの実行が終了すると同時に ssh-agent も終了する。

$ ssh-agent startx X Window の起動

起動されるコマンドの環境変数には、(1) のモードで出力されていた値が設定されているので、起動されたプロセスからは即 ssh-agent 連携が可能になる。

UNIX 環境の場合、上記のように ssh-agent 経由で X Window の起動を行うと、(設定次第ではあるけれど) ウィンドウマネージャの終了と同時に ssh-agent も終了するので、パスフレーズ情報を抱えたまま ssh-agent が稼動し続ける、などというセキュリティ的にアレな事態を回避することが可能。

(3) ssh-agent を -d フラグ付きで起動すると、バックグラウンドプロセス化しないデバッグモードとして稼動する。

まぁ、(3) のモードはおまけみたいなものなので、通常は (1)/(2) の2つのモードだけ把握しておけば問題ない。

ちなみに、僕は当初、(2) の動作モードを:

  1. ssh-agent が fork()
  2. 子プロセス側で、指定コマンドを exec()
  3. 親プロセス側で、子プロセスの終了を waitpid()

みたいな、いわゆる一般的な「外部コマンド実行」と同じフローと考えていたのだけれど、ソースを確認したら、実は:

  1. ssh-agent が fork()
  2. 子プロセス側で、親プロセスの終了を監視
  3. 親プロセス側で、指定コマンドを exec()

というフローだった。

言われてみれば、バックグラウンド動作 (= 子プロセス側で稼動) する (1) モードとの相似性とか、KILL シグナルが飛んだ場合のプロセス刈り取りとか色々考えた場合、後者のフローの方が適切であるのは自明なのだが、完全に盲点だった。人生、日々勉強だなぁ。

で、以上の3つの動作モードのうち、どうやら (1)/(3) は動作するのだけれど、(2) のモードで動作させると、起動プロセスの終了を待たずに ssh-agent が終了してしまうことが判明。

つまり:

$ ssh-agent sleep 120

とかやっている背後で、ps やタスクマネージャでプロセス監視していると、ものの 10 秒もしないうちに、ssh-agent が終了してしまうのだ。

経験的に、Cygwin のプロセス管理/プロセス間通信はイマイチな事が多かったので、「プロセス終了通知が上手いこと機能していないのでは?」という疑惑が。

........そういえば、つい先日 cygwin の update をしたばかりだったなぁ (^ ^;;;)

試しに、waitpid() で子プロセスの終了待ちをするプログラムをでっち上げて、動作確認してみると .... フツーに動くなぁ。

まぁ、問題の ssh-agent の動作は、子プロセス側から親プロセス終了を見張る形態なので、必ずしも同一ではないのだけれど、本当にプロセス連携で失敗しているなら、相当致命的な問題じゃねぇ? > Cygwin

という結論で憂鬱な気分になっていたのだが、ふと思い立って win-ssh-agent の最新版を探してみると .... あれ?

openssh 5.9p1-1 のバグを回避するために、内部構造を変更

などという修正が入った最新版が出てる!

# ついでに、公開場所が SkyDrive から GitHub に変わっている!

手元の環境の openssh バージョンを確認してみると....:

$ ssh -V
OpenSSH_5.9p1, OpenSSL 0.9.8r 8 Feb 2011
$

ビンゴー!

早速最新版の win-ssh-agent をビルドして動かしてみると....動いた!ちゃんと ssh-agent プロセスが残っている!

やれやれ、これでやっと問題解決だ。と思って、何の気なしに「スタートアップ」メニューに登録したショートカットから win-ssh-agent を起動すると .... ぎゃー!Norton が強制的にプロセス停止&ファイル削除しやがった!

Norton の Anti Virus 機能による強制停止&削除は、自作の exe 系で良く踏む地雷なので、cygwin 環境の /usr/local/bin 配下をまとめて「SONAR スキャンの対象から除外」設定をしているのだが、それにも関わらず win-ssh-agent は強制停止&削除の餌食に .... orz

とりあえず、個別の exe ファイルに対して、explorer の右クリック(コンテキストメニュー)から「Norton Internet Security」⇒「Norton ファイルインサイト」で、当該ファイルを「信頼する」に設定することで、何とか実行可能な状態に持ち込む。

変更履歴には、前述のように「内部構造を変更」なる記載がある上に、Norton がプロセス停止&ファイル削除に当たって言うには「新しくスレッドを起こそうと云々」とのことなので:

単に exec(2) するようなプロセスは、簡単な検疫除外設定で見逃しても良いけど、スレッドを起こしたり、fork(2) したりするようなプロセスは、本気で検疫対象から除外してない限りは、見逃すわけには行かない

といったような判断が働いたのではなかろうかと推測 > Norton

まぁ、ビルド領域での単独実行は可能だけど、「スタートアップ」メニューからの起動が駄目なのは、「スタートアップ」経由の侵入は比較的簡単な手口の一つだと思われるから、Norton が不正侵入に対して頑張って目を光らせている証でもあるので、一概に文句を言う訳にも行かないんだよなぁ。

とりあえず ssh-agent による簡便さを失わずに済んだということで良しとしよう。