共通テーマ
OpenSSH キー (鍵) の管理: 第 3 回
エージェント転送と keychain の改善点
コンテンツシリーズ
このコンテンツは全#シリーズのパート#です: 共通テーマ
このコンテンツはシリーズの一部分です:共通テーマ
このシリーズの続きに乞うご期待。
多くの開発者が、旧来のtelnet
コマンドやrsh
コマンドに代わるセキュアで暗号化された優れた手段としてOpenSSHを使用しています。OpenSSHのさらに魅力的な機能の1つは、RSA/DSA認証プロトコルを用いたユーザー認証が可能なことです。この機能は、1対の相補的な数値キーに基づいています。RSA/DSA認証のもっとも大きな魅力の1つは、リモート・システムとの接続をパスワード入力なしで確立できるという点です。予備知識としては、OpenSSHキーの管理について扱う本連載のこれまでの記事、RSA/DSA認証を理解する (第1回)、ssh-agentとkeychainの概要 (第2回) をご覧ください。
第2回目が2001年9月にdeveloperWorksに掲載され、その後SlashdotやFreshmeatで取り上げられたため (これらのサイトは、本稿末尾の参考文献からリンクできます)、数多くの人々がkeychain
を使い始め、数々の変更がkeychain
に加えられました。筆者のところには、世界中の開発者から20件程度の質の高いパッチが送られてきました。筆者は、それらのパッチの多くをkeychain
のソースに採り入れ、今ではバージョン1.8にまでなっています (参考文献参照)。これらのパッチやバグ・レポート、機能についてのリクエスト、感謝の言葉を送っていただいたすべての方々に、心から謝意を表したいと思います。
sshセキュリティーの強化
前回の記事では、ssh-agent
を実行することのセキュリティー上の利点とトレードオフについて、少し時間をかけて説明しました。developerWorksに2回目の記事が掲載されて数日後、筆者のところにSarnoff CorporationのCharles Karney氏から電子メールが届きました。それは、OpenSSHの新しい認証エージェント転送機能について丁寧に述べたものでした。以下では、このことについて少し取り上げたいと思います。Charles氏は、信頼できないマシン上でssh-agent
を実行すると非常に危険であるとも強く指摘しています。もし誰かが、何とかしてそのシステム上でルート・アクセス権限を得たとすると、復号鍵 (decrypted keys) がssh-agent
から抜き出される可能性がある、と。鍵の抜き出しが幾分難しい話だとしても、それは、プロのクラッカーには不可能ではありません。私有鍵の盗難が起こりうるということは、そうしたことがすぐにでも起こるものとして、その防止策を講じるべきであるということを意味します。
私有鍵を守るための方法を編み出すには、まず、われわれがアクセスするマシンを2つのカテゴリーに分類する必要があります。あるホストが、安全対策のよくとられたものである場合、あるいは隔離されたものである場合、すなわち、ルート・アクセス権の悪用がまずありえないようなホストの場合、そのマシンは、信頼できるホストだとみなしてよいでしょう。一方、他の多くの人々が使用したり、システムのセキュリティーに何らかの疑わしさのあるマシンは、信頼できないホストだとみなすべきでしょう。私有鍵が抜き取られることがないように防護するには、ssh-agent
が、(したがってkeychain
も)、信頼できないホストで絶対に実行されないようにしなければなりません。そうすれば、システムのセキュリティーが危険にさらされたとしても、侵入者が手始めに鍵を抜き取るために使うssh-agent
が、見当たらないことになります。
ところが、これによって、また別の問題が生じます。信頼できないホストでssh-agent
を実行できないということになると、では、そうしたシステムからどうやってパスワードなしで安全にssh
接続を確立すればよいのでしょうか。答は、信頼できるホストでしかssh-agent
とkeychain
を使用しないようにし、OpenSSHの新しい認証転送機能を使って、パスワードなしの認証を信頼できないホストにも広げていけばよい、ということです。要するに、リモートのssh
セッションを許し、信頼できるシステム上で実行されているssh-agent
と連絡できるようにすることで、認証転送が有効に機能するというわけです。
認証エージェント転送
認証転送がどのようにして機能するのかを理解していただくために、まず、drobbins
というユーザーが、lappy
という信頼できるラップトップ1台とtrustbox
という信頼できるサーバー1台を使用し、信頼できない他の2つのシステムnotrust1
、notrust2
にアクセスする必要があるという、仮設の状況について考えてみることにしましょう。現在、このユーザーは、下図のように、これら4台のすべてのマシンでssh-agent
とkeychain
を使用しています。
図1. 信頼できるマシンと信頼できないマシンでssh-agentが稼動

こういう使い方をしているときの問題は、誰かがnotrust1
またはnotrust2
でルート・アクセス権限を得たとすると、当然のことながら、その脆弱な状況にあるssh-agent
プロセスから、その者が鍵を抜き出せる、という点です。これを解決するために、drobbins
は、信頼できないホストnotrust1
およびnotrust2
でssh-agent
とkeychain
の実行を中止します。あるいは、もっと用心するとしたら、drobbins
は、ssh-agent
とkeychain
をlappy
上でのみ使用するようにします。これによって、復号私有鍵が他人の目に触れることが制限され、私有鍵の盗難を防ぐことができます (下図)。
図2. lappyのみでssh-agentを稼動。より安全性の高い構成

もちろん、この方法における問題は、drobbins
がlappy
からしかパスワードなしの接続を確立できなくなった点にあります。そこで、認証転送を有効にして、この問題を回避する方法について考えてみましょう。
すべてのマシンがOpenSSHの最近のバージョンを実行しているものとすると、認証転送を利用することで、この問題を回避することができます。認証転送を利用すると、ssh
を発するマシン自身にssh-agentを実行させておく必要はなく、リモートのssh
プロセスは信頼出来るマシン上で実行されているssh-agentとコンタクト出来るようになります。こうすれば、通常は、1台のマシンのみでssh-agent
(およびkeychain
) を実行させることになり、このマシンを基点として (直接または間接に) つながっているすべてのssh
接続が、みなさんのローカルなssh-agent
を使用するようになる、ということを意味します。
認証転送を行えるようにするには、lappy
とtrustbox
の/etc/ssh/ssh_configに以下の1行を追加します。これは、ssh
のconfigファイル (ssh_config) であり、sshデーモンsshd
のconfigファイル (sshd_config) ではありませんので、注意してください。
リスト1. /etc/ssh/ssh_configに以下の行を追加する
ForwardAgent Yes
さて、drobbins
は、認証転送を利用することで、
lappy
から
trustbox
に接続し、さらに
trustbox
から
notrust1
に接続することができます。この際、どの接続においてもパスフレーズを入力する必要はありません。以下のリストに示されるように、どちらのssh
プロセスも、lappy
上で実行されているssh-agent
に「打診 (tap in)」します。
リスト2. lappyに打診
$ssh drobbins@trustbox Last login: Wed Sep 26 13:42:08 2001 from lappy Welcome to trustbox! $ssh drobbins@notrust1 Last login: Tue Sep 25 12:03:40 2001 from trustbox Welcome to notrust1! $
みなさんのところで同じような構成にしてみて、エージェント転送がうまく機能しないというときには、従来どおりのssh
ではなく、ssh -A
として、明示的に認証転送が有効になるようにしてみてください。われわれが、上のように、認証転送を利用してtrustbox
やnotrust1
にログ・インしたときに、裏側で何が行われていたのかを示すのが、以下の図です。
図3. エージェント転送の仕組み

図からわかるように、ssh
は、trustbox
に接続するとき、lappy
で実行されているssh-agent
との接続を維持しています。trustbox
からnotrust1
へのssh
接続が行われる場合、新しいssh
プロセスは、それまでのssh
への認証接続を維持します。その結果、チェーンが延びるわけです。この認証チェーンをnotrust1
から先の他のホストにまで延長できるかどうかは、notrust1
の /etc/ssh/ssh_configがどのように設定されているかによります。エージェント転送が有効にされているかぎり、チェーンの中のすべてのマシンが、信頼できるlappy
上で実行されているssh-agent
を利用することで、認証を行うことができることになります。
エージェントによる接続転送の利点
認証転送には、セキュリティーに関して、本稿では触れないものでも数々の利点があります。エージェント接続転送の重要性を納得させるために、Charles Karney氏が筆者に説いているセキュリティー上の利点は、以下の3つです。
- 私有鍵が、信頼できるマシンにのみ保存される。したがって、悪意のあるユーザーが暗号化された鍵をディスクから盗み出し、暗号を解読するのを防止できる。
ssh-agent
が、信頼できるマシンでのみ実行される。したがって、侵入者がリモートのssh-agent
プロセスのメモリー・ダンプをとり、ダンプから解読私有鍵を抜き出すのを防止できる。- 信頼できるマシンにパスフレーズをキーインしておくだけでよいので、何らかのキーストローク・ロガーが入力時にパスフレーズをこっそり盗み出すのを防止できる。
認証エージェント接続転送に頼ることの欠点は、cronジョブがRSA/DSA認証を利用出来るようになっている問題を解決していないという点です。この問題に対する1つの答は、RSA/DSA認証を必要とするすべてのcronジョブを、LAN上の信頼できるマシンから実行されるように設定することです。必要なら、これらのcronジョブが、ssh
を使ってリモートのシステムに接続し、バックアップを自動化したり、ファイルの同期をとったりといった作業を行うようにすることもできます。
以上、認証エージェント接続転送について見てきたわけですが、次にkeychain
スクリプトそのものに加えられた最近の改善点に目を向けてみようと思います。
Keychain機能の改善点
ユーザーから寄せられたパッチ情報のおかげで、keychain
のソースに数多くの有意義な改善がなされてきました。ユーザーから寄せられたkeychain
のパッチのいくつかは、機能に関するものでした。たとえば、keychain
が~/.ssh-agentというファイルを作成していたことが思い出されます。このファイルの名前は、今は、~/.ssh-agent-[hostname] に変わりました。keychain
が、物理的に別個な複数のホストからアクセスされる可能性のある、NFS設定のホーム・ディレクトリーにも対応できるようにするためです。~/.ssh-agent-[hostname] ファイルの他に、csh
互換シェルが供給することのできる ~/.ssh-agent-csh-[hostname] というファイルも使用されるようになりました。さらには、vt100互換でない端末を使用している場合にカラー表示機能を無効にするための--nocolor
というオプションも新たに追加されました。
シェルの互換性に関する修正
機能的な改善も重要な意味をもつものでしたが、修正の大半は、シェル互換性の問題に関するものでした。keychain 1.0では、bash
が必要でしたが、それ以降のバージョンは、sh
互換のシェルであれば対応できるように変更されています。この変更によって、keychain
は、Linux、BSD、Solaris、IRIX、AIX、あるいはその他のUNIXプラットフォームなど、ほぼどんなUNIXシステムでも「お目にかかる」機能になっています。sh
や一般的なUNIXとの互換性への変遷は、なかなか一筋縄ではいきませんでしたが、非常に得ることの多い経験でもありました。これは主に筆者自身がこれらのほとんどのオペレーティング・システムにアクセスできないためなのですが、1本のスクリプトでこれらすべてのプラットフォームをサポートするようにするために、スクリプトは非常に技巧を弄したものとなりました。ありがたいことに、世界中のkeychain
ユーザーが、いろいろなオペレーティング・システムにアクセスできますし、数多くの人々が互換性の問題を発見したり、それらの問題を解決するためのパッチを送ってくれたりと、大いに協力してくれました。
実際には、解決すべき互換性の問題は2つの種類に分けられます。1つは、世の中で一般に使用されているフリー版および商用版のすべてのUNIXのsh
シェル、zsh
(sh
互換モード)、bash
バージョン1および2など、すべての種類のsh
で完全にサポートされているビルトイン、式、および演算子だけをkeychain
で使用する必要があったということです。以下に、ユーザーから寄せられたシェルの互換性に関する修正点で、keychain
のソースに適用されたものをいくつか紹介します。
以前のsh
シェルは、ユーザーのホーム・ディレクトリーを参照するための~
の規則をサポートしませんので、~
を使っていた行は、$HOME
を使うように変更しました。
リスト3. $HOMEを使うようにする
hostname=`uname -n` pidf=${HOME}/.ssh-agent-${hostname} cshpidf=${HOME}/.ssh-agent-csh-${hostname}
次に、source
コマンドをまったくサポートしていない純粋主義NetBSDの/bin/sh
との互換性が得られるように、source
への参照は、すべて.
に変更しました。
リスト4. NetBSDに合わせてあげる
if [ -f $pidf ] then . $pidf else SSH_AGENT_PID="NULL" fi
ついでに、性能の改善につながる修正もいくつか施しました。ある賢いシェル・スクリプターが、touch foo
と入力してファイルにtouchする代わりに、次のようにすればよいと教えてくれました。
リスト5. ファイルにtouchする
> foo
外部のライブラリーを使わずに、組み込みのシェルの構文を活用するようにすると、fork()
を使わずに済み、スクリプトもわずかながら効率がよくなります。> foo
は、sh
互換のシェルならちゃんと動くはずです。ただし、ash
ではサポートされていないようです。ash
は、常日頃使用されるのではなく、緊急ディスク型シェルの意味合いが強いので、この点は、ほとんどの人にとって問題とはならないはずです。
プラットフォームの実行ファイルの問題
スクリプトを複数のUNIXオペレーティング・システムの下で動作するようにするには、単に純粋なsh
の構文に合わせるだけでは済みません。ほとんどのスクリプトは、grep
、awk
、ps
などの外部コマンドの呼び出しも行います。これらのコマンドについても、できるかぎり標準に準拠した形で呼び出す必要があります。たとえば、UNIXのほとんどのバージョンに含まれているecho
は、-e
オプションを認識しますが、Solarisは認識しません。Solarisの場合、このオプションが指定されると、stdoutに-e
を書き出すだけです。そこで、Solarisにも対応させるために、keychain
は、echo -e
が正しく動作するかどうかを自動検出するようになりました。
リスト6. Solarisの嗅ぎ分け
if [ -z "`echo -e`" ] then E="-e" fi
上では、-e
によるエスケープがサポートされている場合、E
に-e
がセットされます。このとき、echoは以下のようにして呼び出すことができます。
リスト7. もっと旨いechoのかけ方
echo $E Usage: ${CYAN}${0}${OFF} [ ${GREEN}options${OFF} ] ${CYAN}sshkey${OFF} ...
echo -e
の代わりにecho $E
を使用することで、-e
オプションは、必要に応じて動的に有効にしたり無効にしたりすることができます。
pidof、ps
たぶん、互換性に関する修正でもっとも重要なものは、keychain
が現在実行中のssh-agent
プロセスを検出する方法の変更に関る部分だったと思います。それまで筆者は、pidof
コマンドを使ってこれを行っていましたが、pidof
をサポートしていないシステムも多いため、この方法は止めざるを得なくなりました。実際、pidof
は、現在有効なUIDが所有しているssh-agent
プロセスだけわかればよいときでも、ユーザーとは無関係に、システム上で実行されているすべてのssh-agent
プロセスを列挙してきますので、一番うまい方法だとも言えません。
そこで、pidof
は止めにして、ps
の出力をgrep
とawk
にパイプしてやり、必要なプロセスidを抽出するやり方に切り換えることにしました。以下は、ユーザーから寄せられた修正です。
リスト8. pidofよりもパイプを利用したほうがうまく処理できる
mypids=`ps uxw | grep ssh-agent | grep -v grep | awk '{print $2}'`
上のパイプラインでは、mypids
変数に、現在のユーザーが所有しているすべてのssh-agent
プロセスの値がセットされます。パイプラインの中のgrep -v grep
コマンドの部分は、grep ssh-agent
プロセスが、われわれの求めているPID一覧に含まれないようにしています。
このやり方は、考え方そのものは良いのですが、ps
のオプションがBSDやSystem V UNIX系統のさまざまなバージョンであまねく標準化されているわけではないため、それがまた別の悩みの種となります。たとえば、こういうことです。ps uxw
は、Linuxではうまく働くが、IRIXでは働かない。あるいは、ps -u username -f
は、LinuxやIRIXやSolarisでは大丈夫だが、BSDでは、BSDスタイルのps
のオプションしか受け付けないので、うまくいかない、といった問題です。この問題を回避するために、keychain
は、ps
のパイプラインを実行する前に、現在のシステムのps
がBSDまたはSystem Vの構文が使えるかどうかを自動的に検出するようにしています。
リスト9. BSDかSystem Vかの検出
psopts="FAIL" ps uxw >/dev/null 2>&1 if [ $? -eq 0 ] then psopts="uxw" else ps -u `whoami` -f >/dev/null 2>&1 if [ $? -eq 0 ] then psopts="-u `whoami` -f" fi fi if [ "$psopts" = "FAIL" ] then echo $0: unable to use \"ps\" to scan for ssh-agent processes. Report KeyChain version and echo system configuration to drobbins@gentoo.org. exit 1 fi mypids=`ps $psopts 2>/dev/null | grep "[s]sh-agent" | awk '{print $2}'` > /dev/null 2>&1
System VとBSDの両方のスタイルのps
コマンドが使えるようにするために、スクリプトでは、ps uxw
の「dry run」を実行し、ファイルの更新はしません。このコマンドからのエラー・コードが0なら、ps uxw
がちゃんと働いているということですので、psopts
の値を適当に設定します。一方、ps uxw
が0以外のエラー・コードを返してきた場合 (BSDスタイルのオプションを使う必要があるという意味ですので)、ps -u `whoami` -f
を飾ることなく実行し、やはり出力はすべて捨てます。ここまでで、BSDかSystem Vのどちらかのps
の変種が利用できるのかがわかれば、しめたものです。それがわからなければ、エラーを出力して終了します。しかしながら、どちらかのps
コマンドがうまく働く可能性は大きいので、その場合には、上のコード・サンプルの最終行にあるps
のパイプラインを実行します。ps
の直後に$psopts
変数の展開を与えるることで、ps
コマンドに適当なオプションを渡すことができます。
ps
のパイプラインには、Hans Peter Verne氏が筆者に教えてくれた宝のようなgrep
の真価も示されています。もうパイプラインにgrep -v grep
は使っていません。これは消して、grep "ssh-agent"
をgrep "[s]sh-agent"
に変更しています。grep
コマンド1個で、grep ssh-agent | grep -v grep
と同じことができているのです。なぜだか、おわかりになるでしょうか。
リスト10. grepの妙技
mypids=`ps $psopts 2>/dev/null | grep "[s]sh-agent" | awk '{print $2}'` > /dev/null 2>&1
降参ですか。grep "ssh-agent"
とgrep "[s]sh-agent"
がまったく同じテキストをひっぱり出してくるはずだと考えた方、それが正解です。では、ps
の出力がパイプでこれらのコマンドに渡されると、どうして結果が違ってくるのでしょうか。答はこうです。grep "[s]sh-agent"
にした場合、ps
のプロセス一覧へのgrep
コマンドの表示方法を変えているのです。その結果、[s]sh-agent
という文字列は[s]sh-agent
という正規表現に一致しませんので、grep
が自分自身をひっぱってこないようにしているのです。素晴らしいですよね。まだ腑に落ちないようでしたら、少しgrep
を触ってみると、すぐに解ることと思います。
まとめ
筆者のOpenSSHについての連載は今回で終わりです。みなさんがOpenSSHのことをよく理解し、OpenSSHを使ってシステムのセキュリティーを高められるようになれば幸いです。来月からは、共通テーマのコラムとして、「高度なファイルシステムの実装者向けガイド」を連載します。
ダウンロード可能なリソース
関連トピック
- 「共通テーマ: OpenSSHキーの管理 第1回」 (developerWorks、2001年7月) では、RSA/DSA認証を取り上げています。
- 「共通テーマ: OpenSSHキーの管理 第2回」 (developerWorks、2001年9月) では、ssh-agentとkeychainを紹介しています。
keychain
の一番最近のバージョンは、Gentoo Linux Keychainのページで入手できます。- OpenSSH開発のホームページもご覧ください。OpenSSH FAQも覗いてみてください。
- PuTTYは、Windowsマシン用の優れたsshクライアントです。
- O'Reilly & Associates発行の書籍『SSH, The Secure Shell: The Definitive Guide』 (2001年刊) は役に立ちます。この書籍の著者のサイトには、同書に関する情報、FAQ、ニュース、アップデートが掲載されています。
- Slashdotには、電脳お宅向けのニュースや関連情報が掲載されています。
- Freshmeatには、新発表のオープン・ソース・パッケージ一覧が適宜掲載されます。
- developerWorksに掲載されている他のLinux参考文献もご覧ください。
- developerWorksの他のオープン・ソース関連記事もご覧ください。