OpenSSH はそれ自体でも多くの機能をもっていますが、 同時にこれは UNIX の「ツールボックス・アプローチ」にのっとって設計されており、 他のソフトウェアと柔軟に組み合わせることができます。 本章では OpenSSH とそれ以外のソフトウェアを使った応用や、 OpenSSH 自体のちょっと変わった使い方を紹介します。
CVSやSubversion などのバージョン管理ソフトウェアは、 あらかじめ OpenSSH と組み合わせて使用できるように設計されています。 本節ではこれらの簡単な使い方と 4.3.3. rsync をつかったファイル転送 でも紹介した rsync の応用を紹介します。
CVS は Unix で伝統的に使われてきたバージョン管理システムのひとつで、 複数のユーザがひとつのリポジトリのソースファイルを協調して編集できるようになっています。 CVS 自身はネットワーク経由で複数のユーザがリポジトリにアクセスするための pserver という独自の認証システムをもっていますが、pserver の通信は暗号化されておらず、 したがって盗聴やなりすましなどの攻撃を受ける危険性があります。 また pserver を使うには TCP 2401番ポートで待ち受ける専用のデーモンを 立ち上げる必要があり、その分だけシステム管理の手間も増えることになります。 いっぽう OpenSSH と CVS を組み合わせて使う場合、 ユーザはネットワーク経由でリポジトリを安全に編集することができ、 しかもユーザの認証には通常の OpenSSH によるログインと同じ方法が使えます。
CVS を OpenSSH と組み合わせるのは簡単です。
まずクライアントとサーバの両方に cvs
コマンドをインストールしておき、
CVS を使うユーザはクライアント上であらかじめ環境変数 CVS_RSH
に ssh
という値を
指定しておきます。ユーザはクライアント上で cvs
コマンドを実行するさい、
リポジトリの位置としてローカルなパス名のかわりに
「:ext:ユーザ名@ホスト名:サーバ上のリポジトリのパス名
」という
文字列を指定します。すると cvs
コマンドは CVS_RSH
の値に応じて
内部で自動的に ssh
コマンドを呼び出し、OpenSSH 経由で CVS サーバにログインして
リモートコマンドを実行します (図 cvs-client-server)。
なお、このときサーバ上ではつねに "cvs server
" というコマンドが
実行されます。このようにすると cvs
コマンドはサーバモードで動き、
標準入出力を介してクライアントと通信します。
OpenSSH を経由して CVS サーバにアクセスする |
---|
$ cvs -d :ext:[ユーザ名@]ホスト名:リモートパス名 コマンド 引数1 引数2 ... あるいは (環境変数 |
実行例:
client$ cvs -d :ext:yusuke@cvs.example.com:/cvs checkout docs (リモートのリポジトリから docs モジュールをチェックアウトする) Enter passphrase for key '/home/yusuke/.ssh/id_rsa': (秘密鍵のパスフレーズを入力する) cvs checkout: Updating docs U docs/Makefile U docs/man2html.prl U docs/README ...
なお、cvs
コマンドから呼び出される
ssh
コマンドの接続ポート番号や秘密鍵ファイルなどのオプションを
渡したい場合は、個人設定ファイルをつくり、
サーバのホスト名ごとに異なるオプションを指定してください
(4.7. 個人用の設定ファイルでさらに快適に 参照)。
複数のユーザにネットワーク経由で CVS を使わせたいが、
サーバ上でシェルを実行してほしくない場合には、
強制コマンド実行オプション (5.4.2. 一般ユーザに特定のコマンドだけを実行させる 参照)
を使うと CVS 専用のユーザアカウントを作成できます。
ただし現行の CVS バージョン 1.11 では、クライアント側のユーザがリポジトリ名を
:ext:yusuke@cvs.example.com:/etc
などと指定することによって、
ユーザがサーバ上の任意のディレクトリにアクセスできてしまうという欠点があります。
したがってこの cvs server
コマンドで --allow-user
オプションを
使えるようにするパッチをあてる必要があります。
[脚注: CVS バージョン 1.11 用のパッチは http://ioctl.org/unix/cvs/cvs.diff ]
注意 |
---|
現時点の cvs server コマンド (バージョン 1.11) をそのまま使うと、
意図しないユーザがサーバ上の任意のディレクトリにアクセスしてしまう危険性があります。
|
この場合も 5.4.2. 一般ユーザに特定のコマンドだけを実行させる と同様に、
まず環境変数 SSH_ORIGINAL_COMMAND
の値を調べ、
ユーザが通常のシェルを使おうとした場合にエラーメッセージを表示する
シェルスクリプト force-cvs-only.sh
を作成しておきます。
ここでは --allow-user
オプションを使うことによって、
ユーザの指定できるリポジトリ用ディレクトリを /cvs
に制限しています。
あとはこのスクリプトを各ユーザが送ってきた公開鍵の command="..."
オプションに指定すれば完了です。
CVS のラッパプログラム force-cvs-only.sh |
---|
#!/bin/sh # (環境変数 SSH_ORIGINAL_COMMAND の値が cvs server と等しいかどうか検査する) if [ "cvs server" != "$SSH_ORIGINAL_COMMAND" ]; then # (それ以外ならメッセージを表示し終了) echo "Sorry, only cvs use is permitted." exit 1 fi # (--allow-root オプションを追加して cvs server プログラムを実行する) exec cvs --allow-root=/cvs server |
authorized_keys 設定例: [脚注: 実際にはこれはぜんぶつながった 1行です。]
command="/usr/libexec/force-cvs-only.sh",no-pty,no-x11-forwarding,no-port-forwarding,no-agent-forwarding ssh-rsa AAAAB3NzaC1yc2E...(中略)...TYlZC91Lmw== yusuke@client
Subversion は最近普及してきた、CVS に代わるバージョン管理システムです。 基本的な使い方は CVS と似ていますが、よりネットワーク上の共同作業やセキュリティに 配慮して設計されており、OpenSSH との連携もはじめから考慮されています。
Subversion ではリポジトリをふくむモジュールの名前を URL で指定します。
Subversion はリポジトリとしてローカルなファイルシステムだけでなく、http 経由や
独自の svn プロトコルを経由したアクセスもサポートしていますが、
OpenSSH を利用する場合は URL スキーマとして svn+ssh
を利用します (SSH トンネルモード)。
この場合、リポジトリの URL は
「svn+ssh://ユーザ名@ホスト名/パス名
」という
形になります。この形式のリポジトリを指定すると svn
コマンドは
内部で ssh
を呼び出し、サーバ上で svnserve -t
という
リモートコマンドを実行します (図 svn-client-server)。
OpenSSH を経由して Subversion サーバにアクセスする |
---|
$ svn コマンド svn+ssh://[ユーザ名@]ホスト名/パス名 引数1 引数2 ... |
実行例:
client$ svn checkout svn+ssh://yusuke@svn.example.com/svn/pyvnc2swf/ (リモートのリポジトリから pyvnc2swf ディレクトリをチェックアウトする) A pyvnc2swf/vnc2swf.py A pyvnc2swf/LICENCE.TXT A pyvnc2swf/crippled_des.py A pyvnc2swf/mp3.py ...
CVS と同様に、Subversion でも
強制コマンド実行オプション (5.4.2. 一般ユーザに特定のコマンドだけを実行させる 参照)
を使うことで専用のユーザアカウントを作成できます。
Subversion では、サーバ上で svnserve
コマンドを実行する際に
リポジトリの仮想的なルートディレクトリを限定する -r
オプションがサポートされています。
このオプションを使うと、クライアントがリポジトリ以外のディレクトリにアクセスするのを防ぐことができます。
たとえば -r /svn
を指定すると、ユーザは
svn+ssh://yusuke@svn.example.com/svn/pyvnc2swf/
にアクセスするかわりに
svn+ssh://yusuke@svn.example.com/pyvnc2swf/
でアクセスでき、
親ディレクトリ /svn
にはアクセスできなくなります。
以下に示すシェルスクリプト force-svn-only.sh
では
svnserve
を実行する段階で -r
オプションを追加することにより、
予期しないディレクトリへのアクセスを防いでいます。
Subversion のラッパプログラム force-svn-only.sh |
---|
#!/bin/sh # (環境変数 SSH_ORIGINAL_COMMAND の値が svnserve -t と等しいかどうか検査する) if [ "svnserve -t" != "$SSH_ORIGINAL_COMMAND" ]; then # (それ以外ならメッセージを表示し終了) echo "Sorry, only svn use is permitted." exit 1 fi # (-r オプションを加えて svnserve プログラムを実行する) exec svnserve -t -r /svn |
authorized_keys 設定例: [脚注: 実際にはこれはぜんぶつながった 1行です。]
command="/usr/libexec/force-svn-only.sh",no-pty,no-x11-forwarding,no-port-forwarding,no-agent-forwarding ssh-rsa AAAAB3NzaC1yc2E...(中略)...TYlZC91Lmw== yusuke@client
ここでは 4.3.3. rsync をつかったファイル転送 で紹介した rsync
コマンドを使って、
サーバ上のディレクトリをネットワーク経由で安全にバックアップする例を紹介します。
4.3.3. rsync をつかったファイル転送 ではユーザが手動で rsync
コマンドを実行してファイルを転送する方法を
紹介しましたが、ここでは定期的に実行されるバックアップを人手を介さずに、かつ安全に
おこなう方法を説明します。
クライアントとサーバの両方に同じバージョンの rsync
コマンドが
インストールされていると仮定しています。
通常、ネットワーク経由で定期的にバックアップをおこなうさいには、 以下のような条件を満たす必要があります:
rsync
プロセスを root 権限で実行しなければならない。
4章では詳しく説明しませんでしたが、実は OpenSSH では秘密鍵・公開鍵ペア作成時に 空のパスフレーズを設定することで、パスフレーズの入力を必要としない秘密鍵ファイルを 作成することができます。このような秘密鍵ファイルを使うと、 パスフレーズをいっさい入力せずに公開鍵認証でサーバにログインできます。 当然ながら、通常のユーザがこのような秘密鍵を使うべきではありませんが、 デーモンなどから人手を介さず自動的にリモートホストにログインする場合は、 このようなパスフレーズなしの秘密鍵ファイルに頼る必要があります。 しかし、この秘密鍵ファイルがうっかり第三者の手に渡ってしまうと 攻撃者はサーバへ root として簡単にログインできてしまうので、 このような秘密鍵の利用には細心の注意が必要です。
パスフレーズなしの秘密鍵ファイルを作成するには、ssh-keygen
コマンドで
パスフレーズを入力するさいに、ただ Enter を入力します。
実行例:
client# ssh-keygen -t rsa (秘密鍵・公開鍵ペアを生成する) Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): id_backup (鍵の名前を指定) Enter passphrase (empty for no passphrase): (ただ Enter を入力する) Enter same passphrase again: (もう一度 Enter を入力する) Your identification has been saved in id_backup. Your public key has been saved in id_backup.pub. The key fingerprint is: 69:c3:00:fb:70:1e:21:09:09:00:0d:44:b4:d3:05:27 root@server
この後、生成された公開鍵 id_backup.pub
を
サーバ上の /root/.ssh/authorized_keys
ファイルに
登録して強制コマンド実行オプションを追加します。
基本的な方法は 5.4.2. 一般ユーザに特定のコマンドだけを実行させる で紹介した、シェルスクリプトと
環境変数 SSH_ORIGINAL_COMMAND
を使う方法と同じです。
まず環境変数 SSH_ORIGINAL_COMMAND
の値を
調べて rsync
だけを実行するシェルスクリプトを作成し、
これを command="..."
オプションで指定してやります。
ただし rsync
コマンドは CVS や Subversion とは違い、
サーバ上で実行される rsync
のコマンドライン引数が
クライアント側の rsync
に与えるパラメータによって
違ってきますので、あらかじめ rsync
のデバッグ出力を使って、
サーバ側で実行されるコマンドライン文字列を調べておく必要があります。
サーバ側で実行される rsync の引数を調べる |
---|
$ rsync -vv [オプション] 存在しないホスト名:サーバ側パス名 クライアント側パス名 |
たとえば、クライアント側が
rsync -az --delete root@server.example.com:/home /backup
を実行して、サーバ側の /home
ディレクトリの複製を、
クライアント側の /backup
ディレクトリにコピーする
(同時に、すでに存在しなくなったファイルを削除する) ものとしましょう。
この場合、サーバ側で実行されるコマンドを確かめるには以下のように実行します。
client$ rsync -vv -az --delete nonexistent.dom:/home /backup opening connection using ssh nonexistent.dom rsync --server --sender -vvlogDtprz . /home (サーバ側のコマンドラインが表示される) ssh: nonexistent.dom: hostname nor servname provided, or not known rsync: connection unexpectedly closed (0 bytes received so far) [receiver] rsync error: error in rsync protocol data stream (code 12) at io.c(443)
ここでは本来のオプション -az
に加えて、
詳細なデバッグ情報を出力するオプションである -vv
を指定しています。
さらにリモートホスト名として実際には存在しないホスト名 nonexistent.dom
を
指定することで意図的にエラーを発生させ、
実際のネットワーク通信はいっさい行わずに、サーバ上で実行されるであろうコマンド文字列を
「リハーサル」することができます。この例では、
下線部で示されている部分がサーバ上で実行される文字列であることがわかりました。
ただしオプション -vv
は実際にバックアップするときには
使用しないため、ここに表示されているオプション文字列「-vvlogDtprz
」から
vv
を除く必要があります。したがって、実際にサーバ側で実行されるべき
コマンド文字列は「rsync --server --sender -logDtprz . /home
」になります。
ここまできたら、あとは環境変数 SSH_ORIGINAL_COMMAND
を検査して、
この特定のコマンド文字列が与えられたときのみ rsync
を実行するようなシェルスクリプト
rsync-server.sh
を作成し、/root/.ssh/authorized_keys
の
公開鍵に command="..."
オプションの引数として指定します。
サーバ側の rsync-server.sh スクリプト |
---|
#!/bin/sh # (SSH_ORIGINAL_COMMAND の値を調べる) if [ "rsync --server --sender -logDtprz . /home" != "$SSH_ORIGINAL_COMMAND" ]; then # (不正なコマンドを実行しようとした場合はログに記録して終了) logger "Illegal command execution: $SSH_ORIGINAL_COMMAND from $SSH_CONNECTION" exit 1; fi # (rsyncコマンドを実行する) exec $SSH_ORIGINAL_COMMAND |
/root/.ssh/authorized_keys
の設定例:
[脚注: 実際にはこれはぜんぶつながった 1行です。]
command="/root/rsync-server.sh",from="client.example.com",no-pty,no-x11-forwarding,no-port-forwarding,no-agent-forwarding ssh-rsa AAAAB3NzaC1yc2E...(中略)...TYlZC91Lmw== root@server
ここでは秘密鍵が第三者に漏れたときの被害をなるべく小さくするため、
鍵の不正な利用に対してより厳しい措置をとっています。
まず from="..."
オプションを指定して、特定のクライアント
(この場合は client.example.com
) からしかこの公開鍵を使用できないように設定しています。
また、環境変数 SSH_ORIGINAL_COMMAND
に予期しない文字列が格納されていたときは、
何者かがこの鍵を不正に利用した可能性が高いと判断して、logger
コマンドを
使って syslog
にログを残します。ログには環境変数 SSH_ORIGINAL_COMMAND
の値と、
接続元ホストの IPアドレスとポート番号が格納されている環境変数 SSH_CONNECTION
の値を
記録しています。また、このような環境で利用する場合、サーバ上の
sshd_config
設定ファイルの PermitRootLogin
設定項目は、
root がログインできる環境としてはもっとも厳しい forced-commands-only
に
設定しておいたほうがよいでしょう (5.2.3. Root のログインを禁止する 参照)。
クライアント側であるバックアップデータ格納用のマシンでは、cron デーモンなどで秘密鍵ファイル id_backup
を使用することによって、定期的に (人手の介入なしで) サーバの /home
ディレクトリを
バックアップできます。
クライアント側の daily-backup.sh スクリプト |
---|
#!/bin/sh exec rsync -e 'ssh -i id_backup' -az --delete root@server.example.com:/home /backup |
OpenSSH には通常の文字ベース (端末ベース) の通信や X11 転送の機能に加えて、 クライアントとサーバ間における任意の TCP 接続を暗号化する機能があります。 この機能は TCP ポート転送 (TCP port forwarding)、 あるいはただ単にポート転送 (port forwarding) と呼ばれています。 これはクライアントまたはサーバ上で接続を待ち受けるサーバソケット (ポート) を 文字どおり相手のホストに“転送”する機能です。 ポート転送を使うとクライアントとサーバで動いているさまざまな プロセスを (盗聴やなりすましの被害を受けることなく) 互いに接続したり、 特定の用途に特化した簡易 VPN のような機能を実現できたりします。 なお、このような機能は暗号化された通信路の中にもうひとつ別の通信路を確立するため、 一般にトンネリング (tunneling) とも呼ばれています。
ポート転送は、基本的にその TCP 接続の向き
(ローカル→リモートか、あるいはリモート→ローカルか)
によって 2種類に分けられます。本節ではまずこれら
ローカル→リモート (L) と
リモート→ローカル (R) の各ポート転送の基本的な
使い方を説明し、その後ポート転送を使ったいくつかの応用例を紹介します。
[脚注: この 2つのポート転送は混乱しやすいので、本書では
ローカル→リモート (L) と リモート→ローカル (R) のように、
必ずアルファベットをつけて呼ぶことにします。
ここで、矢印 (→) は TCP 接続の行われる方向をあらわし、
L と R はそれぞれ ssh
コマンドのオプションをあらわしています。]
ローカル→リモート (L) のポート転送は、サーバ (リモート) 側で TCP 接続を待ち受けている プロセスと、クライアント (ローカル) 側で実行されるプロセスが安全に通信するための仕組みです。
sshd
が動いているサーバマシンの周辺 (あるいは、サーバマシン自身) で
なんらかの TCP 接続を待ち受けているプロセス S があるとしましょう。このデーモンと
サーバマシンとの間のネットワークは信頼できるため、通常の TCP 接続が可能なものとします。
そして、クライアントマシンの周辺 (あるいは、クライアントマシン自身) で動いている
プロセス C から、このプロセス S に接続したいとします。
しかしサーバ-クライアント間のネットワークは信頼できないため、
この 2つのプロセスが直接 TCP で通信することはできません。
OpenSSH のローカル→リモート (L) のポート転送を使うと、 プロセス C と S は以下のようにして通信できます (図 port-forwarding-l)。 [脚注: なお、ここで示されている矢印は TCP 接続のサーバ-クライアント間の関係を表したもので、 実際にはプロセス C と S の間で情報は双方向でやりとりされます。]
ssh
に対して TCP 接続をおこなう。(接続 1)
sshd
が、プロセス S に TCP 接続をおこなう。(接続 2)
ここで ssh
と sshd
はそれぞれ
プロセス S と C の「代理」として動いていることに注目してください。
ややこしいですが、クライアント側の ssh
プロセスは
ここでは C に対する (TCP 上の) 架空のサーバ S として機能し、
サーバ側の sshd
プロセスは S に対する
(TCP 上の) 架空のクライアント C として機能します。
C がクライアント側の ssh
に TCP接続を行うと、
ssh
はサーバ側の sshd
と連携して、
あたかも C が直接 S に TCP 接続をしているかのように
ふるまいます。こうして C は暗号化についてまったく意識せず、
普通に TCP 接続をおこなうだけで安全に S と通信できます。
ローカル→リモート (L) のポート転送は
ssh
コマンドに -L ポートX:ホストY:ポートZ
という
オプションを与えることで開始できます。
ポート転送はログイン時に明示的に指定する必要があります。
[脚注: 現在の OpenSSH ではログイン後にポート転送を追加することもできますが、本書では扱いません。]
X, Y, Z の値については 図 port-forwarding-l を参照してください。
ssh
がサーバにログインした時点からログアウトするまで、ssh
は
ポートX で接続を待ち受け、ここにおこなわれた接続をサーバ側の ホストY で動いている
ポートZ に中継します。なお、この ホストY はサーバが動いているホスト自身
(localhost
) でもかまいませんし、
サーバの周辺にある (信頼できるネットワーク内の) ホストでもかまいません。
また、ポート転送は個人設定ファイル ~/.ssh/config
を使うことによっても開始できます。
追加のオプション -N
を指定すると、ログイン後にシェルを起動せず、ポート転送のみを行います。
ローカル→リモート (L) のポート転送を使用する |
---|
あるいは$ ssh [-N] -L ポートX:ホストY:ポートZ [ユーザ名@]ホスト名
$ ssh [-N] [ユーザ名@]ホスト名 |
実行例:
client$ ssh -L 8000:localhost:4000 yusuke@server.example.com (ローカル→リモート (L) のポート転送を使用する) Enter passphrase for key '/home/yusuke/.ssh/id_rsa': (秘密鍵のパスフレーズを入力する) Last login: Mon Mar 20 17:34:56 2006 from xx.xx.xx.xx server$
この後クライアント上で netstat -an
を実行すると、
TCP 8000番ポートが新たに開かれているのがわかります。
client$ netstat -an | grep 8000 tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN
クライアントが自分自身の TCP 8000番ポートに接続すると、
サーバ側の TCP 4000番ポートで動いているプロセスへと中継されます。
なお、ポート転送の実行中は、
たとえサーバからログアウトしても中継している TCP 接続がすべて完了するまで
ssh
は終了しません。
オプション -N
をつけた場合 ssh
はポート転送のみをおこなうため、
端末から Control-C を入力するか、あるいは ssh
プロセスが
kill
されるまで終了しません。
実行例:
client$ ssh -N -L 8000:localhost:4000 yusuke@server.example.com (シェルを使用せずにポート転送のみをおこなう) Enter passphrase for key '/home/yusuke/.ssh/id_rsa': (終了したい場合には Control-C を入力する) ^C
リモート→ローカル (R) のポート転送は、クライアント (ローカル) 側で TCP 接続を待ち受けている
プロセスと、サーバ (リモート) 側のプロセスが安全に通信するための仕組みです。
これは TCP 接続の方向が逆であることを除けば、
6.2.1. ローカル→リモート (L) のポート転送 で説明したローカル→リモート (L) のポート転送と同じです
(図 port-forwarding-r)。サーバ側の sshd
プロセスは
C に対する (TCP 上の) 架空のサーバ S として機能し、クライアント側の
ssh
プロセスが S に対する (TCP 上の) 架空のクライアント C として機能します。
プロセス C と S は、以下のようにして通信します。
sshd
に対して TCP 接続をおこなう。(接続 1)
ssh
が、プロセス S に TCP 接続をおこなう。(接続 2)
リモート→ローカル (R) のポート転送は、
ssh
コマンドに -R ポートX:ホストY:ポートZ
という
オプションを与えることで開始できます。X, Y, Z の値については 図 port-forwarding-r を参照してください。
ssh
がサーバにログインすると、サーバ側の sshd
は
ポートX で接続を待ち受け、ここにおこなわれた接続をクライアント側の ホストY で動いている
ポートZ に中継します。ローカル→リモート (L) のポート転送と同様、
ホストY はクライアント自身 (localhost
) でもかまいませんし、
クライアントの周辺にある (信頼できるネットワーク内の) ホストでもかまいません。
ポート転送は個人設定ファイル ~/.ssh/config
を使うことによっても開始できます。
追加のオプション -N
を指定すると、ログイン後にシェルを起動せずポート転送のみを行います。
リモート→ローカル (R) のポート転送を使用する |
---|
あるいは$ ssh [-N] -R ポートX:ホストY:ポートZ [ユーザ名@]ホスト名
$ ssh [-N] [ユーザ名@]ホスト名 |
実行例:
client$ ssh -R 3000:localhost:6000 yusuke@server.example.com Enter passphrase for key '/home/yusuke/.ssh/id_rsa': (秘密鍵のパスフレーズを入力する) Last login: Mon Mar 20 17:34:56 2006 from xx.xx.xx.xx server$
この後サーバ上からサーバマシンの TCP 3000番ポートに接続すると、
クライアント側の TCP 6000番ポートで動いているプロセスに中継されます。
サーバ上で netstat -an
を実行すると、
TCP 3000番ポートが新たに開かれているのがわかります。
server$ netstat -an | grep 3000 tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN
本項ではローカル→リモート (L) のポート転送を応用して、 HTTP のみに対応した簡易 VPN を構築する方法を紹介します。
事業所 A にいるユーザが、離れた事業所 B のイントラネット web サーバ (www.intra.example.com
) に
アクセスしたいとします。このとき、事業所間のネットワークは信頼できないため、
秘密の漏曳を防ぐためになんらかの暗号化を使うものとします。
OpenSSH によるローカル→リモート (L) のポート転送を使えば、
事業所 A にあるクライアントマシンが仮想的な web サーバとして機能します。
事業所 A のユーザがこのクライアントマシンの TCP 80番ポートに接続すると、
この接続はサーバを経由して事業所 B 内の web サーバに中継されます。
こうしてユーザは離れた場所にあるイントラネットへ安全にアクセスできます (図 http-pseudo-vpn)。
この例は通常のポート転送と 2つの点で異なっています。
まず、クライアントは TCP 80番ポートを listen しなければならないので、
ssh
コマンドを root 権限で走らせる必要があります。
またクライアントはこのポートに対して、クライアントマシン以外のマシンからも接続を
受けつける必要があります。
ローカル→リモート (L) のポート転送において、OpenSSH のデフォルトの設定では、
クライアント側で接続を待ち受けているポートにはクライアントマシン自身
(localhost
) しか接続できません。これは ssh
の
listen するソケットが通常は IP アドレス 127.0.0.1
に bind されているためです。
ポート転送を開始する際に、コマンドラインから -g
オプションを指定するか、
~/.ssh/config
設定項目の GatewayPorts
に
yes
を指定すると、この IP アドレス は 0.0.0.0
となり、
どのマシンからでもこのポートに接続できるようになります。
任意のホストに対して TCP ポートを開くことは一般的に危険ですが、
ここではファイヤーウォール内の信頼されたネットワークでクライアントを運用しているため
問題ないと考えます。
転送したポートに localhost 以外からも接続を許可する (ローカル→リモート (L) のポート転送) |
---|
あるいは$ ssh -g [-N] -L ポートX:ホストY:ポートZ [ユーザ名@]ホスト名
$ ssh [-N] [ユーザ名@]ホスト名 |
実行例:
client# ssh -g -N -L 80:www.intra.example.com:80 proxy@server.example.com client# netstat -an | grep 80 (TCP 80番ポートが開いていることを確認する) tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
なお、この例で ssh
を root権限で実行する必要があるのは
クライアントが TCP 80番ポートを listen するためです。
リモートホストに root としてログインする必要はありません。
このような場合はサーバ上に proxy というポート転送専用の
ユーザアカウントをつくり、強制コマンド実行オプションを設定するのがよいでしょう。
ポート転送では -N
オプションを指定した場合、
ssh
コマンドは強制コマンドの実行が完了しても
ログアウトせずにポート転送を続けるため、command="..."
内の
文字列として /bin/true
などの意味のないコマンドを指定しておきます。
また、以下の例では公開鍵の permitopen
を使って、
サーバ側で中継できる TCP 接続を特定のホストの特定のポートのみに制限しておくことをおすすめします。
これによって、たとえこの公開鍵 (に対応する秘密鍵) が悪用されたとしても、
事業所 B 内の別のマシンや別のポート番号に TCP 接続が行われるのを防ぐことができます。
ユーザ proxy
の authorized_keys
設定例:
[脚注: 実際にはこれはぜんぶつながった 1行です。]
command="/bin/true",no-pty,no-x11-forwarding,no-agent-forwarding,permitopen="www.intra.example.com:80" ssh-rsa AAAAB3NzaC1yc2E...(中略)...TYlZC91Lmw== proxy@server
また、上の例では ssh
コマンドを直接コマンドラインから実行していますが、
秘密鍵にパスフレーズをつけなければ起動スクリプトを用いて
自動的に ssh
を実行させることも可能でしょう。
VNC は広く普及している遠隔制御用のプロトコルで、TCP/IP を経由して X Window System だけでなく Windows や Mac OS X のデスクトップを操作することも可能です。 ユーザはまず遠隔操作の対象となるコンピュータ上で「VNC サーバ」を起動しておき、 そこにリモートから「VNC クライアント」をつかって接続します。 ユーザが VNC クライアント上でマウスやキーボードを操作すると、 その情報は VNC サーバに伝えられ、VNC サーバの動いているマシンに 同じマウスやキーボード操作が反映されます (図 vnc)。
[脚注: RealVNC: http://www.realvnc.com/ (Unix, Windows用)
VNC はもともと AT&T ケンブリッジ研究所で GPL ソフトウェアとして
開発されたもので、RealVNC のほかにもいくつかの実装があります。
TightVNC: http://www.tightvnc.com/ (Unix, Windows用)
UltraVNC: http://ultravnc.sourceforge.net/ (Windows用)
OSXVNC: http://www.redstonesoftware.com/vnc.html (Mac OS X用)
通常 VNC は暗号化されていない TCP 接続を使っているため、 信頼できないネットワークを経由して使うことはできません。 [脚注: 商用の RealVNC 製品と UltraVNC は、SSH ではないものの暗号化に対応しています。] ここでは OpenSSH のポート転送を使って、VNC を安全に使う方法を紹介します。
VNC はデフォルトで TCP 5900番ポートを使用します。
あるファイヤーウォールで保護されたネットワークの中に
VNC サーバの動いているマシン desktop1.example.com
がある場合、
OpenSSH のローカル→リモート (L) のポート転送を使って
クライアント側の TCP 5900番ポートからサーバ側 desktop1.example.com
の
TCP 5900番ポートへ中継すれば安全に VNC サーバへ接続できます (図 vnc-forwarding-l)。
ポート転送が開始されたら、ユーザは vncviewer
などの VNC クライアントを使って
クライアント側の TCP 5900番ポートに接続すればよいのです。
以下の例では ssh
コマンドのポート転送に使う
-N
オプションと -L
オプションに加えて、
ログイン後に自動的に ssh
をバックグラウンドで走らせる
-f
オプションを指定しています。
ポート転送のみを使いたい場合や X11アプリケーションをバックグラウンドで走らせたい場合、
このオプションを使うと ssh
実行後にすぐさま次のコマンドを
入力することができます。
実行例:
client$ ssh -f -N -L 5900:desktop1.example.com:5900 yusuke@server.example.com & (ポート転送開始後、ssh をバックグラウンドに移行させる) Enter passphrase for key '/home/yusuke/.ssh/id_rsa': (秘密鍵のパスフレーズを入力する) (ssh がバックグラウンドに移行する) client$ vncviewer localhost:5900 &
一方、VNC には別の利用法もあります。
ローカルで動いている Windows マシンなどに異常が発生したとき、
ここで VNC サーバを走らせておき、リモート側のサポートセンターなどから
VNC クライアントで接続して操作してもらうのです。
[脚注: このアイデアは「PC説教講座」 http://pctraining.s21.xrea.com/networking/vnc-via-rev-ssh.html
に書かれているものを参考にさせていただきました。]
この場合は上の例とは逆に、ユーザはサポートセンターのサーバにログインし、
リモート→ローカル (R) のポート転送を使って
サーバ側で動いている VNC クライアントの TCP 接続をローカルな
マシン mydesktop
の TCP 5900番ポートに中継します (図 vnc-forwarding-r)。
実行例:
client$ ssh -N -R 5900:mymachine:5900 yusuke@server.example.com
サーバ側で使う VNC クライアントが sshd
の動いている
サーバマシンとは異なるマシンで実行されている場合、サーバマシンは
転送するポートに対して、自分自身以外のマシンから接続を受けつけるよう
設定を変更する必要があります。
転送したポートに localhost 以外からも接続を許可する (リモート→ローカル (R) のポート転送) |
---|
サーバ側の sshd_config 設定ファイルで以下のように指定: GatewayPorts yes |
Windows 用の SSH クライアントである PuTTY (4.6.1. Windows 上で PuTTY を使用する 参照) や Mac OS X 用の Fugu (4.6.3. Mac OS X 上で Fugu を使用する 参照) からもポート転送機能を利用できます。
PuTTY でポート転送機能を使うには、ログインの前に、 左側の Category: から「Connection - SSH - Tunnels」という項目を選びます (図 putty-forwarding.)。 最初にローカル→リモート (L) のポート転送を使う場合には、下のボタンから 「Local」を、リモート→ローカル (R) のポート転送を使う場合には 「Remote」を選択します。つぎに "Source port" の欄に ポートX の値を、 "Destination" の欄には ホストY:ポートZ の値をそれぞれ入力し、 「Add」ボタンを押すと転送するポートが 上の "Forwarded ports" 欄に追加されます。 この後サーバにログインすればポート転送が開始されます。 なお、PuTTY ではログイン中にウィンドウのタイトルバーを右クリックして設定を変更することで、 ログインしたあとでポート転送を追加することも可能です。
Mac OS X の Fugu でローカル→リモート (L) のポート転送を使うには、 メニューバーの「SSH」から「New SSH Tunnel」項目を選びます。 すると「Create SSH Tunnel」というダイアログウィンドウが現れるので、 以下の項目を入力します。
この後、「Start Tunnel」ボタンを押せばポート転送が開始されます。
TCP ポート転送 (6.2. ポート転送 参照) は、あらかじめ通信するクライアント側の プロセスとサーバ側のプロセスがわかっているときにのみしか使うことができませんでした。 また、これは TCP 接続のみに限られており、UDP パケットを転送することはできません。 クライアント側とサーバ側に複数のホストやプログラムが接続されており、 それらがお互いに多対多で通信するような場合には TCP ポート転送は使えません。 これを実現するのが IPパケットレベルの転送を実現する VPN 機能です。
OpenSSH バージョン 4.3p1 からは 標準でトンネリングデバイス (tun/tap) を扱う機能が含まれるようになりました。 この機能を使うと、TCP だけでなく UDP パケットなど IPレベルの通信を OpenSSH の暗号化を介して転送することができ、真の VPN を構築することができます。 また、tap デバイスを使うと Ethernet レベルの通信も扱うことができます。
これまで VPN は専用の機器やソフトウェアを使うことになっていましたが、 その設定は煩雑でした。OpenSSH による VPN では OpenSSH と同じ認証方式が使えるうえに、 ほぼ標準の環境で VPN を構築できます。ただしVPN の確立が双方の root 権限を必要とするため、 クライアントとサーバの両方でroot 権限をもてる人しか使えないことや、 VPN の IPアドレスとネットワークインターフェイスを サーバ/クライアントの両方であらかじめ決めておく必要があること、 また TCP 上に実装しているので、IPSec のような機構と比べると効率が悪いことなどの欠点があります。
注意 |
---|
2006年 4月現在ではこの機能はまだ実験段階のため、 安定して動作することは保証されていません。 本書では、Linux と FreeBSD でテストしましたが、 大量のデータを送受信すると不具合が生じることがありますので、 この機能に本格的に依存することはまだ現時点ではおすすめしません。 |
OpenSSH をつかった VPN は基本的にはポート転送の 仕組みと変わるところはありません。ただしポート転送が TCP 接続による ストリーム通信を暗号化していたのに対して、VPN では 各 IPパケット (あるいは Ethernet フレーム) を個別に暗号化します (図 real-vpn)。
OpenSSH の VPN 機能は、 Linux や FreeBSD に内蔵されている tun/tap デバイスを利用しています。 tun/tap デバイスはユーザ空間のプログラムが利用できる仮想ネットワークデバイスで、 この tun/tap デバイスをサーバとクライアントの両方で開き、 その間の通信を OpenSSH が暗号化すると VPN ができあがります。
tun/tap を使ったトンネリングは 2種類に分けられます。 ひとつは tun デバイスを使う方法で、これは Layer 3 (Point-to-Point) を エミュレートし、IPフレームを転送します。 これは PPP プロトコルを使ってサーバと 1対1 で通信するのと等価です。 以下は Linux 上で IPアドレス 192.168.3.1 と 192.168.3.2 の間に tun デバイスを使った VPN が作られている場合の動作例です。
Linux 上における tun デバイスを使った VPN の動作例:
# ifconfig tun0 tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 inet addr:192.168.3.1 P-t-P:192.168.3.2 Mask:255.255.255.255 UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:10 RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) # ping 192.168.3.2 PING 192.168.3.2 (192.168.3.2) from 192.168.3.1 : 56(84) bytes of data. 64 bytes from 192.168.3.2: icmp_seq=1 ttl=64 time=16.8 ms 64 bytes from 192.168.3.2: icmp_seq=2 ttl=64 time=16.1 ms 64 bytes from 192.168.3.2: icmp_seq=3 ttl=64 time=18.3 ms ...
OpenSSH の VPN 機能を使うには、バージョン 4.3p1 以上の OpenSSH と、
tun/tap デバイスの使えるカーネルをクライアントとサーバの両方で利用する必要があります。
現在ほとんどの Linux ディストリビューションでは、
tun/tap デバイスを標準でサポートしているため、
特になにも設定する必要はありません
[脚注: tun/tap デバイスについての詳細は、Linux カーネルのソースコード中にある
Documentation/networking/tuntap.txt
を参照してください。]
FreeBSD の場合は、カーネルを構築するさいに conf ファイル中で
オプション "device tun
" を有効にしておきます。
OpenSSH で VPN を使う際に注意しなければならないのは、
クライアント側の root プロセスが、サーバに ssh
で root としてログインする
必要があるということです。
これは tun/tap デバイスを扱えるのが通常は双方の root だけであるためです。
[脚注: 一般ユーザに tun/tap を許可することは、セキュリティ上問題があるとされています。]
また、OpenSSH を使った VPN ではあらかじめクライアントとサーバの間でいくつかの事柄を
決めておく必要があります:
tun0
などのインターフェイス名に含まれる数字の部分です。]
まずサーバ側の sshd_config
設定ファイル中の PermitRootLogin
設定項目で
root のログインを許可しておく必要があります (5.2.3. Root のログインを禁止する 参照)。
root ログインは危険なため、VPN を本格的に運用する際にはこの値を forced-commands-only
に設定し、
強制コマンド実行オプション (5.4.4. 特定のユーザの権限を部分的に使用させる 参照) をつけることをおすすめしますが、
とりあえずテスト時にはシェルが利用できたほうが便利なため、
without-password
にしておき公開鍵認証を使うのがよいでしょう。
また、トンネリングを許可するために PermitTunnel
設定項目を
yes
または point-to-point
に設定する必要があります。
トンネリングを許可する |
---|
sshd_config設定ファイル:PermitTunnel {yes | no | ethernet | point-to-point}
|
sshd_config ファイル設定例:
PermitTunnel point-to-point
VPN はさまざまな層がからみあった複雑なシステムです。
そのため、OpenSSH で VPN を設定する際には、まずできるだけ単純な設定でテストしたほうがよいでしょう。
本来 tun/tap デバイスを使って VPN を確立するには、
これらのインターフェイスを初期化したあと ifconfig
コマンドを実行して
IPアドレスを設定する必要があるのですが、ssh
コマンドの
役割はただ単に tun/tap を初期化して暗号化トンネルを提供するだけです。
つまり、実際のインターフェイスの設定は外部のコマンドに任されているのです。
この部分は 6.3.3. VPN を実際に運用する で自動化しますが、とりあえず最初はこれらを手動でおこなうことにして、
まずトンネルが確立できるかどうかだけをテストしてみることにします。
ssh
コマンドでVPN を開始するには、-w
オプションを使います。
また、テスト時にはデバッグ出力を表示する -v
オプションもつけておくことをおすすめします。
VPN (トンネリング) を開始する |
---|
# ssh -wクライアント側インターフェイス番号:サーバ側インターフェイス番号 root@サーバ名 |
client# ssh -v -w0:0 root@server.example.com (デバッグモードで server.example.com にログインし、トンネリングを開始する)
-w
オプションにはクライアント側のネットワークインターフェイス番号と、
サーバ側のネットワークインターフェイス番号を指定します。
デフォルトではトンネリング用のデバイスとして tun が使われます。
つまり、-w0:0
はクライアント・サーバともにインターフェイス番号 0 の tun デバイスを
使ってトンネリングを確立するということを意味しています。
うまくいけば次のようなメッセージ (Linux の場合) が出て、通常のシェルが開始されます。
うまくトンネリングができている場合は、サーバとクライアントの両方で
tun デバイスが初期化されているはずです。
これは ifconfig tun0
(あるいは ifconfig -a
) を実行してみればわかります:
実行例 (Linux の場合):
... debug1: Authentication succeeded (publickey). debug1: channel 0: new [client-session] debug1: Entering interactive session. debug1: Requesting tun. debug1: sys_tun_open: tun0 mode 1 fd 7 debug1: channel 1: new [tun] Last login: Mon Feb 13 10:30:42 2006 from xxx.xxx.xxx.xxx server# ifconfig tun0 (tun0 インターフェイスが起動しているかどうか確認する) tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:10 RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
実行例 (FreeBSD の場合):
# ifconfig tun0 (tun0 インターフェイスが起動しているかどうか確認する) tun0: flags=8011<UP,POINTOPOINT,MULTICAST> mtu 1500 Opened by PID 24015
このように表示されている場合はトンネリングが確立しています。
トンネリングが確立していない状態で ifconfig tun0
を実行すると、
Linux ではインターフェイス tun0 そのものが存在せずエラーとなり、
FreeBSD では "Opened by PID XXXX
" の部分が表示されません。
ただし、この状態ではサーバとクライアントの tun デバイス間にトンネリングが確立されただけで、 まだ VPN としての設定は完了していません。これらのデバイスが通信を行うためには、 サーバとクライアント両方の tun インターフェイスに IP アドレスを設定する必要があります。 ここでは別の端末を使って、クライアントとサーバ上の両方で tun デバイスを設定します。
tun デバイスは IP 層をエミュレートする (つまり、IP パケットを直接送受信する) ので、 Ethernet のインターフェイスとは違って、 相手方の IP アドレスも指定してやる必要があります。
tun デバイスに IP アドレスを設定する |
---|
(Linux の場合) # ifconfig tun0 自分のIPアドレス pointopoint 相手のIPアドレス (point to pointではないので注意) (FreeBSD の場合) # ifconfig tun0 自分のIPアドレス 相手のIPアドレス |
これをサーバとクライアントの両方で実行します。 たとえばサーバ側の仮想 IP アドレスを 192.168.3.1、 クライアント側の仮想 IP アドレスを 192.168.3.2 に決めたとすると、 以下のようになります:
(サーバ側 - Linux の場合) server# ifconfig tun0 192.168.3.1 pointopoint 192.168.3.2 (クライアント側 - Linux の場合) client# ifconfig tun0 192.168.3.2 pointopoint 192.168.3.1
これが成功したら、いまや IP レベルでの 通信ができるようになっているはずですので、 お互いに ping を送って確かめてみましょう。
(サーバ側) server# ping 192.168.3.2 (サーバからクライアントに ping) (クライアント側) client# ping 192.168.3.1 (クライアントからサーバに ping)
この段階ではまだクライアントとサーバ双方が通信できているだけで、
その他のマシンは VPN に参加していないことに注意してください。
ローカルネットワーク内にある他のマシンを VPN に参加させるには iptables
(Linux) や
pf
(FreeBSD) などのコマンドを用いて正しいルーティングを設定する必要があります。
VPN セッションはユーザがログアウトしたあとも続きます。
先ほどのデバッグモードで root のシェルから exit
すると、
ssh はシェルから抜けたあとも依然として VPN のセッションを続けます:
server# exit logout debug1: client_input_channel_req: channel 0 rtype exit-status reply 0 debug1: channel 0: free: client-session, nchannels 2 (ここで Control-C を入力) debug1: channel 1: free: tun, nchannels 1 Killed by signal 2.
現在のところ、 VPN セッションを終了するには端末上で
Control-C を押すか、 ssh
プロセスを kill
する以外に方法はないようです。
OpenSSH で VPN を実現するもうひとつの方法は tap デバイスを使って、 Ethernet レベルのトンネリングを確立することです。 tap デバイスと tun デバイスは互いによく似ていますが、tun デバイスが Layer 3 (Point-to-Point) をエミュレートして IPパケットを直接生成、転送するのに対し、 tap デバイスは Layer 2 (Ethernet) をエミュレートし、 Ethernet フレームを転送します。 以下は Linux 上において IPアドレス 192.168.3.1 と 192.168.3.2 との間に tap デバイスを使った VPN が作られている場合の動作例です。
Linux 上で tap デバイスを使った VPN の動作例:
# ifconfig tap0 tap0 Link encap:Ethernet HWaddr 00:FF:7F:02:53:D7 inet addr:192.168.3.1 Bcast:192.168.3.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) # ping 192.168.3.2 PING 192.168.3.2 (192.168.3.2) from 192.168.3.1 : 56(84) bytes of data. 64 bytes from 192.168.3.2: icmp_seq=1 ttl=64 time=37.6 ms 64 bytes from 192.168.3.2: icmp_seq=2 ttl=64 time=16.1 ms 64 bytes from 192.168.3.2: icmp_seq=3 ttl=64 time=16.6 ms ...
トンネリングのさいに tun を使うか tap を使うかは、OpenSSH の
サーバとクライアント両方の設定に依存しています。サーバは許可するトンネリングの種類を
sshd_config
設定ファイルの PermitTunnel
項目で指定し、
クライアントは要求するトンネリングの種類を
/root/.ssh/config
個人設定ファイル (あるいは ssh
のコマンドラインオプション) の
Tunnel
項目で指定します。
これらの項目とトンネリングの種類との関係を、表 tunneling-type にまとめます。
PermitTunnel (サーバ) と Tunnel (クライアント) の値
|
効果 |
---|---|
両方が yes
| PPP (tun) が使われる |
どちらか一方が yes (または point-to-point ) で、
もう一方が point-to-point |
PPP (tun) が使われる |
どちらか一方が yes (または ethernet ) で、
もう一方が ethernet |
Ethernet (tap) が使われる |
どちらか一方が point-to-point で、
もう一方が ethernet |
トンネリングは拒否される |
どちらか一方が no |
トンネリングは拒否される |
実行例:
client# ssh -v -oTunnel=ethernet -w0:0 server Enter passphrase for key '/root/.ssh/id_rsa': (秘密鍵のパスフレーズを入力する) Last login: Mon Feb 13 08:22:39 2006 from xxx.xxx.xxx.xxx server#
ここで -w
オプションに与えている
数値 0
は今度は tun ではなく tap のインターフェイス番号です。
無事サーバにログインできたら、ifconfig tap0
してみてください:
(Linux の場合) # ifconfig tap0 tap0 Link encap:Ethernet HWaddr 00:FF:4A:21:8F:3D BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) (FreeBSD の場合) # ifconfig tap0 tap0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 ether 00:bd:73:36:60:c8 Opened by PID 93688
デバイスができているのを確認したら、IP アドレスを設定します。 tap デバイスの場合は Ethernet をエミュレートするので、 通常のネットワークカードにアドレスを指定するのと同じ方法が使えます。
tapデバイスに IP アドレスを設定する:
(サーバ側) server# ifconfig tap0 192.168.3.1 netmask 255.255.255.0 (クライアント側) client# ifconfig tap0 192.168.3.2 netmask 255.255.255.0
さて、VPN を本格的に使うとなると、いちいちサーバとクライアントの両方で
毎回 ifconfig
を実行していては非効率的です。
また、リモートからの root のシェル使用も禁止したいところです。
強制コマンド実行オプション (5.4.4. 特定のユーザの権限を部分的に使用させる 参照) と
個人設定ファイル (4.7. 個人用の設定ファイルでさらに快適に 参照) を使えば、
これらの作業を安全に自動化できます。
[脚注: といっても、OpenSSH の VPN 機能は現時点では完璧ではありません。
現在の実装では、IP アドレスとネットワークインターフェイス番号を、
サーバとクライアントの両方で前もって取り決めておかないといけません。そのため、
2台以上のクライアントが同時に VPN を使う場合は少々注意が必要です。]
/root/.ssh/config
を書く最初に、クライアント側の設定を自動化してみましょう。 root のホームディレクトリに、以下のような設定ファイルを置きます:
クライアント側の /root/.ssh/config
個人設定ファイル例 (Linux の場合):
Host server-vpn Hostname server.example.com (サーバホスト名) Tunnel point-to-point (tun デバイスを使ったトンネリングを指定する) TunnelDevice 0:0 (トンネリングに使うネットワークインターフェイス番号) PermitLocalCommand yes (サーバにログインしたあと、クライアントで以下のコマンドを実行する) LocalCommand ( sleep 3; ifconfig tun0 192.168.3.2 pointopoint 192.168.3.1 ) &
最初の設定項目 Hostname
には、サーバのホスト名を指定します (4.7. 個人用の設定ファイルでさらに快適に 参照)。
これ以外の 4つの設定項目
(Tunnel
, TunnelDevice
, PermitLocalCommand
, LocalCommand
) が
VPN を使用するためのオプションです。これらのうち Tunnel
については前項で説明しました。
また、TunnelDevice
は、コマンドラインの -w
オプションと同じ働きをします。
新しく登場する2つの設定項目 PermitLocalCommand
と LocalCommand
を使うと、
サーバへのログインが成功したあとで、ローカルのクライアント上で
自動的に特定のコマンドを実行させることができます。ここでは、この機能を使って
クライアント側の ifconfig
コマンドを実行させ、IP アドレスを設定します。
なお、実際には RedHat 系の Linux であれば ifconfig
を直接実行するよりも、
/etc/sysconfig/network-scripts/ifcfg-tun0
などの設定ファイルを作っておき、
直接 ifconfig
を実行するかわりに /sbin/ifup tun0
とするほうが
望ましいでしょう。
Linux 上で LocalCommand 実行の際に IP アドレスを設定するさいの注意 |
---|
上の例で、設定項目 LocalCommand の値に注目してください。
ifconfig コマンドの前に sleep が入っていますが、
これは ssh コマンドの現バージョン (4.3p2) における
仕様に対する応急処置です。現在の ssh は、LocalCommand の実行が完了したあとに
tun/tap デバイスを開きます。ところが Linux では、tun/tap デバイスが使われていない
(どのプロセスも open していない) 状態では、ifconfig コマンドで
IPアドレスを設定することはできません。
LocalCommand のコマンドを
実行する時点ではまだ tun/tap は開かれていないため、sleep コマンドを使わないと
以下のようなエラーが表示されます:
SIOCSIFDSTADDR: No such device tun0: unknown interface: No such device
さいわい
いっぽう FreeBSD の場合は、tun を開く前でも と書いておけば成功します。LocalCommand ifconfig tun0 192.168.3.2 192.168.3.1 |
うまくいけば、以下のように入力するだけで (クライアント側の) VPN は準備完了になるはずです:
client# ssh server-vpn Enter passphrase for key '/root/.ssh/id_rsa': (秘密鍵のパスフレーズを入力する) Last login: Mon Feb 13 10:30:42 2006 from xxx.xxx.xxx.xxx server#
authorized_keys
ファイルを変更する
いっぽうサーバ側の IPアドレスの設定は、
authorized_keys
ファイルの強制コマンド実行オプション
command="..."
を使うことで自動化できます。
一般的に、強制コマンド実行では指定されたコマンドが終了すると自動的にログアウトしてしまいますが、
トンネリングを使っている場合は、コマンドが終了したあともサーバは実行を続けます。
なお、sshd
の場合はこのコマンドを実行する前に tun/tap デバイスを開くので、
クライアント側の解説で紹介した「sleep
トリック」は不要です。
また、公開鍵の tunnel="..."
オプションを使えば、
サーバ側が割り当てる tun または tap のインターフェイス番号も強制的に指定することができます:
サーバ側の /root/.ssh/authorized_keys の設定例 (Linux の場合): [脚注: 実際にはこれはぜんぶつながった 1行です。]
tunnel="0",command="ifconfig tun0 192.168.3.1 pointopoint 192.168.3.2",no-pty,no-x11-forwarding,no-port-forwarding,no-agent-forwarding ssh-rsa AAAAB3NzaC1yc2E...(中略)...TYlZC91Lmw== root@server
以上の例のように設定すると、クライアントからこの公開鍵を使ってログインしたときは必ず
サーバ側の tun0 デバイスが開かれ ifconfig
が自動的に実行される
(しかも、対話的シェルは実行されない) ことになります。
現在ではセキュリティ上の理由などから、プライベートネットワークを利用している組織も多いでしょう。 この場合、外部からアクセスできる IP アドレスは限られた数しか存在しないため、 プライベートネットワーク内に複数のサーバがある場合は、それらのサーバに対して外部から個別の IP アドレスでアクセスすることはできません。 本節ではこのような状況でプライベートネットワーク内の異なるサーバにログインする方法を紹介します。
複数の異なるサーバにログインするひとつの方法は、ログインを 2回に分けておこなうことです。
これはゲートウェイとなるホストで sshd
サーバデーモンが走っている場合に有効です (図 ssh-over-ssh)。
ユーザは最初に ssh
でサーバ gateway
にログインしてから、
ふたたびそのサーバ上で ssh
を実行して server1
にログインします。
ssh
のリモートコマンド機能を使えば、クライアント上から直接
(1回のコマンド入力で) server1
にログインできます。
この場合、ssh
コマンドの -t
オプションを使って、
最初のサーバ (gateway) にログインし、
強制的に仮想端末を割り当てる必要があります。
一般的に ssh
は、リモートコマンドを実行する場合にはサーバ側で
仮想端末を割り当てません。しかし、ここではさらに他のマシンに対して
ssh
を実行する必要があるため、仮想端末が必要になります。
[脚注: ユーザがリモートのサーバ上で対話的にシェルを使うときには、仮想端末は必ず必要です。]
リモートコマンド実行時に強制的に仮想端末を割り当てる |
---|
$ ssh -t [ユーザ名@]ホスト名 コマンド 引数1 引数2 ... |
実行例:
client$ ssh -t yusuke@gateway.example.com ssh yusuke@server1.intra.example.com (多段ログイン) Enter passphrase for key '/home/yusuke/.ssh/id_rsa': (gateway.example.com の認証) Enter passphrase for key '/home/yusuke/.ssh/id_rsa': (server1.intra.example.com の認証) Last login: Mon Mar 29 02:45:09 2006 from xx.xx.xx.xx server1$
上の例では、ユーザは 2回パスフレーズを入力していますが、実際にはここで使用している
秘密鍵ファイルは異なるものであることに注意してください。
最初のパスフレーズ入力 (gateway.example.com の認証) では、
ユーザはクライアント上にある秘密鍵を使って認証します。
ところが 2度目のパスフレーズ入力 (server1.intra.example.com の認証) は
実際には gateway
上で行われているので、
ユーザは server1
のログインに使う秘密鍵を
ゲートウェイ上に置く必要があります。
しかしこの方法ではユーザはパスフレーズを 2度入力しなければならないうえ、
異なる秘密鍵・秘密鍵ペアを 2つ使用する必要があり面倒です。
[脚注: gateway
上にクライアントと同じ秘密鍵を置くこともできますが、
これはクライアントから秘密鍵を持ち出すことになるため、おすすめできません。]
したがって、このような場合は認証エージェントを使うことをおすすめします
(4.4. 認証エージェントを使う 参照)。
認証エージェントを使った場合:
client$ ssh-agent bash (認証エージェントを起動する) client$ ssh-add -t 3600 (認証エージェントに秘密鍵を追加する) Enter passphrase for key '/home/yusuke/.ssh/id_rsa': Identity added: /home3/yusuke/.ssh/id_rsa (/home3/yusuke/.ssh/id_rsa) Lifetime set to 3600 seconds client$ ssh -A -t yusuke@gateway.example.com ssh yusuke@server1.intra.example.com (エージェント転送を許可して多段ログイン) Last login: Mon Mar 29 03:00:55 2006 from xx.xx.xx.xx server1$
この例では認証エージェントの転送を許可しているため、
ゲートウェイ上からもクライアント上の秘密鍵が使われます。
この場合は gateway
に秘密鍵ファイルを置く必要はなく、
gateway
と server1
の authorized_keys
ファイルには
それぞれ同じ公開鍵を登録しておけばよいことになります。
ゲートウェイにログインしたユーザを自動的に別のサーバに転送したい場合は、
ゲートウェイ上のユーザの authorized_keys
ファイルに
強制コマンド実行オプションをつけることもできます。
authorized_keys 設定例: [脚注: 実際にはこれはぜんぶつながった 1行です。]
command="ssh yusuke@server1.intra.example.com",no-x11-forwarding,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2E...(中略)...TYlZC91Lmw== yusuke@client
なお、これら複数のサーバがすべて一元的に管理されている場合は、ユーザが gateway
に
ログインしたあとに、Hostbased 認証を使うこともできます (6.6. Hostbased 認証 を使う 参照)。
この場合、ユーザは gateway
へのログインに成功した時点ですでに信頼されているとみなされ、
server1
からパスワード認証や公開鍵認証を要求されることはありません。
外部から複数のサーバにログインするもうひとつの方法は、ゲートウェイをルータとして使い、
異なるポート番号におこなわれた TCP 接続を異なるホストに中継するよう設定しておくことです。
[脚注: 最近ではこのような機能をもった家庭用ルータもあります。]
たとえばユーザが、 gateway.example.com
の
TCP 10000番ポートに接続すると server1
に、
TCP 20000番ポートに接続すると server2
にログインできるように設定するのです。
(図 sshd-different-port)。
しかしこの方法はこのままで使うと、ホスト認証で問題をひきおこします。
client$ ssh -p10000 gateway.example.com (server1 にログインする) ... server1$ exit (ログアウトする) logout client$ ssh -p20000 gateway.example.com (server2 にログインする) @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ (ホスト鍵が変わっているという警告) @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ...
これは server1 と server2 がそれぞれ別々のホスト鍵をもっているのに対して、
ssh
は known_hosts
ファイルに、
どちらも gateway.example.com という名前でホスト公開鍵を登録してしまうためです。
この問題は ~/.ssh/config
個人設定ファイルを用いて解決します。具体的には、HostKeyAlias
設定項目を使って
ログインするときに known_hosts
中のホスト公開鍵をそれぞれ
server1.intra.example.com と server2.intra.example.com
という架空のホスト名に対応づけるのです。
ホスト公開鍵を架空のホスト名に関連づける (IP アドレスの関連はチェックしない) |
---|
あるいは$ ssh -oHostKeyAlias=架空のホスト名 -oCheckHostIP=no [ユーザ@]実際のホスト名 HostKeyAlias 架空のホスト名 CheckHostIP no |
実行例:
client$ ssh -oHostKeyAlias=server1.intra.example.com -oCheckHostIP=no -p10000 gateway.example.com ...
.ssh/config
設定ファイルの例:
Host server1 HostName gateway.example.com Port 10000 HostKeyAlias server1.intra.example.com CheckHostIP no Host server2 HostName gateway.example.com Port 20000 HostKeyAlias server2.intra.example.com CheckHostIP no
実行例:
client$ ssh server1 ... server1$ exit logout client$ ssh server2 server2$
.ssh/~config
で指定している CheckHostIP no
は
ホスト公開鍵と IP アドレスとの関連をチェックしないよう指示するものです。
ssh
クライアントは、通常ユーザが指定したホスト名に加え、
その IP アドレスについてもホスト公開鍵との対応をチェックしています。
そのため、known_hosts
ファイルにホスト公開鍵を登録するときに、
同一の IP アドレスに対して異なるホスト公開鍵を登録してしまうと警告が表示されて
しまうのです。
CheckHostIP no
を指定しない場合:
client$ ssh server1 ... server1$ exit logout client$ ssh server2 (server1.intra.example.com の IPアドレスが登録されているものと一致しないという警告) Warning: the RSA host key for 'server1.intra.example.com' differs from the key for the IP address 'xx.xx.xx.xx' Offending key for IP in /home/yusuke/.ssh/known_hosts:52 Matching host key in /home/yusuke/.ssh/known_hosts:48 Are you sure you want to continue connecting (yes/no)?
本節では制限された環境で sshd
サーバデーモンを走らせる方法を紹介します。
一般的には特定のユーザ権限や chroot
環境下でデーモンを起動することは
セキュリティの向上につながるとされていますが、 OpenSSH はリモートから
ログインしたユーザがマシン全体を制御できるように設計されているため、
もともと制限された環境で走らせることをあまり想定していません。
使い勝手を下げずに、sshd
サーバデーモンの環境を
制限するには複雑な設定が必要になります。
OpenSSH の sshd
サーバデーモンは通常 root 権限で走らせることを前提としていますが、
一般ユーザ権限で走らせることもできます。
この場合はデーモンを走らせているユーザのみがログインできます。
しかし一般ユーザ権限の sshd
には以下のような欠点があります。
sshd
サーバデーモンを一般ユーザ権限で走らせるには、以下のような手順で設定します。
sshd_config
設定ファイルを作成する。
ssh-keygen
コマンドでホスト秘密鍵とホスト公開鍵のペアを作成する。
この際、パスフレーズはつけない。
sshd_config
設定ファイル中の Port
設定項目と HostKey
設定項目を
変更し、Port
には 1024番以上のポートを、HostKey
には
2. で作成したホスト秘密鍵のパス名を指定する。
sshd
を起動する。このとき、-f
オプションで
sshd_config
設定ファイルのパス名を指定する。
なお、sshd
の実行には絶対パス名を指定する必要があります。
実行例:
server$ mkdir myssh (sshd 用のディレクトリを作成する) server$ cd myssh server$ vi sshd_config (sshd_config 設定ファイルを作成する) server$ ssh-keygen -P '' -f ssh_host_rsa_key (パスフレーズなしでホスト鍵を生成する) Generating public/private rsa key pair. Your identification has been saved in ssh_host_rsa_key. Your public key has been saved in ssh_host_rsa_key.pub. The key fingerprint is: 22:96:cb:91:38:a4:88:78:54:7f:24:e8:14:64:02:fe yusuke@server server$ /usr/sbin/sshd -f sshd_config (設定ファイルを指定して sshd を起動する)
chroot は Unix のセキュリティ機能のひとつで、あるプロセス以下のすべての
子プロセスからのアクセスを、ファイルシステム中の一部のディレクトリ (jail) 内に制限します。
ここでは sshd
サーバデーモンを chroot 環境下で走らせる方法を簡単に紹介します。
chroot 環境で動かすプロセスは、万が一 root 権限が乗っ取られた場合でも
侵害の影響範囲をある程度制限できるため、一般にセキュリティを大きく改善させると考えられています。しかし、
その設定方法は非常に煩雑なため、つねにおすすめできるわけではありません。
[脚注: また chroot も完璧ではありません。chroot 環境下でも root 権限と
適切なプログラミング環境があれば、chroot したディレクトリ以外のファイルにアクセスできることが知られています。
http://www.bpfh.net/simes/computing/chroot-break.html ]
また、当然ながら chroot 環境下で走っている sshd
デーモンで
一般的なサーバ管理を行うこともできません。chroot 環境下で sshd
サーバデーモンを
利用するのは、OpenSSH を使用する目的が非常に限られているときのみに有効です。
chroot 環境の構築方法はオペレーティングシステムによって大きく差があるため、
ここではごく簡単な代表例を示すのみにとどめます。
以下の例は Red Hat で、ユーザがログインできる chroot 環境下において
sshd
サーバデーモンを動かす最低限の手順を示したものです。
[脚注: ログインのあと、ユーザのホームディレクトリに chroot するパッチもあります。 http://chrootssh.sourceforge.net/ ]
もっとも単純な chroot 環境を設定する (Red Hat):
# mkdir /chroot (chroot用のディレクトリを作成する) # cd /chroot # mkdir bin dev etc home lib sbin var var/empty var/empty/sshd (必要なディレクトリを作成する) # cp /etc/passwd etc/ (/etc/passwd ファイルをコピーする) # cp /bin/sh bin/ (シェルをコピーする) # ldd /bin/sh (シェルに必要なライブラリをコピーする) libtermcap.so.2 => /lib/libtermcap.so.2 (0x40020000) libdl.so.2 => /lib/libdl.so.2 (0x40025000) libc.so.6 => /lib/i686/libc.so.6 (0x42000000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) # cp /lib/lib{termcap,dl,c}.so* lib/ # cp /lib/ld-linux.so.2 /lib/libnss*.so* lib/ # cp /usr/sbin/sshd sbin/ (sshd をコピーする) # ldd /usr/sbin/sshd (sshd に必要なライブラリをコピーする) libpam.so.0 => /lib/libpam.so.0 (0x40020000) libdl.so.2 => /lib/libdl.so.2 (0x40029000) libresolv.so.2 => /lib/libresolv.so.2 (0x4002c000) libcrypto.so.2 => /lib/libcrypto.so.2 (0x4003d000) libutil.so.1 => /lib/libutil.so.1 (0x40101000) libnsl.so.1 => /lib/libnsl.so.1 (0x40104000) libcrypt.so.1 => /lib/libcrypt.so.1 (0x40119000) libc.so.6 => /lib/i686/libc.so.6 (0x42000000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) # cp /lib/lib{pam,resolv,crypto,util,nsl,crypt}.so* lib/ # mknod dev/null c 1 3 (必要なデバイスノードを作成する) # mknod dev/zero c 1 5 # mknod dev/urandom c 1 9 # /usr/sbin/chroot . /sbin/sshd -d (chroot 環境で sshd を走らせる) debug1: sshd version OpenSSH_4.3p2 debug1: read PEM private key done: type RSA debug1: private host key: #0 type 1 RSA debug1: read PEM private key done: type DSA debug1: private host key: #1 type 2 DSA debug1: rexec_argv[0]='/sbin/sshd' debug1: rexec_argv[1]='-d' ...
まず chroot 環境に必要なディレクトリやファイルをコピーして
必要なデバイスノードを作成し、その後テストを行います。
次に sshd
が実行時に必要とする動的リンクライブラリ (.so
ファイル) を特定するために
ldd
コマンドを使っています。
なお、sshd
が走るためには、/dev/null
, /dev/zero
および
/dev/urandom
の 3つのファイルが必要ですので注意してください。
また、この例のように設定すると sshd
サーバデーモンのログを
syslog 経由で記録できなくなるため、実際には syslogd
デーモンのオプションを
変更して chroot 環境下に /dev/log
ソケットを作成させる必要があります。
なお FreeBSD では、chroot 環境を構築する手助けをおこなう jailtools
[脚注: http://www.the-labs.com/FreeBSD/JailTools/ ] などのツールが用意されています。
Red Hat Linux にも同様のツール
[脚注: http://www.jmcresearch.com/projects/jail/ ]
が存在します。
OpenSSH でサポートされている Hostbased 認証は、 クライアントマシンとサーバマシンどうしがユーザを介さずに直接認証をおこなうことによって、 ユーザレベルの認証を省略する認証方式です。 本書ではほとんどの場合、ユーザの使うクライアントは システム管理者によって管理されているわけではないという状況を想定してきました。 しかし、もしシステム管理者がクライアントも含む複数のマシンを一元的に管理できているなら、 Hostbased 認証を使うことで、ユーザがパスワードやパスフレーズの入力なしに それらのマシンのあいだを自由にログインできるようになります。
Hostbased 認証の長所:
Hostbased 認証の短所:
公開鍵認証では、クライアント上のユーザが サーバに対して、そのユーザ自身の秘密鍵やパスフレーズを使って、 そのユーザの身分をサーバに対し証明しました (3.3.2. 秘密鍵・公開鍵ペアを使ったユーザ認証 参照)。 いっぽう Hostbased 認証では、クライアントマシンとサーバマシンどうしが直接認証します。 通常の公開鍵認証では、クライアント側は最初のホスト認証によりサーバが 本物であることを確認していましたが、サーバ側はそのユーザの秘密鍵については信頼しても、 クライアントマシン全体についてはなにも知らされていません。 しかし、もしサーバがクライアントマシン全体を信用できれば、 そこに正しくログインできているユーザは信用してよいということになります。 そのため、クライアントとサーバの認証がうまくいけば、 ユーザ自身の秘密鍵やパスワードを使う必要はなくなります。
Hostbased 認証では、最初にクライアント側がサーバの正当性を確かめる ホスト認証を実施し、次にサーバ側がクライアントの正当性を確かめます。 これはクライアントがサーバ側に登録されたクライアントのホスト公開鍵に対応する ホスト秘密鍵の所有をサーバに対して証明することによっておこないます。
Hostbased 認証では、認証するのはマシン同士であってユーザではありません。そのため認証に
必要な各種ファイルはユーザのホームディレクトリではなく、
クライアントマシンの OpenSSH 設定ファイル用ディレクトリ
(/etc/ssh
など) に置きます。
まず、ホスト認証のためにクライアント側にホスト秘密鍵が必要です。
これは OpenSSH をインストールしたときに、ホスト公開鍵と一緒に自動的に
生成されているはずです。
また Hostbased 認証ではサーバ側に ssh_known_hosts
ファイルと、
shosts.equiv
ファイルが必要です。ssh_known_hosts
ファイルには、
クライアントのホスト公開鍵を登録します。いっぽう shosts.equiv
ファイルには、
どのクライアントからの、どのユーザにログインを許可するかを記します。
クライアント側の設定ファイル用ディレクトリ | サーバ側の設定ファイル用ディレクトリ |
---|---|
クライアント認証に使うホスト秘密鍵ファイルが必要。
|
どのクライアントのどのユーザにログインを許可するかを登録したファイルが必要。
|
サーバ側の ssh_known_hosts
ファイルは
ユーザの ~/.ssh/known_hosts ファイルと似ていますが、
ここではホスト名のかわりに IP アドレスを登録します。
たとえばクライアント側の IP アドレスがclient$ cat /etc/ssh/ssh_known_hosts (ssh_known_hosts ファイルの内容を確認する) 11.22.33.44 ssh-rsa AAAAAsuDBHzaC...(中略)...xtjuDRmSp3XqcT= (11.22.33.44 のホスト公開鍵が登録されている) ...
55.66.77.88
であり、
そのホスト公開鍵ファイルが ssh_host_rsa_key.pub.client
だとすると、
ホスト公開鍵は以下のようにして登録できます。
ホスト公開鍵を登録する:
サーバ側のserver# ( echo -n '55.66.77.88 '; cat ssh_host_rsa_key.pub.client ) >> /etc/ssh/ssh_known_hosts
shosts.equiv
は
どのクライアントから、どのユーザのログインを許可するかを記述するファイルですが、
その文法は .rhosts
ファイルや .shosts
ファイルと同じです。
shosts.equiv ファイルの文法 |
---|
[+-]IPアドレス [[+-]ユーザ名] |
設定例:
11.22.33.44 # (11.22.33.44 からログインするユーザはすべて許可する) 55.66.77.88 yusuke # (55.66.77.88 からのユーザ yusuke のログインを許可する)
また、クライアント側とサーバ側の設定ファイルをどちらも書き換える必要があります。
Hostbased 認証では、クライアント側がサーバに自分の権限を証明するために
クライアントマシン上のホスト秘密鍵を使います。しかしクライアントマシンの
ホスト秘密鍵はそのマシンの正当性を示すために使われるため、
root権限がなければアクセスできません。
OpenSSH では、一般ユーザ権限で実行される ssh
コマンドは、
ssh-keysign
というroot権限で走る
補助プログラムを呼び出すことによってホスト秘密鍵を使用します。
[脚注: 以前のバージョンの OpenSSH では、ssh
コマンドに
setuid root ビットを指定することでホスト秘密鍵にアクセスしていました。]
デフォルトではクライアントとサーバともに Hostbased 認証の使用は禁止されています。
Hostbased 認証を行うためには、クライアント側では root 権限で
ssh_config
設定ファイルを書き換えて、Hostbased 認証と
ssh-keysign
の使用を許可しておくことが必要です。
Hostbased 認証を許可する (クライアント側) |
---|
クライアント側の ssh_config 設定ファイル:
EnableSSHKeysign yes HostbasedAuthentication yes |
同様に、サーバ側でも sshd_config
設定ファイルの変更が必要です。
ここでは管理者があらかじめ設定した shosts.equiv
ファイルと
ssh_known_hosts
ファイルだけを参照するよう、
IgnoreRhosts
設定項目と
IgnoreUserKnownHosts
設定項目に yes
を設定します。
また、IP アドレスを使ってすべてのクライアントの認証をおこなえるようにするため、
UseDNS
設定項目に no
を指定しています。
[脚注: UseDNS
設定項目に yes
を指定した場合、
shosts.equiv
ファイルには IP アドレスではなく
ホスト名を書く必要があります。しかしこの場合は DNS サーバの逆引きを
正しく設定する必要があるため、一般に手続きはより煩雑になります。]
Hostbased 認証を許可する (サーバ側) |
---|
サーバ側の sshd_config 設定ファイル:
HostbasedAuthentication yes IgnoreRhosts yes IgnoreUserKnownHosts yes UseDNS no |
注意 |
---|
IgnoreRhosts に no を指定すると、
sshd サーバデーモンは Hostbased認証の許可確認時に
shosts.equiv だけでなく、ログインしようとしているユーザの
ホームディレクトリ上にある ~/.shosts ファイルも使用します。
また、IgnoreUserKnownHosts に no を指定すると、
sshd サーバデーモンは信頼できるクライアントのホスト鍵として
ssh_known_hosts だけでなく、ログインしようとしているユーザの
ホームディレクトリ上にある ~/.ssh/known_hosts ファイルも使用します。
どちらのオプションも予期しないユーザをログインさせてしまう危険性があります。
安全性を考えると、これらのオプションは yes にしておくことをおすすめします。
|
前項で説明した設定が正しく行われていれば、ユーザはパスワードやパスフレーズの 入力なしでサーバにログインできるはずです。
client$ ssh yusuke@server.example.com (Hostbased 認証でログインする) Last login: Fri Apr 7 00:56:17 2006 from xx.xx.xx.xx server$
注意 |
---|
ユーザが公開鍵認証と Hostbased 認証のどちらでもログインできる場合、
サーバは公開鍵認証のほうを優先して使用します。
公会議認証を利用する場合、ユーザは通常パスフレーズを尋ねられます。ところが、
認証エージェントを使っている状態では、ユーザはそれと気づかずに
公開鍵認証でログインしてしまう可能性が生じます。したがって、
Hostbased 認証がうまくいくかどうかをテストする場合は
まちがって公開鍵認証でログインしてしまわないよう注意してください。
このような勘違いを防ぐため、Hostbased 認証のテスト時には
ssh -oPubkeyAuthentication=no などのオプションを与えて
一時的に公開鍵認証を禁止するとよいでしょう。
|
本書では主に比較的新しいバージョンの OpenSSH について、その使い方を説明しています。ところが、SSH の通信方式には現在広く用いられている SSH2 プロトコルとは別に、 SSH1 プロトコルと呼ばれている方式が存在します。 OpenSSH では SSH1 プロトコルと SSH2 プロトコルのどちらも使用でき、 これらはユーザから見た分には機能的にほとんど差がありません。しかし、 内部的にはこの 2つのプロトコルはまったく互換性がないのです。 また、SSH1 プロトコルは SSH2 プロトコルと比較してセキュリティが弱く、SSH2 プロトコルが 普及してきた現在では利用をおすすめしません。 [脚注: SSH1 は暗号化に DH 鍵交換を使っていません。 また使用できる暗号化アルゴリズムも限られており、 なりすましの検出機構にも弱いものを使っています。] しかしサーバやクライアントが古く、SSH1 プロトコルしかサポートしていない場合は、 たとえ自分が最新バージョンの OpenSSH を使っていても、SSH1 プロトコルでログインせざるを得なくなります。 OpenSSH プロジェクトの統計によれば、このような古いサーバはまだ世界に 6% ほど存在しています。 [脚注: http://www.openssh.com/usage/ja/ssh-stats.html ] 本項では現在の OpenSSH で SSH1 プロトコルを使用する方法を紹介します。
OpenSSH では、SSH1 プロトコルを使っていても 本書で説明したほとんどの機能が SSH2 プロトコルと同様に使えます。 ただし SSH1 プロトコルでは秘密鍵ファイルと公開鍵ファイルの形式が SSH2 プロトコルとは異なります。そこで本項では、これらの鍵ファイルの扱い方にしぼって、 SSH1 プロトコルの使い方を説明します。
SSH1 プロトコルを使っているサーバに公開鍵認証でログインする場合、
クライアント上ではまず ssh-keygen
コマンドを使って
秘密鍵・公開鍵ペアを生成する必要があります。
これは 4.1.1. クライアント上で秘密鍵と公開鍵ペアを生成する で説明した方法と同じですが、
SSH1 プロトコルの秘密鍵・公開鍵ペアを生成する場合は
ssh-keygen
コマンドに -t rsa1
というオプションを与えてやります。
SSH2 プロトコルのときと同じように秘密鍵と公開鍵ファイルが
~/.ssh/
ディレクトリ内に作成され、
公開鍵の指紋が表示されます。
SSH1 用の秘密鍵と公開鍵ペアを生成する |
---|
$ ssh-keygen -t rsa1 |
実行例:
client$ ssh-keygen -t rsa1 (SSH1 の秘密鍵・公開鍵ペアを生成する) Generating public/private rsa1 key pair. Enter file in which to save the key (/home/yusuke/.ssh/identity): (パス名を入力、あるいは Enter を押す) Enter passphrase (empty for no passphrase): (秘密鍵につけるパスフレーズを入力する) Enter same passphrase again: (同じパスフレーズをもう一度入力する) Your identification has been saved in identity. (鍵が生成された) Your public key has been saved in identity.pub. The key fingerprint is: 81:f9:19:2b:d3:61:f8:60:7f:67:ee:8f:96:0c:2d:1a yusuke@client (鍵の指紋が表示される)
SSH1 プロトコル用の秘密鍵ファイルと公開鍵ファイルは、それぞれデフォルトで
identity
と identity.pub
という名前が付けられます。
秘密鍵を記録した identity
ファイルの
パーミッションがそれぞれ 600 (-rw-------
、所有者のみが読み書き実行可能)
になっていることに注意してください。
秘密鍵ファイルが他人から読める状態になっていると、OpenSSH クライアントはその使用を拒否します。
なお、SSH2 プロトコルで使っている秘密鍵ファイル id_rsa
はテキストファイルでしたが、
SSH1 プロトコルで使う identity
ファイルはバイナリ形式です。
この時点では、identity.pub
ファイルも 1行のみからなるファイルですが、
1行が非常に長いため、実際の端末上では何行にも分かれて表示されます。
id_rsa
ファイル:
client$ cat identity.pub 2048 35 24980870124867471...(中略)...1274497676742299294801 yusuke@client
SSH1 プロトコルを使って公開鍵認証でログインする場合も、
SSH2 プロトコルの場合と同様に、
サーバ上で cat
コマンドを使って ~/.ssh/authorized_keys
ファイルに公開鍵を登録します
(4.1.2. 公開鍵をサーバに登録する 参照)。
OpenSSH は各行の格納されている公開鍵の形式を自動的に判定するため、
ひとつのファイル中に SSH1 と SSH2 の公開鍵をまぜて登録できます。
server$ ssh-keygen -l -f identity.pub (送られてきた公開鍵の指紋を確認する) 2048 81:f9:19:2b:d3:61:f8:60:7f:67:ee:8f:96:0c:2d:1a yusuke@client server$ mkdir ~/.ssh/ (.ssh ディレクトリを作成する) server$ cat identity.pub >> ~/.ssh/authorized_keys (identity.pub の内容を authorized_keys に追加する)
クライアント側の ~/.ssh/ |
サーバ側の ~/.ssh/ |
---|---|
ログインに使う秘密鍵ファイル identity が必要。
client$ ls -la ~/.ssh/ drwxr-xr-x 60 yusuke 4048 Mar 9 07:13 ../ drwx------ 2 yusuke 104 Apr 4 04:09 ./ -rw------- 1 yusuke 974 Apr 4 04:09 identity -rw-r--r-- 1 yusuke 638 Apr 4 04:09 identity.pub |
ログインに使う秘密鍵に対応する公開鍵 (identity.pub ) を登録した authorized_keys ファイルが必要。
server$ ls -la ~/.ssh/ drwx------ 2 yusuke 104 Mar 9 12:11 ./ drwxr-xr-x 59 yusuke 4136 Mar 9 14:02 ../ -rw------- 1 yusuke 2230 Apr 4 04:11 authorized_keys ※ これはサーバ上のユーザ yusuke が直接コマンドを実行した場合の出力例です。 |
OpenSSH の ssh
クライアントは相手の sshd
サーバが
使用できるプロトコルを自動的に判定する機能をもっています。
ログイン時にサーバが SSH1 プロトコルしかサポートしていない場合、
ssh
クライアントは自動的に SSH1 プロトコルを使用します。
SSH2 プロトコルもサポートしているサーバに対して
強制的に SSH1 プロトコルでログインする場合は、ssh
コマンドの実行時に -1
オプションを
指定してください。SSH1 プロトコルでは、デフォルトの秘密鍵ファイルとして ~/.ssh/identity
が使われます。
SSH1 プロトコルでも、SSH2 プロトコルと同じようにホスト認証
(3.2. ホスト認証のしくみ 参照) が行われます。
そのホストに初めてログインするユーザは、必ずシステム管理者に
ホスト公開鍵の指紋が正しいことを確認してから yes
と答えるようにしてください。
なお、SSH1 プロトコルで使われるホスト秘密鍵とホスト公開鍵は、
それぞれサーバの sshd
設定用ディレクトリの ssh_host_key
ファイル (秘密鍵)
と ssh_host_key.pub
ファイル (公開鍵) に格納されています。
SSH1 プロトコルでログインする:
client$ ssh -1 oldserver.example.com (強制的に SSH1プロトコルでログインする) The authenticity of host 'server.example.com (11.22.33.44)' can't be established. RSA1 key fingerprint is 89:e8:8f:f7:1b:6f:86:01:34:72:5d:b1:96:cd:0b:a3. (ホスト公開鍵の指紋が正しいかどうか確認する) Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'server.example.com,11.22.33.44' (RSA1) to the list of known hosts. Enter passphrase for RSA key '/home/yusuke/.ssh/identity': (秘密鍵のパスフレーズを入力する) Last login: Tue Apr 4 04:16:21 2006 from xx.xx.xx.xx oldserver$
SSH ソフトウェアはもともとオープンソースソフトウェアとして開発され、 その仕組みは一般に公開されていました。しかし現在、SSH ソフトウェアには商用のものも存在しています。 [脚注: 2006年現在、SSH Communications Security 社から SSH Tectia という商品名で販売されています。 詳細は http://www.ssh.com/ を参照してください。] 商用 SSH ソフトウェアも OpenSSH と同じプロトコルを使っており、 多くのコマンド名やオプションは OpenSSH と似ていますが、 使用している公開鍵ファイルの形式が異なるため、OpenSSH と商用 SSH の間で 相互にログインするためには公開鍵ファイルを変換する必要があります。
OpenSSH の ssh-keygen
コマンドをつかって
秘密鍵・公開鍵ペアを生成し、OpenSSH クライアントから 商用 SSH サーバにログインするには、
OpenSSH 用に生成された公開鍵ファイル
(id_rsa.pub
または id_dsa.pub
ファイル) を
商用 SSH 用の IETF形式 に変換する必要があります。これには
ssh-keygen -e
(export) コマンドを使います。
OpenSSH 用の公開鍵ファイルを商用 SSH サーバ用に変換する |
---|
$ ssh-keygen -e -f OpenSSH公開鍵ファイル > IETF形式の公開鍵ファイル |
実行例:
$ cat ~/.ssh/id_rsa.pub (元の OpenSSH 用の公開鍵ファイル) ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAtryZO2p...(中略)...HGMpKKbdzoQ== $ ssh-keygen -e -f ~/.ssh/id_rsa.pub > id_rsa.pub.ietf (IETF形式の id_rsa.pub.ietf に変換する) $ cat id_rsa.pub.ietf (変換された IETF形式の公開鍵ファイル) ---- BEGIN SSH2 PUBLIC KEY ---- Comment: "2048-bit RSA, converted from OpenSSH by yusuke@grape" AAAAB3NzaC1yc2EAAAABIwAAAQEAtryZO2p/2o8WAHR5PrUyICMTmjTt8NWR+mcicXkHE+ qv7bEs0Wppa6n2XOWcyv4Q91Y8QiJSspD40pLU+mjPpTIN/RQW12r+H+XCNM4ymaVzgBiQ ... vmQdDeBY/HGMpKKbdzoQ== ---- END SSH2 PUBLIC KEY ----
なお、商用 SSH ではサーバ側の公開鍵の管理方法も OpenSSH とは異なります。
authorized_keys
ファイルに公開鍵をまとめて登録する代わりに、
複数の異なる公開鍵ファイルを ~/.ssh2/
ディレクトリ上に置き、
~/.ssh2/authorization
ファイルで公開鍵認証に使用する
ファイル名を指定するという方法を採用しています。
詳しくは商用 SSH のマニュアルを参照してください。
商用 SSH の ssh-keygen2
コマンドをつかって
秘密鍵・公開鍵ペアを生成し、商用 SSH クライアントから OpenSSH サーバにログインするには、
IETF 形式の公開鍵を OpenSSH 用の形式に変換する必要があります。
これには ssh-keygen -i
(import) コマンドを使います。
商用 SSH サーバ用 の公開鍵ファイルを OpenSSH 用に変換する |
---|
$ ssh-keygen -i -f IETF形式の公開鍵ファイル > OpenSSH公開鍵ファイル |
実行例:
$ cat id_rsa.pub.ietf (元の IETF形式の公開鍵ファイル) ---- BEGIN SSH2 PUBLIC KEY ---- Comment: "2048-bit RSA, converted from OpenSSH by yusuke@grape" AAAAB3NzaC1yc2EAAAABIwAAAQEAtryZO2p/2o8WAHR5PrUyICMTmjTt8NWR+mcicXkHE+ qv7bEs0Wppa6n2XOWcyv4Q91Y8QiJSspD40pLU+mjPpTIN/RQW12r+H+XCNM4ymaVzgBiQ ... vmQdDeBY/HGMpKKbdzoQ== ---- END SSH2 PUBLIC KEY ---- $ ssh-keygen -i -f id_rsa.pub.ietf > id_rsa.pub (OpenSSH 用の id_rsa.pub に変換する) $ cat id_rsa.pub (変換された公開鍵ファイル) ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAtryZO2p...(中略)...HGMpKKbdzoQ==
商用 SSH クライアントを使っていたユーザが OpenSSH クライアントに乗り換えるには、
これまで 商用 SSH クライアントで使っていた IETF 形式の秘密鍵を OpenSSH で使える形式に変換する必要があります。
まず、商用 SSH の ssh-keygen2
コマンドを使って、秘密鍵ファイルに
つけられていたパスフレーズを解除してください。これによって秘密鍵ファイルは
OpenSSH の ssh-keygen -i
(import) コマンドで変換できるようになります。
変換したあとは OpenSSH の ssh-keygen -p
コマンドで新たにパスフレーズを
つけ直せば完了です。
また、商用 SSH サーバを OpenSSH の sshd
サーバに置き換える際も同様です。
サーバ上に登録されているホスト秘密鍵ファイルを IETF 形式から
OpenSSH の形式に変換する必要があります。
商用 SSH の秘密鍵を変換するときは、安全のため元の秘密鍵ファイルそのものは
書き換えず、これを一時的な秘密鍵ファイルに複製してから ssh-keygen2
コマンドで
パスフレーズを解除します。この後、OpenSSH 用の秘密鍵を生成したあとに
一時的なファイルを削除すれば、2つの異なった形式をもつ秘密鍵ファイルが入手できることになります。
商用 SSH 用の秘密鍵ファイルを OpenSSH 用に変換する |
---|
$ cp IETF形式の秘密鍵ファイル 一時的な鍵ファイル $ ssh-keygen2 -e 一時的な鍵ファイル $ ssh-keygen -i -f 一時的な鍵ファイル > OpenSSH用の秘密鍵ファイル $ chmod 0600 OpenSSH用の秘密鍵ファイル $ ssh-keygen -p -f OpenSSH用の秘密鍵ファイル $ rm 一時的な鍵ファイル
注意… |
実行例:
$ cp ~/.ssh2/id_dsa_2048_a tmpkey (商用 SSH 用の秘密鍵ファイルを一時的な鍵ファイルに複製) $ ssh-keygen2 -e tmpkey (一時的な鍵ファイルのパスフレーズを解除する) Cannot read public keyfile tmpkey.pub. Passphrase needed for key "2048-bit rsa, yusuke@client, Wed Apr 05 2006 01:03:22 -0400". Passphrase : (元のパスフレーズを入力) Do you want to edit key "2048-bit rsa, yusuke@client, Wed Apr 05 2006 01:03:22 -0400" (yes or no)? yes Your key comment is "2048-bit rsa, yusuke@client, Wed Apr 05 2006 01:03:22 -0400". Do you want to edit it (yes or no)? no Do you want to edit passphrase (yes or no)? yes New passphrase : (ただ Enter を入力) Again : (再び Enter を入力) Do you want to continue editing key "2048-bit rsa, yusuke@client, Wed Apr 05 2006 01:03:22 -0400" (yes or no)? no Do you want to save key "2048-bit rsa, yusuke@client, Wed Apr 05 2006 01:03:22 -0400" to file tmpkey (yes or no)? yes $ ssh-keygen -i -f tmpkey > id_rsa (商用 SSH 用の秘密鍵ファイルを OpenSSH 用の秘密鍵ファイルに変換) $ chmod 0600 id_rsa (安全のため、パーミッションを設定する) $ ssh-keygen -p -f id_rsa (OpenSSH 用の秘密鍵ファイルにパスフレーズを設定する) Key has comment 'id_rsa' Enter new passphrase (empty for no passphrase): (元のパスフレーズを入力) Enter same passphrase again: (再度パスフレーズを入力) Your identification has been saved with the new passphrase. $ rm tmpkey (一時的な鍵ファイルを削除する)
注意 |
---|
変換したあとのパスフレーズをつけていない一時的な秘密鍵ファイルは必ず削除するようにしてください。 |