共通テーマ: OpenSSH キー (鍵) の管理: 第 3 回

エージェント転送と keychain の改善点

この連載第3回目の記事では、Daniel Robbins氏がOpenSSHエージェント接続転送を利用してセキュリティーを高める方法を紹介します。また、keychainシェル・スクリプトに加えられた最近の改善点についても報告します。

Daniel Robbins (drobbins@gentoo.org), President/CEO, Gentoo Technologies, Inc.

Daniel Robbins氏は、ニューメキシコ州アルバカーキーに住んでいます。彼は、Gentooプロジェクトのチーフ・アーキテクト、Gentoo Technologies Inc. の社長/CEOです。著書に、Macmillanから出版されているCaldera OpenLinux Unleashed、SuSE Linux Unleashed、Samba Unleashed があります。Daniel氏は、小学2年のとき初めてLogoプログラム言語や、中毒になる恐れのあったPac Manに出会って以来、何らかの形でコンピューターに関係してきています。これで、彼がなぜSONY Electronic Publishing/Psygnosisでリード・グラフィック・アーチストを務めているかが分かるでしょう。愛妻Maryさんや、生まれたばかりの愛娘Hadassahちゃんとの時間をとても大切にしています。彼の連絡先はdrobbins@gentoo.org です。



2002年 2月 01日

多くの開発者が、旧来の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-agentkeychainを使用しないようにし、OpenSSHの新しい認証転送機能を使って、パスワードなしの認証を信頼できないホストにも広げていけばよい、ということです。要するに、リモートのsshセッションを許し、信頼できるシステム上で実行されているssh-agentと連絡できるようにすることで、認証転送が有効に機能するというわけです。


認証エージェント転送

認証転送がどのようにして機能するのかを理解していただくために、まず、drobbinsというユーザーが、lappy という信頼できるラップトップ1台とtrustbox という信頼できるサーバー1台を使用し、信頼できない他の2つのシステムnotrust1notrust2 にアクセスする必要があるという、仮設の状況について考えてみることにしましょう。現在、このユーザーは、下図のように、これら4台のすべてのマシンでssh-agentkeychain を使用しています。

図1. 信頼できるマシンと信頼できないマシンでssh-agentが稼動
図1

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

図2. lappyのみでssh-agentを稼動。より安全性の高い構成
図2

もちろん、この方法における問題は、drobbinslappyからしかパスワードなしの接続を確立できなくなった点にあります。そこで、認証転送を有効にして、この問題を回避する方法について考えてみましょう。

すべてのマシンがOpenSSHの最近のバージョンを実行しているものとすると、認証転送を利用することで、この問題を回避することができます。認証転送を利用すると、sshを発するマシン自身にssh-agentを実行させておく必要はなく、リモートのsshプロセスは信頼出来るマシン上で実行されているssh-agentとコンタクト出来るようになります。こうすれば、通常は、1台のマシンのみでssh-agent (およびkeychain) を実行させることになり、このマシンを基点として (直接または間接に) つながっているすべてのssh 接続が、みなさんのローカルなssh-agentを使用するようになる、ということを意味します。

認証転送を行えるようにするには、lappytrustboxの/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として、明示的に認証転送が有効になるようにしてみてください。われわれが、上のように、認証転送を利用してtrustboxnotrust1にログ・インしたときに、裏側で何が行われていたのかを示すのが、以下の図です。

図3. エージェント転送の仕組み
図3

図からわかるように、sshは、trustboxに接続するとき、lappyで実行されているssh-agentとの接続を維持しています。trustboxからnotrust1へのssh接続が行われる場合、新しいsshプロセスは、それまでのsshへの認証接続を維持します。その結果、チェーンが延びるわけです。この認証チェーンをnotrust1から先の他のホストにまで延長できるかどうかは、notrust1の /etc/ssh/ssh_configがどのように設定されているかによります。エージェント転送が有効にされているかぎり、チェーンの中のすべてのマシンが、信頼できるlappy上で実行されているssh-agentを利用することで、認証を行うことができることになります。


エージェントによる接続転送の利点

認証転送には、セキュリティーに関して、本稿では触れないものでも数々の利点があります。エージェント接続転送の重要性を納得させるために、Charles Karney氏が筆者に説いているセキュリティー上の利点は、以下の3つです。

  1. 私有鍵が、信頼できるマシンにのみ保存される。したがって、悪意のあるユーザーが暗号化された鍵をディスクから盗み出し、暗号を解読するのを防止できる。
  2. ssh-agentが、信頼できるマシンでのみ実行される。したがって、侵入者がリモートのssh-agentプロセスのメモリー・ダンプをとり、ダンプから解読私有鍵を抜き出すのを防止できる。
  3. 信頼できるマシンにパスフレーズをキーインしておくだけでよいので、何らかのキーストローク・ロガーが入力時にパスフレーズをこっそり盗み出すのを防止できる。

認証エージェント接続転送に頼ることの欠点は、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の構文に合わせるだけでは済みません。ほとんどのスクリプトは、grepawkpsなどの外部コマンドの呼び出しも行います。これらのコマンドについても、できるかぎり標準に準拠した形で呼び出す必要があります。たとえば、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の出力をgrepawkにパイプしてやり、必要なプロセス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を使ってシステムのセキュリティーを高められるようになれば幸いです。来月からは、共通テーマのコラムとして、「高度なファイルシステムの実装者向けガイド」を連載します。

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, Open source
ArticleID=228928
ArticleTitle=共通テーマ: OpenSSH キー (鍵) の管理: 第 3 回
publish-date=02012002