ポートフォワーディング編

以下のテキストは、執筆時当時の情報を元に書いたものであり、 現在の情勢にそぐわないことを含む場合があるので注意されたい。 また、テキストは最終提出原稿で校正を経る前のものなので、実際にUNIXUSER 本誌に記載されたものとは異なる。誤字脱字等そのままである。

致命的な誤り以外は加筆修正等は行なわないので情報の鮮度に気をつけつつ 利用して欲しい。

目次


==============================================================================
■特集■ 我らトンネルビルダー
==============================================================================
%% Part1 ポ-トフォワ-ディング編
%% 解説ポイント:TCP/IP,ポ-ト,HTTP,POP,トンネル,暗号化
%% 目的:HTTP(Proxy)しか空いていないファイアウォ-ル環境から
%%   自宅に構築したPOPサ-バ-にアクセスしてメ-ルを読む

グローバルIPv4アドレスが潤沢に使える時代はとうの昔に過ぎ去り、ほとんどの
サイトではエンドユーザレベルの一般ホストはプライベートアドレスを利用して
LANを形成していることだろう。

またセキュリティの観点から、外部(インターネット)から組織内部のホスト
への攻撃行為を未然に防ぐため、組織ネットワークの入口にFirewallを設置する
のが常識となっている。さらには、内部ネットワークから外部へのアクセスさえ
も、利用できるサービスを限定するなどの強い制限を掛けていることもあるだろ
う。これらは不慮の情報流出事故を防ぐためにも重要な設定と言えよう。

さて、情報というものは、部外者にもれてはならないという性質を持つ半面、必
要としている人に早く確実に伝わることが重要という性質も持つ。Firewallの守
りを固めることで、不測の事態で被る不利益の減少が期待できるいっぽう、固す
ぎることによって情報伝達に不要なコストがかかったり、遅延を招いたりする不
利益も考えられる。

今回の特集では、Firewallを利用してある程度の防御を固めているローカルエリ
アネットワーク(LAN)にあるホストと、外部ネットワークにあるホストとの間で
必要な情報のやりとりを可能にするために、既存の防御体制を改変することなく
必要最低限の通信路を確保する方法を考えていく。実際のところ、そのような
「トンネリング」の需要は多く、対応するツールも多い。それらをインストール
すれば簡単に実現できてしまうかもしれないが、実際にどのような仕組みでトン
ネルが構築されているのかを理解しておくのは安全にトンネリングを行なう上で
重要なことだろう。トンネルを作成するという実用的な作業を題材に、普段言葉
だけで理解しているTCP/IPの知識をできるだけ深いものにすることを目指したい。


■
■ポートフォワーディング編
■



●現在のLAN事情

昔のインターネットは「ピアーツーピア」★が基本であり、全てのホストはグロー
バルIPアドレスを持ち、互いに対等に、方向を気にすることなく通信するのが当
たり前だった。現在ではユーザの利用する端末レベルで言えば、「インターネッ
トに繋がっている」コンピュータでグローバルIPアドレスを持っている方が少な
いだろう。

+- ★ピアツーピア ------------------------------------------------------------
Peer to Peer。P2P とも。2つのホストが対等関係でネットワーク接続し、
通信を行なうこと。
+-----------------------------------------------------------------------------

ひとつのオフィスという規模のLANで考えた場合、現在ではそこに属する全て
のホストにはプライベートアドレスを持たせ、内部では互いに自由に通信を行な
い、外部へのアクセスはNAT★を利用しているところが多いだろう。

+- ★NAT ---------------------------------------------------------------------
Network Address Translation

ゲートウェイとなるホストで、そこを通過するパケットのソースアドレスと宛先
アドレスを変換する方法。プライベートIPアドレスをもつホストから外界インター
ネットにパケットが出るときにソースアドレスをグローバルIPアドレスに変換して
出して行く(図 り 参照)。狭義の「NAT」はパケットのアドレスを書き換えるのみだ
が、ポート番号も書き換える仕組みを NAPT (Network Address Port
Translation) という。複数のポート番号を利用してパケットを外界に送出する
ことで、内部LANにある複数のホストが同時にアドレス変換機構を利用できる。


図 り                      (Private)                      (Global)
                          10.1.2.11 =>       +---------+   x.y.z.w =>
 +--- Private 10.1.2.0/24-----------------+--+ NAT box +----------
 |                                           +---------+
 +--[ Client A 10.1.2.10 ]
 |
 +--[ Client B 10.1.2.11 ]
 | <====
 :
 :

ほとんどの環境ではポート番号を含めたアドレス変換機構を利用しているので、
現在ではNAPTを含めたものを広義の「NAT」として指し示すことが多い。本文中
ではNAPT機構を含めた広義の「NAT」の意味で使用する。「IPマスカレード」は
NAPT機構のLinuxによる実装。
+-----------------------------------------------------------------------------

NATを利用した環境ではすくなくとも内部からインターネットへは出て行ける環
境が揃っているので、ほとんどの場合は不満なくネットワーク資源を利用できる
だろう。「片道」だけの許可でも内部にいる分には幸せである。しかし、中長期
プロジェクトで出張先と自社のデスクを何度か往復したりするような場合、双方
向でデータのやりとりができた方が便利である。もし、本当に大規模なプロジェ
クトだとしたら、対価を払い専用回線を引いて物理的にも安全な通信路を確保し
た方が良いだろうが、それほどでもない場合は必要最低限のサービスだけをアク
セスできるようにすれば十分なことが多い。このようなときに、必要なサービス
だけを引き込むために「ポートフォワード」という技法が手軽に利用できるので
後半でその方法を解説する。

●制約の強いFirewall

通常利用しているLANがNAT環境の場合は、内部から外部へは自由に出て行ける。

ところが場合によっては、内部から外部のサーバに接続できるサービスをも、よ
り強く限定しているFirewallもある。NATでは、パケットのアドレス変換こそ経
るものの、内部ホストと外部ホストでパケットレベルでデータをやりとりできる
【図 り】。それよりも厳しく、特定のサービスに限定して、内部のクライアン
トからのデータの送信要求をいったん引き受けて、本当のサーバに代理で問い合
わせを行ない、返って来た結果を内部クライアントに戻すようなものがある【図
ち】。これを一般的に代理サーバ、またはプロクシ(Proxy)サーバと呼ぶ。

---[図 ち]------------------------------------------------------------\

  +----------------+ HTTP(TCP/80番ポート) +--------+
  |内部クライアント| ====================>| Proxy  |
  +----------------+			  | Server |
					  +--------+
					     ↓代理で取得
			〜〜 インターネット〜〜
			↓
		     +----------+
		     |外部	|
		     |Webサーバ |
		     +----------+
絵心がないので具体的な案が沸きませんが、パケットレベルではつながっていない
イメージが出ればなあ………と
----------------------------------------------------------------------/

プロクシサーバといえば「Webページを見るときに利用するもの」としておなじ
みかもしれない。実際には、Webサービスだけでなく様々な種類のプロトコルを
代理でおこなうものが存在する。どのようなサービスを代理取得できるようにす
るかはそのサイトの都合によって決まるだろう。とはいえ、少なくともWebサー
ビス(HTTP)に関しては需要が高いので利用できるようにしているだろう。

このような場合は、出向先から自社オフィスに戻った時に、出向先のメイルを取
り込んだりすることも不可能となる。それがどんなに重要度が高いからといって、
Firewallの設定を変えて出向先とのやりとりを自由にする、などということをし
ていたら切りがないし、そうした「特例設定」は外し忘れなどのトラブルを招き
やすい。

そうした場合に、システム全域の設定を特別に緩めることなく、外部の決められ
た場所とだけ、情報授受の枠を拡げるための技法を考えて行こう。繰り返しにな
るが、本稿の目的はVPNなどを張ることそのものよりも、後半で述べる「ポート
フォワード」などに必要な基礎知識を理解して行くことである。そのために、こ
れまで漠然ととらえていたTCP/IPのしくみを、以下の例を通じて体験しながら理
解していって欲しい。


●身近なサービス

我々が普段利用しているサービスを考えてみよう。今では欠かすことのできない
ものとして、WWW、Mail、POPをとりあげよう。たとえばWebページを見るとき、
所望するページがどのようにしてやってくるかを理解する。

・Webページを見る場合

  普段なにげなく見ているWebページが、自分の使っているブラウザ画面に届く
  までにどのような手順を経ているのかを考えてみよう。たとえば

	http://www.unixuser.jp/

  というWebページを見る場合を考える。手元のコンピュータでは【図 い】の
  ような解釈がおこなわれる。

---[図 い]----------------------------------------------------------------
	http://www.unixuser.jp/
	-+--   ------+------- ---
         |           |         +-- / というパス名の資源を取得すべし
	 |	     +--- www.unixuser.jp というサーバに繋げ
	 +--- プロトコルHTTPを使って得られるサービスである

	 +暗黙のルール → HTTPのデフォルトポート番号は80

--------------------------------------------------------------------------

  これらの情報をもとに相手サーバにリクエストを伝え、結果が返って来るまで
  を支えている仕組みを簡単に押さえておこう。

・TCP/IPのプロトコル階層

  人と人とがお互いにコミュニケーションを取るときに決められた約束のことを
  「プロトコル」という。「山」と言ったら「川」と答えるのもプロトコルの一
  つである。コンピュータ同士でも通信するときの取り決めをプロトコルという。
  普段なにげなく見ているWebページが手元に届くまでの仕組みは、いくつかの
  プロトコルが重なりあってできている。それらのプロトコル階層は下位(ハー
  ドウェアに近い方)から、

	* EthernetやPPPなど、実際に信号を流しあって繋がるときのプロトコ
	  ルの層 (ネットワークインタフェース層)

	* IPアドレスによって互いを識別するときのプロトコルの層(インター
          ネット層)

	* 特定の相手に実際にデータを流すときの実際の出入口を決めるプロト
          コル(トランスポート層; TCP/UDP)

	* アプリケーションごとにデータのやりとり方式を決めるプロトコル
          (アプリケーション層; HTTP, SMTP, NTP…など)

  のように分類されている。先ほどの例のWebページ http://www.unixuser.jp/
  が届くまでの動きをプロトコル階層にしたがって体感してみよう。【図 い】
  の解釈により http://www.unixuser.jp/ を得るためには、

	サーバホスト			www.unixuser.jp
	アプリケーションプロトコル	HTTP
	パス名				/

  という情報を元にアクセスすれば良いことが分かった。さらに、もう一点重要
  な暗黙のルールとして、

	HTTPのポート番号		TCP/80

  を利用する。一般的には接続するサーバホストには(サーバホストでない場合
  も)複数のサービスが起動している。ネットワーク経由でなんらかのサービス
  を受け取りたい場合、一つのサーバホスト内で起動しているサービスのうちど
  のサービスを受け取りたいのかを識別する必要がある。「ポート番号」を指定
  するとどのサービスかを指定できる。一般的にポート番号とそれに対応するサー
  ビスは決まっていて(表 ろ)、サービス固有のクライアントソフトウェアを使
  う場合、ユーザはポート番号を意識しなくて良いようになっている。

---[表 ろ]------------------------------------------------------------

	【主なサービスとポートの対応】

	サービス			種別		ポート番号

	ftp(データ)			TCP		20
	ftp(コントロールポート)		TCP		21
	SSH(Secure Shell)		TCP		22
	telnet				TCP		23
	SMTP(メイル配送)		TCP		25
	DNS(ネームサーバ)		UDP(TCP)	53
	HTTP(Web)			TCP		80
	POP(メイル取り込み)		TCP		110
	NTP(時刻同期)			UDP		123
	IMAP(メイル取り込み)		TCP		143
	HTTPS(HTTP Security)		TCP		443
	CVS pserver			TCP		2401

----------------------------------------------------------------------

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

・TCPを利用したサービス取得を体感する

  実際にWebページを取得する手順を「手で」行ってみることで、TCP/IPのプロ
  トコル階層のそれぞれの働きがより明確になるだろう。
  http://www.unixuser.jp/ を【図 い】のように解釈したとして、その後の手
  順を追ってみよう。中で何が起きているかを理解するため、敢えて回りくどい
  方法を取ってみたい。このときに挙動を確認するためにtcpdumpコマンドを利
  用する。

----[コラム ethereal]-------------------------------------------------\

Etherreal (http://ethereal.netarc.jp/) はフリーのプロトコルアナライザで、
各種Unix及びWindowsに対応している。Ethereal は300以上ものプロトコルを理
解し、捕捉したパケットを対応するプロトコル内での意味にまで踏み込んで解析
するツールである。GUIバージョンである ethereal コマンドをroot権限で起動
し、キャプチャを行なうと下図のような結果が得られる。

%image ethereal.png

上段が取り込んだパケットの一覧、中段がパケットをレイヤごとに階層的に解析
して表示するウィンドウ、下段が実際のパケットダンプである。たとえば、POP3
のログイン時のパケットを Post Office Protocol のレイヤで解析した結果は以
下のようになる。

%image ethereal-apop.png

このようにアプリケーションがやりとりしたメッセージも復元可能である。もし
これがAPOPでなかったら、パスワードも丸見えとなる。実際にこの様子を目の当
りにすると暗号化しない通信がいかに無防備かが分かるだろう。

Etherreal は、上記URLの "Download" の項目より "Binary Packages" もしくは
"Source Code" のリンクを辿ることで入手可能だ。(執筆時現在)
ethereal-0.9.9 までのバージョンは remote-code-execution exploit が発見さ
れているので、既に古いバージョンの Ethereal をインストールしている場合は
早急にアップデートするのが良いだろう。

----------------------------------------------------------------------/


  * tcpdumpコマンド

    ネットワークインタフェースを通過するヘッダとパケットをメッセージとと
    もに表示するのが tcpdump コマンドである。パケットを生で見ることがで
    きるので利用に当たってはプライバシーの侵害をおこす可能性もあるので厳
    重に注意して利用して欲しい【註 は】。そのような性質を持つこともあり、
    管理者権限(スーパーユーザ)でなければ利用できない。もし、ネットワーク
    管理権限を持っていない場合、管理者に無断で使用すると「クビが飛ぶ」ほ
    どの事態に陥る職場もあるだろう。運用には慎重に慎重を重ねて頂きたい。

---[※ は]------------------------------------------------------------\
個人所有のコンピュータをインターネットにつなぐことが現実的でなかった時代
には、教育を受けたシステム管理者だけが使うように注意していたところもある
だろう。しかし現在のように、安売りPCでも、経験とモラルに欠けるユーザでも
簡単にインターネットにつなげる昨今では、安易にパケットを見るなと注意する
ことよりも、「部外者には簡単にLANにつながせない」工夫と、「機密情報を生
の状態(プレイン)で流さない」ように守りを固めておくことの方が重要だろ
う。
----------------------------------------------------------------------/

    tcpdumpコマンドは

	# tcpdump [-オプション] 条件式

    のように起動する。<条件式> に与えた式を元に、表示すべきパケットの
    絞り込みをおこなう。<条件式>を省略すると監視するネットワークインタ
    フェースに流れるパケット全ての情報を表示する。<オプション>と<条件
    式>は非常に多いので、今回利用するものに絞って解説しよう。

    -c <数>

	tcpdump起動後、指定した<数>だけパケットの情報を受け取ったら終
	了する。回数を指定しないと SIGINT または SIGTERM シグナルを受け
	取るまで(C-c で止めるまで)永遠に動き続けるので、遅い回線の先で
	tcpdumpを不用意に起動すると停止するまで時間がかかり思わぬトラブ
	ルを生むことがあるので注意したい。

    -i	<インタフェース>

	監視するネットワークインタフェースを指定する。-i による指定を省
	略すると、システムにあるネットワークインタフェースで稼動している
	もののうち最初に一覧に現れるインタフェースを選んで、そこに流れる
	パケット情報を表示する。

    -n

	パケットの送信元(source)/送信先(destination)アドレスをホスト名に
	直さず(IPアドレスのまま)表示する。またポート番号もニックネームに
	直さず数字のまま表示する。

    -p

	ネットワークインタフェースを Promiscuous mode にしない。デフォル
	トでは「(可能なら)する」。Promiscuous mode にすると、自分宛のパ
	ケット以外も受け取るので盗聴行為と区別がつかなくなる。LAN全域の
	トラブル診断時など緊急の場合以外は -p を指定した方が良いケースも
	あるだろう。もっとも、対象ホスト宛のパケット以外はインタフェース
	に送り届けない機器(スイッチングハブなど)の下流にある場合はこの限
	りでない。

    -q

	表示する情報を簡素にする(-qをつけたほうがtcp/udpの区別が分かりや
	すい)。

    続いて、tcpdumpに与える<条件式>のうち、今回利用するものについて説
    明しよう。条件式の部分には絞り込みの対象となるホスト名、プロトコル、
    ポート番号などを書くことになる。パケットの、種別、送信元アドレス、送
    信先アドレス、tcp/udpのポート、などによって表示するパケットを選別す
    る。今回利用するのはポートの指定で、特定のポートに対するパケットのみ
    を選別したい場合は、条件式に

	port <ポート番号>

    と書く。今回は、実際にパケットが出て行く様子を目で確認するためだけに
    利用するので "port" 構文しか利用しないが、その他にも多数の構文がある。
    詳しくはオンラインマニュアル tcpdump(1) を参照して欲しい。

    以下の実験ではktermなどのターミナルを2枚起動しておき、片方でtcpdump
    を、もう片方で接続のためのコマンド群を入力しよう。  

  【実験開始】

  http://www.unixuser.jp/ を得るには次のような手順をとる。

  1. サーバホストのIPアドレスを引く
  2. 求めたIPアドレスのTCP/80番ポートに接続する
  3. / のWebページを取得する

  実際にこれらの手順を追って行こう。

  1. サーバホストのIPアドレスを引く

     ホスト名からIPアドレスへの変換はDNSの問い合わせが発生する。DNSは、
     UDPの53番ポートを利用するはずなので、パケット監視のためには次のよう
     に起動しておく【註 に】。

	# tcpdump -npq port 53
	  ~~~~~~~~~~~~~~~~~~~~
	tcpdump: listening on sip0

---[※ に]------------------------------------------------------------\
ネットワークインタフェースを複数持つホストの場合はパケット情報を表示した
いインタフェース名を -i オプションで指定する必要があるかもしれない。
----------------------------------------------------------------------/
     続いてhostコマンドを利用してDNSの問い合わせをする。

	% host www.unixuser.jp
	  ~~~~~~~~~~~~~~~~~~~~
	www.unixuser.jp has address 210.155.136.45

     するとtcpdumpが情報を表示するだろう。
tcpdump: listening on sip0
01:23:11.862469 172.19.5.85.64058 > 172.19.5.10.53: udp 33
01:23:11.862843 172.19.5.10.53 > 172.19.5.85.64058: udp 49
01:23:11.863336 172.19.5.85.64057 > 172.19.5.10.53: udp 33
01:23:11.863772 172.19.5.10.53 > 172.19.5.85.64057: udp 33
01:23:11.863811 172.19.5.85.64056 > 172.19.5.10.53: udp 33
01:23:11.864016 172.19.5.10.53 > 172.19.5.85.64056: udp 33

     この行を見ると分かるように

	送信元IPアドレス.送信元ポート > 送信先IPアドレス.送信先ポート

     という情報をもとに発生しているパケットを知ることができる。tcpdumpの
     出力結果より、172.19.5.85(クライアント)から172.19.5.10(サーバ)の
     53/udpポートに問い合わせが行っていることが分かる。

  2. 求めたIPアドレスのTCP/80番ポートに接続する

     上で得られたアドレスに接続してみよう。ひとくちに「接続する」と言っ
     てもWebサーバプログラムと通信しなければならない。HTTPの場合は通常
     TCP/80番ポートを利用する。これを実感するために telnet コマンドを利
     用する。一般的な利用法では、telnet コマンドは遠隔ホストにログインす
     るためのものという感覚だが、実際はTCPの任意のポート番号を指定してそ
     こに接続してやりとりをおこなうことができる。

     まず、TCPの80番を探るためにtcpdumpを起動する。

	# tcpdump -npq tcp port 80
	  ~~~~~~~~~~~~~~~~~~~~~~~~
tcpdump: listening on sip0

     別の端末で telnet を利用してWebサーバに接続しよう。

	% telnet 210.155.136.45 80
	  ~~~~~~~~~~~~~~~~~~~~~~~~
Trying 210.155.136.45...
Connected to www.unixuser.jp.
Escape character is '^]'.



     tcpdump の結果を見てみよう。

----------------------------------------------------------------------\
19:38:05.106974 172.19.5.85.64679 > 210.155.136.45.80: tcp 0 (DF)
19:38:05.140643 210.155.136.45.80 > 172.19.5.85.64679: tcp 0 (DF)
19:38:05.140675 172.19.5.85.64679 > 210.155.136.45.80: tcp 0 (DF)
----------------------------------------------------------------------/
 
     この結果から、ローカルホスト(172.19.5.85) のポート64679と、Webサー
     バホストのポート80が、接続された回路のように機能してWebページ取得の
     ためのやりとりを進めることが伺える。実際にWebページコンテンツを取得
     するときにもこのポートが保たれることに注意する。

  3. / のWebページを取得する

     HTTPでは GET というリクエストによってWebページを取得する【註 ほ; 
     実行例 へ】。
---[※ ほ]------------------------------------------------------------\
現在のほとんどのブラウザでは HTTP/1.0, HTTP/1.1 で取得するので
書式は若干異なる。
----------------------------------------------------------------------/

【実行例 へ】
----------------------------------------------------------------------\
	Escape character is '^]'.
	GET /
	~~~~~






UNIX USER
UNIX USER TOP PAGE

  :
  :
  以下多くの行が続く
----------------------------------------------------------------------/

     tcpdump の結果を見てみよう【実行例 と】。

【実行例 と】
----------------------------------------------------------------------\
19:41:23.673670 172.19.5.85.64679 > 210.155.136.45.80: tcp 7 (DF)
19:41:23.706335 210.155.136.45.80 > 172.19.5.85.64679: tcp 0 (DF)
19:41:23.713881 210.155.136.45.80 > 172.19.5.85.64679: tcp 1380 (DF)
19:41:23.715748 210.155.136.45.80 > 172.19.5.85.64679: tcp 1380 (DF)
19:41:23.715772 172.19.5.85.64679 > 210.155.136.45.80: tcp 0 (DF)
19:41:23.753787 210.155.136.45.80 > 172.19.5.85.64679: tcp 1380 (DF)
19:41:23.755658 210.155.136.45.80 > 172.19.5.85.64679: tcp 1380 (DF)
  :
  :
  以下同様の行が続く
----------------------------------------------------------------------/


   このように、最初の接続で確立された通路を利用してデータのやりとりをお
   こなっていることが分かる。TCPによる通信は、特定のポートとそこに接続し
   に行くクライアント側のポートが固定されているので、一本の仮想的な専用
   通信回路ができるとイメージして良い。「一本を最後まで使う」という性質
   があるので、TCPで供給されるサービスは、以下で説明する
   ポートフォワーディングの恩恵を得
   やすいと言えよう。

   少し話は外れるが、telnetコマンドを利用したTCP接続は、専用クライアント
   ソフトを使うのと違って、実際にやりとりしている情報が如実に分かるので、
   メイルサーバやPOPサーバの動作検証をするときには非常に有用である。


●ポートフォワーディングによるサービスの延伸

ポートフォワーディングは、その名の通り特定のポートを別のホスト上の指定
したポートへフォワード(転送)する技法である。一般的には特定のポートに来た
接続要求(とその後の接続)をフォワードするもの全てを指す言葉だが、実用度が
高いのは物理的に離れたネットワークに存在するホストの間に仮想的なトンネル
を作成し、そこを通してポートフォワードを行なうものだろう【図 ぬ】。


---[図 ぬ-Port-Forward]----------------------------------------------\

    +---------+            +----------+
    |         |		   |          |        
    | host A  |		   |  host B  |       /-->□
    |       --+------------+--	      |      /
    |	 /--->->- tunnel--->-->-------+-----/
    |   /   --+------------+--	      |
    |   |     |		   |          |
    +---+-----+            +----------+
        |
   ○---+
host A と host B は離れていて、違うネットワークに入っている
tunnelの部分は長い筒で、あぶなそうなインターネットの間を
貫通するような感じ。
----------------------------------------------------------------------/

2ホスト間はインターネットを通過するので、トンネル部分は暗号化するなどの
付加機能を利用することが多い。2ホスト間を暗号化したトンネルで結び、そこ
を通してポートフォワードすることが手軽にできる代表的なツールがSSHである。
ポートフォワーディングを利用したサービス延伸の最初の例として OpenSSH を
利用したものをとりあげよう。


●OpenSSHを用いたポートフォワード

主にリモートログインのために使われる OpenSSH は、現在では多くのUnix系OS 
に標準装備されている。SSH以前にもリモートログインの時に確立する通信を暗
号化し、パスワードと通信内容そのものを暗号化するためのツールはいくつかあっ
たが、ポートフォワード(TCPのみ)など本来の機能(リモートログイン)以上の付
加価値がついていたことがSSHの普及の要因のひとつと言えよう。

OpenSSHは、基本的に2つのホスト間でのリモートログインを行うものなので、そ
の2ホストをむすぶ暗号化回線を仮想的なトンネルとして利用したフォワーディ
ングを提供する。その形態には2種類あり、ローカルホストの指定したポートに
来た接続をトンネル経由でリモートホスト側に渡し、さらにそのホストから到達
できる任意のホストの任意のポートに転送する、ローカルフォワード【図 り】。
そしてもうひとつは、リモートホストのポートに来た接続をローカルホストから
到達できるどこかのポートへ転送する、リモートフォワード【図 ぬ】である。
ここで注意が必要なのは暗号化されている区間はsshクライアントとsshdの間の
トンネル部分だけという点である。

もう一点、これらのフォワーディングでは SSHによるリモートログインをする必
要があるので、ポートフォワードによるサービスの延伸を実現するにはどちらか
片方のホストが、外部からSSH接続を許可する設定になっている必要がある。外
部からのSSHログインのためにSSH用ポート(デフォルトで22/tcp)を空けることの
安全性を吟味するのが望ましい【コラム る】。

---[図 り-Local-Forward]----------------------------------------------\
                  Firewall
   +----------+     >                 +---------------+
   | Local    |     <                 |  Remote       |
   |  Host    +-------- 22番ポート ---+   Host        |
   |      ________________________________	      |
   |[ssh]/    +------------------------   \[sshd]     |
   |    /     |     >                 |    \	      |
   |   |      |     <                 |     \	      |
   +--/ \-----+     >                 +-----|---------+
       ↑	    >			    |
    任意のポート    <		            |
    にきた接続を    >			    Remote Host から
            ==> ○  <			    到達できる任意のホストの
                    >  × <===		    任意のポートへ
                    <			    ↓
                    >               

----------------------------------------------------------------------/

---[図 ぬ-Remote-Forward]----------------------------------------------\
                  Firewall
   +----------+     >                 +---------------+
   | Local    |     <                 |  Remote       |
   |  Host    +-------- 22番ポート ---+   Host        |
   |      _________________________________	      |
   |     /    +------------------------    \	      |
   |    /     |     >                 |     \	      |
   |   |      |     <                 |     | 	      |
   +---|------+     >                 +----/ \--------+
       |     ==> ○ <		            任意のポートに来た 
   localhostから    >			    接続を
   到達できる       <			    
   任意ホストの     >  × <===		    
   任意ポートへ     <			    
       ↓           >               

----------------------------------------------------------------------/


---[コラム る]--------------------------------------------------------\

SSHによるリモートログインであればパスワードの漏洩は、平文を使うtelnetな
どと比べてはるかに可能性が低い。しかし、人為的ミスによるパスワードの漏洩
にたいしてはSSHの暗号化も無力である。そのような事態を警戒する場合は、外
部からのSSH接続を受けるsshd(SSHデーモンプログラム)の設定を「プレインパス
ワード(Unixパスワード)ログインは認めない」ようにしておくと、さらに安全性
は高まる。外部からのログインを受けるホストのsshd設定ファイル
(/etc/ssh/sshd_config 等) で、

RSAAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no

にしておき、公開鍵暗号方式による認証でなければ受け付けないようにする。公
開鍵暗号方式では、

	・ユーザが覚えているパスフレーズ
	・ローカルホスト内の秘密鍵
	  (パスフレーズがないと取り出せない)
	・リモートホスト上の公開鍵
	  (リモートホストにあらかじめ登録しておかなければならない)

の三者が合致して初めて認証通過となる。仮にパスフレーズが洩れたとしても認
証を破ることはできない。もっとも、コンピュータにパスフレーズをメモした紙
を張りつけた状態で盗まれたりしたら破られてしまう。しかしそれはコンピュー
タのセキュリティを語る以前の問題だろう。

念のため、公開鍵を使う場合の準備方法を手短に説明しておこう(OpenSSH 3.5
を想定)。

1. ローカルホストで秘密鍵と公開鍵のペアを作成する

	% ssh-keygen -t dsa
	  ~~~~~~~~~~~~~~~~~
	秘密鍵につけるパスフレーズを2回、間違わずに入力する

2. ~/.ssh/id_dsa (秘密鍵) と ~/.ssh/id_dsa.pub(公開鍵) が生成されるので
   (鍵のファイル名は ssh-keygen -f オプションで変更可能)、
   それらのうち公開鍵ファイルをリモートホストに転送する。

3. リモートホストの ~/.ssh/authorized_keys に公開鍵の内容を追加する(なけ
   れば新規に作成し、他人に読まれないようにchmodする)。このファイルをリ
   モートホストにコピーする場合に、一時的にプレインパスワードでのログイ
   ンを許可する設定にするか、実際にFDなどの媒体で物理的に移動して持ちこ
   むことになる。

4. ローカルホストに戻り、通常通り ssh ログインする。

より詳しい説明はオンラインマニュアル ssh-keygen(1), sshd(8) を参照された
い。
   
----------------------------------------------------------------------/

%%↓本文続く

以下の2例は、リモートホストに対してSSHログインを行なう設定がされているも
のと仮定する。


・ローカルフォワードの利用

  出張先のPOPアクセスを自社からおこなう場合、などという例を考えてみよう。
  出張先のPOPサーバは、出張先のLANでしか利用することができない【図 を】。

---[図 を]------------------------------------------------------------\

  +--- 自社 LAN ---+      ||	              || +----出張先-------+
  |	           |  NAT ||	 POP(110)     || |	[POP srv]  |
  |              --+--[GW]-->-->-->-->-->-->×[gw]		   |
  |		   |      ||\	internet      || |		   |
  |		   |	  || \---->-->-->--->--->--->[sshd]venus   |
  |		   |	  ||	 SSH(22)   ○ || +-----------------+
  +----------------+ FireWall
						sshdのホスト=venus
						POPサーバホスト=mercury
----------------------------------------------------------------------/

  このようなとき、つまり、リモートホストのLANのみで利用できるサービスを
  ローカルLANから利用するときに使うのがローカルフォワードである。もちろ
  ん、リモート側のLANのPOPサーバが外部からも自由にアクセスできるような場
  合でも、POP通信路をSSHの暗号化トンネル内に閉じ込めて、インターネットを
  通過するときの傍受を防ぐという目的にも有効である。

  OpenSSH によるローカルフォワードは ssh コマンド -L オプションで指定す
  る。

	ssh -L ローカルポート番号:転送先ホスト:転送先ポート \
		リモートホスト

  もし、【図 を】の場合に、ローカルホストの10110番ポートへのアクセスを、
  リモート側LANのPOPサーバに転送したい場合は、以下のようにする

	% ssh -L 10110:mercury:110 venus
	(例が簡潔になるようにホスト名を短く表記したが、実際にはsshが
	 名前解決できるホスト名を指定しなければならないことに注意)

  
・リモートフォワードの利用

  出張先での作業中、自社オフィスにある資料を利用したい、などという例を考
  えてみよう。【図 を】の状況とほぼ同じだが、自社LANには外部からのSSHが
  全く許可されていないものとする【図 わ】。

---[図 わ]------------------------------------------------------------\

  +--- 自社 LAN ---+      ||外からは全て遮断  || +----出張先-------+
  |	           |      ||	              || |	           |
  |                |      ||× <-<--<--<--<--<-[gw]		   |
  |		   |      ||	internet      || |		   |
  |	[saturn]   |	  ||	              || |                 |
  |		   |	  ||	              || +-----------------+
  +----------------+ FireWall
    自社での使用ホスト
     =saturn
----------------------------------------------------------------------/

  このようなときには、あらかじめ(もしくは誰かに頼んで)自社LANのホストか
  ら出張先のホストに対してリモートフォワードを確立しておく【図 か】。
  「ファイルを取得する」ためのプロトコルには様々なものがあるが、この場合
  scp(1) を利用するものとしよう。図でいえばvenusとsaturn間でscpをおこな
  うということである。

---[図 か]------------------------------------------------------------\

  +--- 自社 LAN ---+      ||	              || +----出張先-------+
  |	           |  NAT ||	 SSH(22)      || |   [venus]       |
  |            /---+-------------------------------->|sshd |	   |
  |  saturn   / _______________tunnel________________+     |	   |
  | +--------/ /,---+-------------------------------->[10022]	   |
  | |  22<====//   |      ||	internet      || |		   |
  | |	    +-/	   |	  ||	              || |                 |
  | +-------+	   |	  ||	              || +-----------------+
  |		   |	  ||
  +----------------+ FireWall
    自社での使用ホスト
     =saturn
----------------------------------------------------------------------/

  OpenSSH によるリモートフォワードは ssh コマンド -R オプションで指定す
  る。

	ssh -R リモートホストでのポート番号:転送先ホスト:転送先ポート \
		リモートホスト

  もし、【図 か】の場合に、venusの10022番ポートへのアクセスを、saturnで
  のSSHポート(22)にフォワードしたい場合は

	% ssh -R 10022:saturn:22 venus

  とする。venus側からファイルコピーを行なうには venus で localhost の
  10022番ポートに接続する。scpを使うのであれば、

	% scp -P 10022 localhost:docs/urgent.pdf .

  などとする【コラム よ】。

---[コラム よ]--------------------------------------------------------\
ポートフォワードを利用して、localhostのポート経由でリモートホストにSSH接
続すると ~/.ssh/known_hosts ファイルに localhost の host key エントリが
できるが、そのkeyは実際にはリモートホストのものである。複数のリモートホ
ストに対してlocalhostから接続を行なうとkeyの食い違いが生ずる。これを避け
るには ~/.ssh/config に以下のように書いておくと良いだろう。もし、
ポートフォワードで host1, host2 に接続するのであれば、

[~/.ssh/config]

Host host1
 Port			10022
 HostName		localhost
 HostKeyAlias		host1の名前
 UserKnownHostsFile	~/.ssh/host1-key

Host host2
 Port			20022
 HostName		localhost
 HostKeyAlias		host2の名前
 UserKnownHostsFile	~/.ssh/host2-key

などとしておくとkeyの不整合が避けられるだけでなく、接続先ホストにそれぞ
れ host1, host2 と書くだけでポート番号の指定を省略できるようになるので作
業が楽になるだろう。
----------------------------------------------------------------------/

  なお、OpenSSHによるローカルフォワードとリモートフォワードは一度にいく
  つでも定義できるので、POP3とSMTPをローカルフォワードし、SSHポートをリ
  モートフォワードする

	% ssh -L 10110:mercury:110 \
	      -L 10025:mercury:25 \
	      -R 10022:saturn:22 venus

  のような指定も可能だ。また、既にSSHログインしている場合は、ログイン先
  ホストで起動しているコマンドラインでリターンキーを押してから

	~C		(チルダ 大文字C)

  を押すとsshコントロールプロンプトが現れるので、ここにコマンドラインオ
  プションと同じ表記でローカルフォワード/リモートフォワード指定を追加す
  ることも可能となっている。以下の例は、10080番ポートのローカルフォワー
  ドを追加したものである。

	([RET] ~ C を押したあと)
	ssh> -L 10080:mercury:80 [RET]
	     ~~~~~~~~~~~~~~~~~~~

  また、現在フォワードされているポートの一覧は、~# (チルダ・パウンド)を
  押すことで得られる。

●httptunnelを用いたポートフォワード

NATを利用して外部ネットワークに出て行くことすら抑制されている時にも、場
合によってはトンネリングが可能である。
httptunnel(http://www.nocrew.org/software/httptunnel.html)は、名前からも
分かるようにHTTPを通してトンネリングを行なうツールである。

プロトコルにHTTPを使うメリットは、間にプロクシサーバが入っても利用できる
ということである【図 た】。httptunnel では、トンネリングを行ないたい2台
のホストのうちHTTP接続要求を発する方でクライアントプログラム htc を、も
う一方でサーバプログラム hts を起動する。

---[図 た]------------------------------------------------------------\

 NATなどで直接パケットが出て行ける場合
 +------------------+                +-----------------------+
 |                  |		     |     +---->任意ホスト  |
 |                  |		     |     |     任意ポートへ|
 |     [htc]        |		     |   [hts]               |
 |      |||         |	HTTP	     |    |||20022/tcp       |
 |      ||+----------->--->--->--->--->---+|| (例)           |
 |      ||             tunnel              ||                |
 |      |+------>------>-------------------+|                |
 |      |                                   |                |
 |      ++------------>--->--->--->--->------                |
 |                  |(ポート80を使う |                       |
 +------------------+  とは限らない) +-----------------------+


 プロクシ経由でしか外に出られない場合
 +------------------+                +------------------+
 |                  |  8080/tcp	     |     +----->任意ホスト任意ポートへ
 |     [htc]        |  +------+	     |   [hts]          |
 |      |||         |  | Proxy|	     |    || 10022/tcp  |
 |      ||+--------->--->・ ・--->--->--->+|| (例)      |
 |      ||   tunnel    +------+	           ||           |
 |      |+---------------------------------+|           |
 |      +-----------------------------------+           |
 |	            |                |			|
 +------------------+                +------------------+

    Proxyは8080番で、htsは20080番
----------------------------------------------------------------------/

  htcは、自分のところに来たTCPパケットをHTPPリクエストに変換してhtsに送
  る。htsでは受け取ったHTTPリクエストを、最初のパケットの形に復元し、あ
  らかじめ指定した別のポートへフォワードする。どんなパケットもいったん
  HTTPリクエストに変換するのでProxyに中継してもらっても首尾良くhtsに伝わ
  るというわけである。もし、サーバ側のフォワード先をSSH(22)ポートに指定
  すれば、クライアント側がプロクシ経由でしか外に出て行けない環境であっても
  指定したホストにSSHログインできる【註 た】

---[※ た]------------------------------------------------------------\
ただし利用するプロクシサーバプログラムによっては
httptunnelによるパケット変換が利用できないこともある。
----------------------------------------------------------------------/

  ここでは、httptunnelを利用してプロクシを経由したSSH接続を実現してみよ
  う。SSH接続が通れば、通したSSHによるローカルフォワード/リモートフォワー
  ドが利用できるため、さらにいくつか必要なサービスを通すことが容易になる。
  とはいえ当然のことだが、

	→プロクシ経由で
	→パケットを変換し
	→さらに復元して通したトンネル内に
	→SSHトンネルを通してできた
	→ローカルフォワード/リモートフォワードによる接続

  という多段のオーバーヘッドをともなうため、速度が要求されるものには向
  かないことに気をつけて欲しい。
  

・httptunnelのインストール

  http://www.nocrew.org/software/httptunnel.html の "Latest Release" セ
  クションにあるリンクからパッケージを入手しよう。執筆時の安定版は3.0.5, 
  開発版は3.3となっている。ここでは httptunnel-3.3.tar.gz を取得したもの
  として手順を示す。

  ソースアーカイブを展開し configure と make を行なう。

	% tar zxpf httptunnel-3.3.tar.gz
	% cd httptunnel-3.3
	% ./configure
	% make

  root になりインストールする。

	% su
	# make install

  htc, hts コマンドは標準では /usr/local/bin に インストールされる。これ
  を変えたい場合は ./configure に --prefix=インストール先PREFIX を指定す
  る。

・htsの起動(サーバ側)

  サーバとなるホストでは、自由に使えて、外からのアクセスを解放している任
  意のポートを利用して hts を起動する。そして、外部からそのポートにアク
  セスしてきた場合に、そのリクエストをどこにフォワードするかを指定する。
  典型的な場合のhtsの起動は、

	hts -F フォワード先ホスト:フォワード先ポート  ポート番号

  という形式で、「ポート番号」を省略すると8888番を使う。

  ここでは、10080番ポートに来たリクエストを、同じホストのSSHポートにフォ
  ワードする。
  
	venus% hts -F localhost:22 10080	 - - - - - - - -(A)

  これを起動しておきクライアント側からの接続設定をする。

・htcの起動(クライアント側)

  クライアントとなるホストではサーバホストでhtsが待ち受けているポートを
  指定してhtcを起動する。典型的な場合のhtcコマンドの起動は、

	htc [オプション] サーバホスト:サーバポート

  という形式で、サーバポートは省略可能でその場合8888番と見なされる。
  主なオプションには以下のものがある。

	-A USER:PASSWORD	プロクシサーバの認証指定
	-P PROXY:PORT		プロクシサーバホストとポート番号の指定
	-F PORT			フォワードするポート番号

  先述のサーバ側のhts起動例に対応するhtcの起動は以下のようになる。
  サーバホスト名を venus とし、ローカルフォワードするポート番号を20022と
  すると

	saturn% htc -F 20022 venus:10080	 - - - - - - - -(B)

  のようになる。(A)と(B)によりローカルホスト(saturn)側の20022/tcpへの接
  続がリモートホスト(venus)のSSHポートにフォワードされる。プロクシサーバ
  を使いたい場合は

	saturn% htc -F 20022 -P proxyhost:8080 venus:10080

  などとすればよいだろう。ローカルホストの20022番ポートにSSH接続できるこ
  とを確認する。

	saturn% ssh -p 20022 localhost

  ポートフォワードがうまく行っていればリモートホストにログインできる。
  ~/.ssh/known_hosts の host key に関する警告が出る場合は【コラム よ】を
  参考に config を書くとよい。

---[コラム れ]--------------------------------------------------------\
個人的経験にもとづくものだが、筆者が知人と共に試した範囲では、htsは起動
し続けているとクライアントの接続を受け付けなくなってしまうことが多いので、
hts を常時起動しておくのは実用的といいがたい。となると on demandでその都
度起動することになるのだが、「ローカルホストにいながらにして最初にサーバ
側でhtsを起動するのは??」という鶏と卵の関係になる。最初にhtsを起動するキッ
クスクリプトを作って、外部から別の通信手段を介して起動できるようにしてお
くとよい。

たとえば、電子メイル経由で hts 起動スクリプトが走るようにしておくのも一
案だ。qmailを使っているサイトであればリストAのようなスクリプトで、指定した
ポート番号でhtsを起動することができる。

--[リスト A ~/Mail/start-hts]-------------------------------
#!/bin/sh

# ~/.qmail-hts-XXX 経由で呼ばれた場合
# $EXT1 => hts-XXX
# $EXT2 => XXX
# $SENDER => 送信者アドレス
# となる。~/.qmail-hts-default を作れば任意のXXXで呼べる。

admin=admin@example.co.jp		# 通知アドレス
allow=taro@example.co.jp		# 許可者のアドレス

port=$EXT2
pidfile=$HOME/.hts-$port.pid
hts=/usr/local/bin/hts

if [ x"$SENDER" != x"$allow" ]; then	# 許可者でない
  echo "Invalid hts call from $SENDER" | Mail -s hts-start $admin
  exit 0
fi

if [ -f $pidfile ]; then
  pid=`cat $pidfile`
  if ps ux | egrep "^[^ ]+ $pid " > /dev/null; then
    kill $pid
  fi
fi

$hts -F localhost:22 -p $pidfile $port > /dev/null 2>&1
(echo -n "hts started at port $port, pid "
 cat $pidfile ) | Mail -s hts-report $allow 	# 状態を許可者に送り返す
------------------------------------------------------------

--[リスト B ~/.qmail-hts-default]---------------------------
|./Mail/start-hts
------------------------------------------------------------

  qmail以外を利用している場合も procmail などの助けを借りればこうしたス
  クリプトを起動できるだろう。
---------------------------------------------------------------------/


●まとめ

個人の業務の範囲でいえば、離れたところにあるオフィスから必要な情報を取得
したいという場合、いずれかのTCPポートが通っていればそれで足りてしまうケー
スは多いと思われる。たとえば Windows とのファイル交換が必要な場合も 
WinSCP 【註 そ】などを利用すればSSH経由で安全なファイル交換ができること
を考えると、SSHポートだけでも通す意味は大きいといえよう。
---[※ そ]------------------------------------------------------------\
http://winscp.vse.cz/eng/
----------------------------------------------------------------------/

ここまで解説した技術をうまく利用することができれば、Firewallでなるべく安
全側に振り多めにポートを塞いでおいて、個々の業務でポートフォワードを利用
するという設定も可能となる。

%%%% Local Variables:
%%%% buffer-file-coding-system: euc-jp
%%%% End:


yuuji@example.org
Fingerprint16 = FF F9 FF CC E0 FE 5C F7 19 97 28 24 EC 5D 39 BA
HIROSE Yuuji - ASTROLOGY / BIKE / EPO / GUEST BOOK / YaTeX [Tweet]