前回少しだけ紹介したように, ssh にはクライアント側あるいはサーバー側のポートを反対側へ転送する ポート・フォワード機能があります。 この機能を使えば任意のプロトコルを暗号化できるので重宝します。 ポート・フォワード機能で真価を発揮するのは, ProxyCommand *4 を設定してファイアウオールを越えて ssh 接続を行っているときです。 例えば出張に持参したノート PC と社内 LAN 上の PC との間で 任意のプロトコルを使って盗聴の心配なく通信できます。
ssh クライアントを実行したローカル・ホストのポートを ssh サーバーが動いている内部 LAN 上のリモート・ホストに転送すれば, ローカル・ホスト上で実行する任意のクライアントから 内部 LAN 上のサーバーに接続することができます。 例えば, klab.org の内部 LAN 上のホスト kamiya へ ssh 接続する際は, 図 1 のように実行して, ローカル・ホストの 10080 番ポートを内部 LAN 上の Web プロキシ proxy.klab.org *5 の 8080 番ポートへ転送します。
ssh -L 10080:proxy.klab.org:8080 kamiya |
図 1 ローカル・ホストのポートをリモート・ホストへ転送(その1) |
図 1 中の「-L 10080:proxy.klab.org:8080」が ローカル・ホストのポート(10080 番)を リモート・ホスト(proxy.klab.org の 8080 番ポート)へ転送するオプションです。 コマンドライン・オプションで指定する代りに, ~/.ssh/config で図 2 のように指定することもできます。
Host kamiya ProxyCommand /home/sengoku/bin/proxy-into %h %p LocalForward 10080 proxy.klab.org:8080 |
図 2 ローカル・ホストのポートをリモート・ホストへ転送(その2) |
するとローカル・ホスト上の Web ブラウザの設定で, Web プロキシを localhost の 10080 番ポートに設定すれば, klab.org の内部 LAN 上の任意の Web サーバーに アクセスできるようになります(図 3)。
図 3 インターネットから内部 LAN 上の Web サーバーへアクセス |
ただし, 内部 LAN 上の Web プロキシ proxy.klab.org が, 内部 LAN 上の Web サーバーへのアクセスを許可していることが必要です。 LAN によっては, LAN 上の Web サーバーにアクセスするときは, Web ブラウザから直接接続させるために, Web プロキシ経由の接続を禁止しているかもしれません。
そのような場合は, LAN 上の Web サーバーへアクセスするための Web プロキシを新たに設置する必要があります。 LAN 上の Web サーバーへのアクセスだけならキャッシュする必要が無いので, stone *6 の簡易 http プロキシ機能を使うこともできます。 例えば, 図 4 のように stone を動かします。
stone -l proxy 8080 & |
図 4 簡易 http プロキシとして stone を実行 |
さて, これで出先のノート PC から内部 LAN 上の任意の Web サーバーに アクセスできるようになりました。 ただしこのままでは, インターネット上の Web サーバーにアクセスするときにも内部 LAN 上の Web プロキシ経由でアクセスすることになり, 少々非効率です。
Netscape などの Web ブラウザであれば, アクセスする URL ごとに Web プロキシを自動設定することができます。 例えば,図 5 のような内容のファイル 「/home/sengoku/proxy.pac 」を作成し, Netscape Communicator 4.7 の「Edit」メニューで「Preferences...」を選び, 写真 1 のように「Automatic proxy configuration」を選択して 「Configuration location(URL)」に 「file:/home/sengoku/proxy.pac 」を指定します。
function FindProxyForURL(url, host) { if (dnsDomainIs(host, ".klab.org")) { if (host == "www.klab.org") { return "DIRECT"; } return "PROXY localhost:10080; DIRECT"; } else { return "DIRECT"; } } |
図 5 プロキシ自動設定スクリプト「proxy.pac」の中身 |
写真 1 プロキシ自動設定スクリプトの指定 |
図 5 のスクリプトは, Web ブラウザがアクセスする URL ごとにプロキシを自動的に設定するためのものです。 このスクリプトは JavaScript で記述します*7。
プロキシ自動設定スクリプトが設定されると, ブラウザは新しい URL へアクセスしようとするごとに, このスクリプトで定義された関数 FindProxyForURL を, URL およびホスト名を引数として呼び出します。 例えば, 「http://www.klab.org/」をアクセスする場合は, 第一引数が「http://www.klab.org/」, 第二引数が「www.klab.org」になります。
そして FindProxyForURL の返り値に基づき, ブラウザは Web プロキシを自動設定します。 例えば, 返り値が「DIRECT」であればプロキシを設定せず, Web サーバーに直接アクセスしますし, 返り値が「PROXY localhost:10080」であれば localhost の 10080 番ポートのプロキシ経由でアクセスします。 複数の設定を列挙することも可能で, 例えば返り値「PROXY localhost:10080;DIRECT」は, まず localhost の 10080 番ポートのプロキシを使用し, もしこのプロキシが使用不可であれば, Web サーバーへ直接アクセス(「DIRECT」)します。
図 5 中, 「dnsDomainIs(host,".klab.org")」は ホスト名のドメイン名部分が「klab.org」であれば「true」を返す関数です。 つまりこのスクリプトは,
という意味になります。
proxy.pac は, ファイルとして読むこともできますが, Web サーバーから読み込むこともできます。 Web サーバーから読み込む場合, Web ブラウザ側は, 写真 1 の「Configuration location(URL)」に proxy.pac がある URL を入力するだけで良いのですが, Web サーバー側は proxy.pac を送信する際, 「Content-Type」が「application/x-ns-proxy-autoconfig」 になるよう設定しなければなりません。 例えば Apache の場合であれば, 設定ファイル httpd.conf に図 6 を加えるか, mime.types に図 7 を加えます。
AddType application/x-ns-proxy-autoconfig .pac |
図 6 httpd.conf での設定 |
application/x-ns-proxy-autoconfig pac |
図 7 mime.types での設定 |
逆方向のポート・フォワード, すなわち ssh サーバーが動いているリモート・ホストのポートを, ssh クライアントを実行したローカル側の LAN 上のホストへ転送することもできます。 この機能を使えば, 本来一方向のアクセスしか許可されていない環境下で, 逆方向のアクセスが可能になります。 例えば, 内部 LAN からファイアウオールを越えてインターネットへアクセスするための プロキシは用意されているけれども, インターネットから内部 LAN へアクセスする手段は セキュリティ上の理由などから一般ユーザーには提供されていないサイトが 特に大企業などに多いのではないかと思います。 あるいは, 内部 LAN がプライベート・アドレス*で構築されていて, ファイアウォールのアドレス・ポート変換*機能により 内部 LAN からインターネットへは任意の接続が可能だけれども, 逆方向は接続不可能というケースは, 特にケーブル・テレビ会社などが提供する インターネット接続サービスに多いでしょう。 いずれの場合でも, 内部 LAN からインターネット上の ssh サーバーに ssh 接続できれば, 逆方向のアクセスも可能になります。
例えば klab.org の内部 LAN 上のホスト kamiya から インターネット上のホスト asao.gcd.org へ ssh 接続する際, 図 8 のように実行すれば, ssh 接続先(asao.gcd.org)の 10022 番ポートを ローカル・ホストの 22 番ポートへ転送 (「-R 10022:localhost:22」オプション)し, ssh 接続先の 10080 番ポートを 内部 LAN 上の Web プロキシ proxy.klab.org の 8080 番ポートへ転送 (「-R 10080:proxy.klab.org:8080」オプション)します。
ssh -R 10022:localhost:22 -R 10080:proxy.klab.org:8080 asao.gcd.org |
図 8 リモート・ホストのポートをローカル側へ転送 |
「-L」オプションと同様, 「-R」オプションも, コマンドライン・オプションで指定する代りに, ~/.ssh/config に指定することもできます(図 9)。 するとリモート側で asao.gcd.org の 10022 番ポートへ ssh 接続すれば, 内部 LAN 上のホスト kamiya へ ssh 接続できるようになりますし, リモート側で実行する Web ブラウザの設定で asao.gcd.org の 10080 番ポートを Web プロキシに設定すれば, 内部 LAN 上の Web サーバーへアクセスすることができるようになります (図 10)。 図 10 では, ssh サーバーはインターネット上にありますが, 別のサイトの内部 LAN 上の ssh サーバーであっても, ssh 接続できる限り同様のことが可能です。
Host asao.gcd.org RemoteForward 10022:localhost:22 RemoteForward 10080:proxy.klab.org:8080 |
図 9 ローカル・ホストのポートをリモート・ホストへ転送 |
図 10 プライベート・アドレス上にあるサーバーへのアクセス |
ただし, ssh サーバー側から内部 LAN へアクセスできるのは, 図 8 の ssh 接続が続いている限りにおいてです。 ssh クライアントあるいはサーバーのいずれかのマシンをリブートすれば 当然 ssh 接続は切れますし, リブートしなくてもネットワークの不調などでパケットが届かない状態が続けば ssh 接続が切れてしまうことはあります。 そのたびに内部 LAN に(物理的に)出向いて行って, 図 8 を実行し直すというのは面倒です*8。
そこで, ssh 接続が切れたら再接続を行うスクリプトを書いて, ブート時に自動実行する方法を考えます。 例えば, 図 11 に示す rc.ssh スクリプトを /etc/rc.d/rc.ssh に置き, /etc/rc.d/rc.local に図 12 の行を挿入してブート時に実行します。 セキュリティ上の理由から, rc.ssh スクリプトを実行するユーザーは なるべく権限の無いユーザーにすべきです。 ここでは nobody 権限で実行しています(図 12)が, rc.ssh を実行する専用のユーザーを作る方が望ましいでしょう。
|
図 11 rc.ssh スクリプト |
図 8のssh 接続を行い,接続が切れたら再接続を行う。 |
su - nobody -c "/etc/rc.d/rc.ssh &" |
図 12 rc.ssh (図 11) をブート時に実行 |
rc.ssh スクリプトは, リモート・ホスト上で図 13 の sh スクリプトを実行します。 つまり 30 秒に 1 回, 「1」を標準出力へ出力し続けるプログラムです。 そして rc.ssh は「1」が送られてくるかを監視します。 もし 60 秒待っても「1」が送られてこなければ ssh 接続が切れてしまったものと見なし, ssh クライアントに HUP シグナルを送った後, 再接続を行います。
#!/bin/sh while : do sleep 30 echo 1 done |
図 13 リモート・ホスト上で実行するコマンド |
なお, この rc.ssh は複数のリモート・ホストに対して ssh 接続を維持できるように書いてあります。 すなわちリモート・ホスト名をキーとし, そのリモート・ホストに ssh 接続する際のオプションを値とする opt 連想配列に, リモート・ホストの数だけ要素を追加すれば OK です。 ssh のオプションは何でも指定可能なので, ローカルからリモートへのポート・フォワードを行うことも可能です。
さて, これでポート・フォワードを常に維持し続けることが可能になったわけですが, 一点注意すべき点があります。 それはパスフレーズの問題です。 本連載第 9 回「ssh(前編)」で説明したように, ssh のかぎを作成する際は必ずパスフレーズを設定すべきです。 ところがパスフレーズを設定すると, ssh 接続する際にパスフレーズを入力する必要があり, rc.ssh スクリプトのように自動的に接続を行いたい場合に困ってしまいます。
ssh-agent を走らせておけば, 毎回パスフレーズを入力する必要は無くなりますが, リブートすると ssh-agent を立ち上げ直してパスフレーズを入力する必要があり, rc.ssh のようにブート時に実行されるスクリプトの場合は ssh-agent は使えません。
従って常時ポート・フォワードを行う場合は, ポート・フォワード専用の ssh かぎをパスフレーズ無しで作ることになります。 万一かぎファイルを盗まれても影響を最小限に抑えるために, 極力権限の無いユーザーのかぎを作るべきです。 そして次の 2 つの制約を設定して, かぎが使える状況を限定します。
常時ポート・フォワードの場合, ssh クライアントを実行するホストは常に同じですから, ssh サーバーは特定のホストからの接続のみを受け付ければ十分です。 ただし図 10 に示すように, ssh クライアントとサーバーとの間でアドレス変換などを行うので, 接続元アドレスは ssh クライアントを実行するホストにはなりません。 アドレス変換を行うホストのアドレスになるでしょう。 もちろん, 接続元アドレスは IP スプーフィングによって偽造可能ですから, この対策は万全ではないのですが, 安全性を高める方法としては有効でしょう。
rc.ssh スクリプトの場合, ssh サーバーで実行すべきコマンドは図 13 の内容に限られます。 通常の ssh 接続のように任意のコマンドを実行する必要はありません。 図 13 は, 30 秒に 1 回, 「1」を標準出力へ出力し続けるだけのプログラムですから, このプログラムのみ実行できるように設定しておけば, 万一かぎファイルを盗まれて, 実行されてもさほど困ることはありません。
もちろん, 特定のコマンドしか実行できなくても, ポート・フォワードを任意に設定されてしまうと安全がおびやかされますので, かぎは厳重に管理すべきです。
(1),(2)共に ~/.ssh/authorized_keys ファイルにおいて, クライアントのかぎに対応する公開かぎの前にオプションを付けることにより, 設定可能です。 オプションは「,」で区切ることで複数指定できます。 オプションに続く公開かぎを認証に用いた接続時のみ, そのオプションが適用されます。 オプションとして以下の項目が指定できます。
接続を許可する接続元ホスト名のパターンを指定します。 「*」と「?」をワイルド・カードとして使うことができ, 「,」で区切ることにより複数のパターンを指定できます。 パターンの前に「!」を付けると, パターンにマッチしないホスト名からの接続のみを受け付けます。
ssh 接続を受け付けたとき実行するコマンドを指定します。 ssh クライアントから送られてきたコマンドは無視します。
ssh クライアントから送られてきたコマンドを実行する前に, 環境変数を設定します。
ポート・フォワードを禁止します。
X プロトコルの転送*9 を禁止します。
ssh-agent プロトコルの転送*10 を禁止します。
従って, rc.ssh を受け付けるサーバー asao.gcd.org の ~nobody/.ssh/authorized_keys ファイルの設定は, 「from="パターン"」と「command="コマンド"」のオプションを使って 図 14 のように記述すると良いでしょう。 図 14 中の napt.klab.org は, アドレス変換を行うホスト名*11 です。
from="napt.klab.org",command="sh -c 'while :;do sleep 30;echo 1;done'" 1024 35 23...2334 nobody@kamiya.klab.org |
図 14 ~/.ssh/authorized_keys |
公開かぎ本体は長いので,途中を省略しています。 |
本稿は日経Linux 2001 年 2 月号に掲載された、 実践で学ぶ、一歩進んだサーバ構築・運用術, 第 11 回「ssh (後編)」を日経BP 社の許可を得て転載したものです。
Copyright(C)2001 by 仙石浩明 <sengoku@gcd.org>
無断転載を禁じます