実践で学ぶ、一歩進んだサーバ構築・運用術

第 9 回 ssh (前編)


秘密かぎと公開かぎ

ssh では, クライアント側とサーバー側のそれぞれが自身の秘密かぎと公開かぎを持ちます。 サーバーのかぎはサーバーのインストール時に作られます。 OpenSSH のデフォルトでは, 秘密かぎと公開かぎをまとめたファイルが, /usr/local/etc/ssh_host_key に, 公開かぎをテキストで表現したファイルが, /usr/local/etc/ssh_host_key.pub にインストールされます。

一方, クライアントのかぎはユーザーそれぞれが自分専用のかぎを作らなければなりません。 デフォルトでは, 秘密かぎと公開かぎをまとめたファイルが ~/.ssh/identity に, 公開かぎをテキストで表現したファイルが, ~/.ssh/identity.pub に作られます。

ssh プロトコル

クライアント側の identity.pub ファイルの内容が, サーバー側の ~/.ssh/authorized_keys に登録されていれば, ssh クライアントを使ってログインできます。 ssh プロトコルについてより詳しく知りたい方は, インターネット・ドラフト draft-ietf-secsh-architecture-05 *13 などを参照してください。

ここでは ssh プロトコルの概要を簡単に説明します。

共通かぎの送信

ssh クライアントが ssh サーバーへ接続要求を出すと, 互いのバージョン番号などを交換した後, クライアントが共通かぎをランダムに生成し, サーバーに対して送信します。 この共通かぎ*14 は, クライアントとサーバーとの間の通信を暗号化するために使われるかぎです。 共通かぎをサーバーに送信するときに盗聴されてしまっては元も子もありませんから, 暗号化して送ることになりますが, この時のプロトコルを 図 3 に示します。

共通かぎの送信
図 3 共通かぎの送信

この後のクライアントとサーバー間の通信は共通かぎによって暗号化される。

まず, サーバーは自身の公開かぎ(1)をクライアントに対して送信します。 クライアントは接続相手サーバーごとの公開かぎを保存しています。 もしサーバーの公開かぎ(1)が, 以前同じサーバーから送られてきた公開かぎと一致しなかった場合は, 図 4 のような警告をクライアントがユーザーに対して表示します (OpenSSH の場合。以下,同様)。 すなわち, だれかがセッションのハイジャック*15 をしている可能性がある, という警告です。


@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
Please contact your system administrator.
Add correct host key in /home/sengoku/.ssh/known_hosts to get rid of this message.
RSA host key for azabu.klab.org has changed and you have requested strict checking.

図 4 サーバーかぎが異なるという警告

もちろん, ハイジャックされたのではなくて, 本当にサーバーのかぎが変更されたのかも知れません。 サーバーの管理者に問い合わせる必要があるでしょう。 図 4 の警告表示にあるように, クライアントは接続相手サーバーごとの公開かぎを ~/.ssh/known_hosts ファイルに保存しています。 本当にサーバーのかぎが変更されたのであれば, known_hosts ファイル内の, このサーバーに対応する部分を削除して, 接続し直してください。

known_hosts ファイルに, サーバーの公開かぎ(1)が登録されていない場合, クライアントには, その公開かぎが本物かどうか確認する手段がありません。 そこで, ユーザーに対して図 5 のように表示*16 し, ユーザーの確認を求めます。 本当に接続するのか? (「Are you sure you want to continue connecting(yes/no)?」 という問いに対し, 「yes」と答えれば, サーバーの公開かぎ(1)が, known_hosts ファイルに登録されます。


The authenticity of host 'azabu.klab.org' can't be established.
RSA key fingerprint is 9e:f3:1d:35:67:ad:74:54:35:8a:7b:9a:46:dc:c4:c7.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'azabu.klab.org' (RSA) to the list of known hosts.

図 5 初めて接続するサーバーである場合の確認

さて, これでサーバーから正しい公開かぎが送られてきたことが確認できました。 次にクライアントはサーバーの公開かぎ(1)(図 5 を参照)を使って, 共通かぎ(2)を暗号化します。 そして暗号化された共通かぎ(3)をサーバーへ送信します。 サーバーは, 自身の秘密かぎ(4)を使って, 暗号化された共通かぎ(3)を復号化することによって, 共通かぎを得ます。

以上で, クライアントとサーバーは同じ共通かぎを持つことができました。 この後, クライアントとサーバーとの間の通信は, この共通かぎによって暗号化されます。

この暗号通信が行える, ということは サーバーがクライアントと同じ共通かぎを持っていることの証明ですから, サーバーが正しい秘密かぎ(4)を持っていることの証明にもなります。 つまりクライアントがサーバーの認証を行ったことになります。

クライアント認証

次はサーバーがクライアントの認証を行う番です。

接続に先立って, サーバー上の各ユーザーのホーム・ディレクトリにある ~/.ssh/authorized_keys ファイルに, ログインを許可するクライアントの公開かぎを登録しておきます。 クライアント認証とは, 登録された公開かぎに対応する秘密かぎをクライアントが持っているか 確認することです。 もちろん秘密かぎ自体をサーバーに送ってしまうと, 「秘密」でなくなってしまいますから, 秘密かぎを送らずに秘密かぎを持っていることを サーバーに対して証明しなければなりません。 クライアント認証のプロトコルを 図 6 に示します。

クライアント認証
図 6 クライアント認証

まず, クライアントは, サーバーにログインするときのユーザー ID と, 自身の公開かぎ(5)をサーバーに対して送信します。 サーバーは, ログインを要求されたユーザーのホーム・ディレクトリの authorized_keys ファイルを確認して, ログインが許可されているか調べます。

authorized_keys ファイルにクライアントの公開かぎ(5)が登録されていれば, サーバーはランダムな数値 X (6)を生成し, 公開かぎ(5)で暗号化します。 そして暗号化された X をクライアントに送信します。 これをチャレンジ(7)と呼びます。 つまりクライアントが本当に公開かぎ(5)に対応する秘密かぎを持っているのか 試すための「チャレンジ」です。

クライアントは, チャレンジ(7)を秘密かぎ(8)で復号化して元の X (6)を得ます。 X をハッシュ関数 MD5 で変換した値 Xc (9)を レスポンス(「チャレンジ」に対する受け答え)としてサーバーへ送信します。 サーバーは, クライアントと同様に X (6)をハッシュ関数 MD5 で変換した値 Xs (10)を計算し, クライアントから送られてきたレスポンスと比較します。

もし一致していたらならば, クライアントがチャレンジを正しく復号化できたことの証明になり, クライアントが正しい秘密かぎを持っていることの証明にもなります。 つまりサーバーがクライアントの認証を行ったことになります。

ssh-keygen

クライアント側のかぎを作るにはクライアント側で ssh-keygen コマンドを使います。 ssh-keygen コマンドを使って, クライアントのかぎを作る実行例を 図 7 に示します。


asao:/home/sengoku % ssh-keygen
Generating RSA keys: .........................................ooooooO.....ooooooO
Key generation complete.
Enter file in which to save the key (/home/sengoku/.ssh/identity):
Created directory '/home/sengoku/.ssh'.
Enter passphrase (empty for no passphrase): this is a sample passphrase
Enter same passphrase again: this is a sample passphrase
Your identification has been saved in /home/sengoku/.ssh/identity.
Your public key has been saved in /home/sengoku/.ssh/identity.pub.
The key fingerprint is:
2e:9b:b8:2e:0e:6a:a3:d9:b1:cf:77:14:63:73:0b:e4 sengoku@asao.gcd.org

図 7 ssh-keygen コマンドで秘密かぎと公開かぎを作る

最初に, 「Enter file in which to save the key」と, かぎを保存するファイル名を聞いてきます。 デフォルトは ~/.ssh/identity です。 この場合, 秘密かぎと公開かぎをまとめたファイルが ~/.ssh/identity に, 公開かぎをテキストで表現したファイルが ~/.ssh/identity.pub に作られます。

~/.ssh/identity.pub ファイルの内容を, サーバー側のホーム・ディレクトリの ~/.ssh/authorized_keys ファイルに追加 (無い場合は新規に作成する)すれば, ssh でログインできるようになります。

ここで注意すべきなのは秘密かぎ(identity ファイル)の管理です。 秘密かぎを持っている人はだれであれ正規ユーザーであると サーバーに見なされるわけですから, 絶対に第三者に盗まれないようにしなければなりません。 当然, 秘密かぎファイルはデフォルトで ユーザー本人しか読めないパーミッションになっていますが, パーミッションが与えられて無い人でも 何かのはずみに読むことができるかも知れません。

例えば, テープなど*17 にバックアップすれば, テープを読める人ならファイルのパーミッションに関係なく読めますし, マシンに物理的にアクセスできる人にとっては ファイルのパーミッションなど関係ありません。 フロッピあるいは CD-ROM などからブートさせれば だれでも root になれるのですから。

また, Windows95/98 などの OS では, ファイルにユーザーごとのパーミッションを指定することさえできません。

パスフレーズの設定

そこで, ssh では秘密かぎは暗号化して保存する仕掛けになっています。 暗号化のためのかぎが, 「Enter passphrase」で入力するパスフレーズ*18 です。 パスフレーズさえ他人に知られなければ, 秘密かぎファイル自体は盗まれても大丈夫です*19 。

私の場合, Linux 上で ssh-keygen を実行して作ったかぎファイル identity を, Windows98 上のディレクトリ「C:\ SENGOKU\.ssh\identity」に コピーして使っています。 写真 2 のように, ssh クライアントは実行時にパスフレーズの入力を要求しますから, 他人に勝手にかぎを使われる心配はありません。

さて, ssh-keygen コマンドがパスフレーズの入力を要求したとき (図 7 中の「Enter passphrase」の部分)で, そのまま改行を押せば(「empty for no passphrase」), かぎファイル identity が暗号化されずに作られますが, このようにして作ったかぎは, 盗まれてもセキュリティ上問題がない用途に限定するべきです。 特殊な事情がない限りは, 必ずパスフレーズを設定するようにしてください。

パスフレーズの入力

もう一点, 注意しなければならないのは, 入力したパスフレーズが盗聴されないか, という点です。 例えば telnet など (通信路が暗号化されないプロトコル)でリモート・マシンにログインして, ssh-keygen コマンドなどを実行した場合, 入力したパスフレーズは平文でネットワークを流れますから, 盗聴される危険が無いとは言えません。 面倒でも ssh-keygen を実行するマシンのコンソールなどから入力するべきです。

X 端末は, セキュリティに十分注意を払わないと, キー入力や X 端末に表示させた文字などが盗聴される恐れがあります。 X 端末上でのパスフレーズの入力は, なるべく避けたほうが無難でしょう。

反面, Windows マシンを端末として用いる場合など, 手元のマシンで端末エミュレータを実行する場合は, マシン自体に第三者が小細工する恐れ*20 が無ければ比較的安全です。

従って, 手元のマシンが Windows マシン(クライアントA), 少し離れたところと遠隔地に Linux マシンがある (それぞれサーバーB ,サーバーC )場合, 次のような手順を踏むと良いでしょう。


(1)サーバーB のコンソールからログインして ssh-keygen コマンドを実行し, パスフレーズを設定して identity ファイルを作成。 これをフロッピへコピーした後, サーバーB 上の identity ファイルを削除。 identity.pub ファイルを ~/.ssh/authorized_keys へ移動。 authorized_keys は他人から読めないようにパーミッションを設定する。 例えば, 「chmod 600 ~/.ssh/authorized_keys」を実行する。

(2)(1)のフロッピをクライアントA へ入れて identity ファイルを適当な位置へコピー。 フロッピは内容を完全に消去*21 するか, 厳重に保管。

(3)クライアントA 上の ssh クライアントを使ってサーバーB へログイン。 この時, キーボードから入力するパスフレーズを肩越しに他人に盗み読まれないように注意 (以下同様)。

(4)クライアントA と サーバーB の間の通信は暗号化されているので盗聴される心配はない。 従ってサーバーB 上で実行するコマンドに対して パスフレーズなどを安全に入力することができる。

(5)サーバーB 上でssh-keygen コマンドを実行し, パスフレーズを設定して ~/.ssh/identity および, ~/.ssh/identity.pub を作成する。

(6)適当な方法(ftp, メールなど)でサーバーB 上の ~/.ssh/identity.pub ファイルの内容, あるいは(1)で ~/.ssh/authorized_keys へ移したクライアントA の identity.pub をサーバーC へ送る。 identity.pub は公開かぎなので盗聴されても構わないが, 内容が改変されていないか確認する必要はある。

(7)(5)の identity.pub をサーバーC 上の ~/.ssh/authorized_keys に登録。

(8)サーバーB 上のssh クライアント, あるいはクライアントA 上の ssh クライアントを使ってサーバーC へログイン。


手元のマシンが Linux マシンなら, 「サーバーB」を手元のマシンと読み替えて, (5)以降の操作を行うだけで済みます。 任意のサーバーに対して(6)から(8)の操作を繰り返せば, ssh クライアントのログイン先をいくつでも増やすことが可能です。

上記手順は一見複雑に見えますが, ネットワークを流れるデータはすべて安全ではない, と仮定すれば必然的な手順と言えるでしょう。 それに, いったんすべてのサーバー・マシンと手元の端末間のすべての通信を 暗号化する設定にしてしまえば, 「安全でない」ネットワークを使う状況が無くなってしまうので, むしろ楽です。

すなわち, 1 カ所でも平文でデータが流れる部分があると, その部分に重要なデータが流れないか常に気を配らなければならないわけで, それなら最初の一度だけ十分注意を払って 平文で流れる部分を完全に無くしてしまう方が理にかなっています。 上記手順(8)で ssh クライアントを使ってログインする実行例を 図 8 に示します。


asao:/home/sengoku % ssh azabu.klab.org
Enter passphrase for RSA key 'sengoku@asao.gcd.org': this is a sample passphrase
Last login: Mon Oct 9 15:35:15 2000 from asao.gcd.org
azabu:/home/sengoku %

図 8 ssh クライアントを使ってリモート・ログイン

クライアントA とサーバーB 間の通信は ssh で暗号化されているので, サーバーB (図 8 の例では asao.gcd.org)上では, 安全にパスフレーズを入力することができます。 しかし, rsh コマンドと比べるとパスフレーズを入力しなければならない分, 面倒に感じるかも知れません。 普段, リモート・ログインの手段として telnet を使っている人ならば, ユーザー ID とパスワードを入力するのに比べれば楽なので, パスフレーズを入力するのもさほど面倒とは感じないかも知れません。

では, リモート・コピーの場合はどうでしょうか(図 9)。 rcp コマンドならばパスワード無しにファイルがコピーできることを考えると, ファイル 1 つコピーするたびにパスフレーズを入力するのは 手間と感じる人の方が多くなりそうです。


asao:/home/sengoku % scp .cshrc azabu:/tmp/.
Enter passphrase for RSA key 'sengoku@asao.gcd.org': this is a sample passphrase
.cshrc   |   1 KB | 1.8 kB/s | ETA: 00:00:00 | 100%

図 9 ssh クライアントを使ってリモート・コピー

ssh-keygen コマンドを使ってクライアントのかぎを作成するときに, パスフレーズを設定しなければ, パスフレーズ無しにリモート・ログインやリモート・コピーできますが, 前述したように identity ファイルが盗まれるだけで秘密かぎが 他人の手に渡ってしまうので, かなり危険です。

では, どうすればいいのでしょうか ?

ssh-agent

暗号化したかぎファイルだと, 復号化するためにパスフレーズが必要。 だからといって復号化したファイルを置いておくと, ファイルを盗まれる恐れがある。 ならばファイルでなくメモリー上に置いておけば良い, という発想で作られたのが ssh-agent です。 メモリー上に置いたデータならばマシンをリブートすれば消えてしまいますから, ファイルに置くよりは安全と言えます。

ssh-agent コマンドは実行するとデーモンとして動作します。 ssh クライアントなど, 他のコマンドと通信するために, ssh-agent コマンドは unix ドメイン・ソケットを使用します。 実行例を図 10 に示します。


asao:/home/sengoku % ssh-agent
setenv SSH_AUTH_SOCK /tmp/ssh-JOmUZ377/agent.377;
setenv SSH_AGENT_PID 378;
echo Agent pid 378;

図 10 ssh-agent コマンドと環境変数

この場合, unix ドメイン・ソケット /tmp/ssh-JOmUZ377/agent.377 を使用し, デーモンのプロセス ID が 378 番であることを示します。 このファイル名およびプロセス ID を ssh クライアントなどに伝えるために環境変数を利用します。 つまり, 図 11 などと実行してから ssh コマンドなどを実行します。


asao:/home/sengoku % setenv SSH_AUTH_SOCK /tmp/ssh-JOmUZ377/agent.377
asao:/home/sengoku % setenv SSH_AGENT_PID 378

図 11 環境変数を設定する

もちろん setenv コマンドを手で入力するのは面倒なので, ssh-agent を実行するとき, その出力が自動的に実行されるように, eval コマンドを使うと良いでしょう(図 12)。


asao:/home/sengoku % eval `ssh-agent`
Agent pid 378

図 12 ssh-agent の実行

次に, ssh-agent デーモンのメモリーに秘密かぎを登録します。 そのためには ssh-add コマンドを用います(図 13)。 ssh-add を実行する前に, あらかじめ 図 12 を実行しておく必要があります。


asao:/home/sengoku % ssh-add
Need passphrase for /home/sengoku/.ssh/identity
Enter passphrase for sengoku@asao.gcd.org: this is a sample passphrase
Identity added: /home/sengoku/.ssh/identity (sengoku@asao.gcd.org)

図 13 ssh-add を使って秘密かぎを登録する

図 13 のように, ssh-add はパスフレーズの入力を要求し, このパスフレーズを使って identity ファイルを復号化し, ssh-agent デーモンのメモリーに登録します。 いったん ssh-add を使って秘密かぎを ssh-agent に登録した後は, パスフレーズ無しに ssh/slogin/scp コマンドが使えます。

例えば, リモートの azabu.klab.org 上で hostname コマンドを実行するには, 図 14 のように実行します。


asao:/home/sengoku % ssh azabu.klab.org hostname
azabu.klab.org

図 14 ssh を使ったリモート実行

秘密かぎを ssh-agent のメモリーに登録してあるため, パスフレーズを入力しなくて済む。

*13
インターネット・ドラフトとは, IETF で標準化作業中の草稿です。 標準化が行われれば RFC になりますが, インターネット・ドラフトの段階では 常に更新や置き換えが行われる可能性があります。 インターネット・ドラフトは作られてから 6 カ月たつと無効になります。 draft-ietf-secsh-architecture-05 は次の URL で参照できます。 http://search.ietf.org/internet-drafts/draft-ietf-secsh-architecture-05.txt
*14
暗号化に使われるかぎと,復号化に使われるかぎが同一である,対称暗号化方式におけるかぎ。
*15
連載第 7 回「ファイアウォール(前編)」参照。
*16
サーバーのかぎの指紋(fingerprint)は, サーバー上で「ssh-keygen -l -f /usr/local/etc/ssh_host_key.pub」 などと実行することにより知ることができます。 この指紋と,図 5 で表示される指紋が一致するか確認すれば, 公開かぎが本物かどうか確認できます。
*17
最近はテープをバックアップに利用することはほとんど無いかも知れません。
*18
パスワードが 8 文字程度の「ワード」(単語)であるのに対し, パスフレーズ(語句)は複数の単語からなる「句」を設定できます。 パスワードは辞書攻撃によって比較的簡単に破ることができますが, パスフレーズならば(よほど有名な句を設定しない限りは) 破られる心配は無いと言えるでしょう。
*19
とは言っても, 盗まれないに越したことはないわけで, むやみにコピーしたり, 複数の人が使う Windows95/98 マシンに インストールするのは避けるべきでしょう。
*20
例えば,キーボードとマシンの間のケーブルに盗聴装置を付けられてしまうと, どうしようもありません。 もっと原始的に, キーボードを肩越しにのぞく監視カメラを設置するだけでも, パスフレーズを盗むことは可能です。
*21
もちろん,DEL コマンドで消すだけでは復活が可能なので, 確実に内容を消す必要があります。

(設定ファイル)


本稿は日経Linux 2000 年 12 月号に掲載された、 実践で学ぶ、一歩進んだサーバ構築・運用術, 第 9 回「ssh (前編)」を日経BP 社の許可を得て転載したものです。

Copyright(C)2000 by 仙石浩明 <sengoku@gcd.org>
無断転載を禁じます

| home | up |

sengoku@gcd.org