〇〇とssh-agentは使いよう

件のIBM developerWorksサイトのOpenSSH解説ウェブページの第2回第3回はほぼ全編を通してSSHを便利に使うための補助ツールであるssh-agent、そして、ssh-agentと協調してさらに便利に(???)使うために同記事著者が開発したkeychainなるフリーソフトウェアとそれらの使い方の紹介を行なっている。

SSHがサポートする各種ユーザ認証方式のなかでももっとも安全性が高いのは疑いなくユーザ公開鍵によるものなんだけど、これを使うとリモートホストにアクセスする度に自分の秘密鍵を複号するためのパスフレーズを入力しなきゃなんないというデメリットも洩れなく付いてきちゃうわけ。で、これじゃいくら安全なんだと言われようが、パスワード入力一切なしでリモートアクセスできるrsh環境に慣れ親しんでしまったユーザには受け入れられ難いだろうってことで考えられたのがssh-agentという仕掛けだったということなんだ。

ssh-agentはユーザのクライアントPCの上で動く独立したプロセスで、ユーザの秘密鍵情報をキャッシュして記憶し、必要な時にはユーザに代って秘密鍵を用いた処理を行なうことを目的としている。そのため、ssh-agentプロセスを立ち上げてそれに自分の秘密鍵を記憶させておけば、リモート計算機にアクセスする際には秘密鍵を用いた認証処理(SSH1ではチャレンジ&レスポンス、SSH2では電子署名)はssh-agentが肩代わりしてくれるため、パスフレーズ入力一切なしのrshと同じ快適な操作環境が実現できるってこと。

さて、それでは前出の記事がssh-agentに関してどんなおかしなことを書いているかを見てみようか。

ssh-agentの最も良い起動方法って?

まずは第2回の中間部分より:

ssh-agentの最も良い起動方法は、~/.bash_profileに上記の行を追加することです。それにより、ログイン・シェルで起動される全てのプログラムが環境変数を参照してssh-agentを特定し、必要に応じて鍵に関する照会ができるようになります。
ここで『上記の行』と言ってるのは
eval `ssh-agent`
こんな行のことね。たしかにこれでもログインするたびにssh-agentプロセスが自動的に立ち上がってくれるので、一見便利そうに見えるかも。でもこの方法にはログイン・シェルを終了してもssh-agentが終了しないという問題がある。そのため、知らないうちにssh-agentが山のように溜ってしまい、最悪システムリソースを喰い尽くしちまうという可能性もあるわけ。同記事ではその後ssh-agentの制限事項という部分で
まず1つは、~/.bash_profileにeval `ssh-agent`を追加した場合に、各ログイン・セッションごとにssh-agentの新しいコピーが起動されるということです。これは、いささか無駄であると同時に、ssh-agentの新しいコピーごとにssh-addによって私有鍵の追加が必要となります。これは、システム上でオープンする端魔ワたはコンソールが1つのみである場合にはあまり問題にはなりませんが、大抵の場合はかなりの数の端末をオープンするため、新しいコンソールをオープンするごとにその都度パスフレーズの入力が必要となります。実際には1つのssh-agentプロセスで十分であるため、その必要はまったくありません。
というようにいちおうこの使い方に不利益があることをは書いているけど、ssh-agentプロセスが溜っていくという不利益には一切触れていないし、そもそもこの不利益ってのは自作ソフトkeychainの優位性を強調するためのダシとして引合いに出しただけって感じだし。

じゃ、どうすりゃいいんじゃい、と言えば、ログイン・シェルから切り離してssh-agentを起動するんじゃなくて、ssh-agentの子プロセスとしてシェル(なりなんなり)を起動してやれば良いだけの話。たとえばログイン・プロセスを通常のシェルを実行する環境として用いるなら~/.bash_profileの最後で

exec ssh-agent $SHELL
とかしてやればいいし、X サーバを立ち上げるならば同じく~/.bash_profileの最後で
exec ssh-agent startx
とかしてやればいいというわけ。こうしておけばssh-agentの子プロセスとして立ち上げたコマンドが終了(異常終了でもなんてもいい)すると同時にssh-agentプロセスも綺麗に消えてくれる、という寸法。

上で単にssh-agentを起動するのではなくってexecしてるのは特にXに関してはセキュリティ対策ね。たとえば XFree86 なんかだとXサーバ画面から仮想コンソール画面に移ることができちゃったりするので、シェルから普通にXサーバを起動していたりすると実はかなり危険なので。

OpenSSHの新しい認証エージェント転送機能?

第3回の導入部には、

developerWorksに2回目の記事が掲載されて数日後、筆者のところにSarnoff CorporationのCharles Karney氏から電子メールが届きました。それは、OpenSSHの新しい認証エージェント転送機能について丁寧に述べたものでした。
なんてことが書いてあるんだが、さぁ一斉に突っ込みましょう。『そのOpenSSHの新しい認証エージェント転送機能ってのは一体なんじゃい?』

おいらの知る限り、ssh-agentの転送なんてのはssh-1.2.X、たぶんOpenSSHが元にしたバージョンである1.2.12にも存在してた機能なんだから、そんなものを指してOpenSSHの新しい認証エージェント転送機能などと呼ぶなんざぁ、『わたしはなんにも知りません』と宣言してるようなもんだってばさ。

ssh-agent転送なら信頼できないリモートホストに対して行っても安全?

同じく第3回では、信頼できるホスト/信頼できないホストが混在するネットワーク(まぁ普通にある環境でしょう)での問題として、
こういう使い方をしているときの問題は、誰かがnotrust1 または notrust2 でルート・アクセス権限を得たとすると、当然のことながら、その脆弱な状況にあるssh-agentプロセスから、その者が鍵を抜き出せる
という点を挙げ、さらにこれを防ぐために信頼できないホストには秘密鍵を置かずssh-agentも立ち上げないようにすると、そこからさらに他のホストにリモートアクセスする際パスワードなしの認証ができず不便(そもそも、この程度の不便は耐えるべきものだと思うんだが)。で、それを防ぐ策として
すべてのマシンがOpenSSHの最近のバージョンを実行しているものとすると、認証転送を利用することで、この問題を回避することができます。認証転送を利用すると、sshを発するマシン自身にssh-agentを実行させておく必要はなく、リモートのsshプロセスは信頼出来るマシン上で実行されているssh-agentとコンタクト出来るようになります。
という方法を提案している。

ま、『すべてのマシンがOpenSSHの最近のバージョンを実行しているものとすると』の部分は見なかったことにしておくとして、しかし、この方法、ほとんど何の改善にもなってまへん。信頼できないホストにはssh-agentの転送もやっちゃ駄目。なぜなら、そのホストでrootになれるならば上の『リモートのsshプロセスは信頼出来るマシン上で実行されているssh-agentとコンタクト出来る』という機能を利用して、当該ユーザがパスワードなしでログインできるよう設定しているホストにどこでもログインできてしまうから。

結局のところ何故SSH秘密鍵を護りたいかと言えば、それを使って勝手にいろんなホストにログインされたくないからというだけの話なんだから、秘密鍵そのものを盗めないからといってなんにも救われないんだよね。

また、転送したssh-agentの口を使ってローカルホストに逆ログインされる可能性もあるので、そうされてしまえば作動中のssh-agentから秘密鍵そのものを抜き取られてしまう可能性もないとは言えなかろう。

やっぱ、信頼できないホストにはssh-agentの転送もせず、そこを経由してどこかにさらにリモートログインしよう(公開鍵認証方式を使わない方法でもね。なにせ信頼できないホストなんだから、ログイン時のパスワード入力なんてのも当然モニタされてるとみなさにゃいかん)なんてことも考えないのが吉だと思うよ。ま、おとなしくリモートログインの起点は信頼できるホストだけにしときなさいってこった。

常駐ssh-agentプロセス?

で、keychainなんだが、これを一言で表せばシステム上で長期間常駐する一つのssh-agentプロセスを他のすべてのプロセスで共有することにより、(たとえばログインするたびに)毎回秘密鍵を覚えさせなきゃならんという手間を省こうじゃないか、という思想だろう。

これだと多くの場合ログインするだけですぐにssh-agentによる代理認証が可能になるんで滅茶苦茶便利。特に、いままでログイン認証のためにパスワード入力した直後にssh-agentへ秘密鍵を覚えさせるための秘密鍵パスフレーズを入力しなきゃならんので二度手間になってやだなぁと考えていた人にとっては朗報のように感じられるだろう。が、しかし。

この思想の一番の問題は、秘密鍵を握ったssh-agentプロセスがユーザによるコントロールのまったくない状態で場合によっては何日間にも渡ってシステム中に永続してしまう、という点にある。ということは故意にしろ偶然にしろ誰かがそのシステムにそのユーザとしてログインできてしまった場合、その誰かは当該ユーザが許容している範囲内の任意のリモートホストにログインできてしまうことになるわけだ。普通ならローカルホストにログインされたところで危険な目に遭うのはそのローカルホストだけで済むものが(もちろん、そのユーザがそれなりに適切なパスフレーズで自分の秘密鍵を保護していれば、の話だけど)、なまじ永続するssh-agentプロセスなんてものが存在していたおかげで、被害を受けるホストがいきなり広範囲に広がってしまうことになるわけ。下手すりゃそれでカタストロフィを招く可能性すらあると思う。

環境によってはそんな心配をせずこういう手抜きツールを安全に使える場合もあり得るだろうけど、結構難しいと思うよ。たとえば自宅から勤務先にSSHでのリモートログインができる環境で自宅なんだから大丈夫だろうと自分のサーバでssh-agentを永続させておいたら、侵入してきた空巣が偶然無茶苦茶 UNIX に詳しくって会社の重要データごっそり盗まれちゃった、てな可能性もあるわけだしね。

同じく手を抜く方法ではあっても、FreeBSD port 版 OpenSSH に含まれていた pamssh の方がなんぼか真っ当な方向だと思う。こいつはSSH秘密鍵のパスフレーズを知っているかどうかによってログイン等の認証を行なってくれるPAMモジュールで、認証に成功すると同時にssh-agentを立ち上げてその配下でシェル (なりなんなり) を動かしてくれ、さらには認証のときに入力したパスフレーズ使ってssh-agentプロセスへの秘密鍵の登録までやっちゃってくれるので、結果的に得られる効果はkeychainとほぼ一緒。しかもssh-agentプロセスはログインセッションの終了とともに消えてくれるので上に書いた危険もなし(゚д゚)ウマー。ただし、最近の FreeBSD port からは pamssh は消えちゃってるみたいなんでちょと残念。たしか FreeBSD 3.4 のリリースの頃までは入っていた筈なんだけどねー。

2002.5.31 追記:
と書いたんだけど、2chのUNIX板sshスレッドの情報によると、4.6 以降にはまた標準で pamssh が含まれるようになるんだそうだ。めでたし。

で、そもそもログインプロセスと独立してようがしていまいが、ssh-agentがある程度長期間に渡って鍵を握りっぱなしにしているというのは、それなりに危険なことなんだよねー。SSH ML春山さんに教えてもらったんだけど、今年の 1 月にはopenssh-unix-dev@mindrot.orgというML でssh-agentが鍵を保持する時間を制限できるようにしてはどうかという議論があったんだそうな。

JavaRing たぶんこの問題に対する最終的(に近い)解は、秘密鍵をPC以外のデバイスに切り離してやり、必要なときだけ接続するって方向にあるんだろう。おいらはJavaRingってデバイスに秘密鍵を入れといて、pamsshとPAM iButtonという二つのPAMモジュールを合体させたようなPAMモジュールを作ってLinuxで使ってるけど、結構快適。SSH1プロトコルにしか対応してないのと、OpenSSH2.9というちょっと古めのバージョン用になっちゃってるということで、いまんとこ非公開なんだけど、その辺りがクリアできたらここで公開しようかとは思ってるんで。


稲村 雄 =JANE=
Last modified: Fri May 31 18:17:34 JST 2002