Open SSH 5.9p1 での ssh-agent プロセス終了問題
普段使いのデスクトップ/ノートPCは共に Windows 環境なので、win-ssh-agent を使用して SSH のパスフレーズ入力を省力化。「スタートアップ」メニューに win-ssh-agent のショートカットを登録しておいて、起動時に勝手に立ち上がるようにしてある。
ところが、ある日ノートPCを起動したら、突然:
というダイアログが表示されるように。
なんじゃそりゃ?と思いつつ、ターミナルを開いて 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) の動作モードを:
- ssh-agent が fork()
- 子プロセス側で、指定コマンドを exec()
- 親プロセス側で、子プロセスの終了を waitpid()
みたいな、いわゆる一般的な「外部コマンド実行」と同じフローと考えていたのだけれど、ソースを確認したら、実は:
- ssh-agent が fork()
- 子プロセス側で、親プロセスの終了を監視
- 親プロセス側で、指定コマンドを 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 による簡便さを失わずに済んだということで良しとしよう。