VPN-Warp

Perl の非同期I/Oモジュール POE を使って VPN-Warp relayagent を書いてみました」に 続いて、 同じく POE を使って VPN-Warp リレー サーバも書いてみました。 これで、オープンソースだけを使って VPN-Warp を実現することができます。

今までも、 BIGLOBE の VPN ワープのページから証明書を取得すれば、 月額 525円で VPN-Warp を試してみることはできたわけですが、 ちょっと試してみたい場合など、 有料であることがネックである感は否めませんでした。 特に、 常日頃からオープンソースを使いこなしている方々だと、 ちょっと使ってみたいだけなのにお金を払うのはねぇ、 と思ってしまうのではないでしょうか。 かくいう私も、 無料「お試し版」のサービスやソフトウェアに慣れきってしまっているので、 試しに使ってみようとする場合に、 それが有料だったりすると、 いきなり億劫になってしまう今日このごろだったりします (^^;)。

というわけで、 オープンソース版 VPN-Warp です。 使い方はあまりフレンドリーではありませんが、 なんたって全て公開してしまっているので、 興味あるかたは、 とことんいじってみてはいかがでしょうか。

relayagent.pl と同様、 今回公開する relayserver.pl も SSL 暗号化/復号の機能を含んでいません。 したがってリレー サーバへの https アクセスを stone などを 通して SSL 復号する必要があります。 例えば stone を

stone -z cert=cert.pem -z key=priv.pem \
      localhost:12345 443/ssl &

などと実行しておき、 relayserver.pl を

relayserver.pl 12345

と実行します。これだけで 443番ポートはリレー サーバとして利用できます。 つまり、relayagent とブラウザからの https 接続を受付けると、 リレー サーバが両セッションを中継し 「ブラウザ → リレー サーバ → relayagent → Webサーバ」 という経路で通信できます。

                      リレー            イントラ         イントラ
ブラウザ ─────→ サーバ ←──── relayagent──→ Webサーバ
            https     443番ポート                        80番ポート

見かけは極めて tiny ですが、 通信プロトコルは本物(?)の VPN-Warp と互換性があるので、 「VPN-Warp relayagent フリー ダウンロード」から ダウンロードできる VPN-Warp relayagent を使うこともできます。

そもそも論で言えば、 リレー サーバの役目は単にデータを右から左へ渡すだけなので、 以下に示すようにその中核の部分は極めてシンプルです。 しかしながら、もちろんこれは KLab(株) で運用しているリレー サーバが単純であることを意味しません。 機能がシンプルでも、大量の同時接続 & 大量データを受付ける耐高負荷性能や、 機器の一部に故障が起きてもサービスが影響を受けない高可用性を実現するために、 様々な工夫を盛り込んでいます。

では、relayserver.pl の中身を順に見ていきましょう。

#!/usr/bin/perl
use POE qw(Component::Server::TCP Filter::Stream);
my $Port = shift;
my $PollID;
my $PollHeap;
my $PollBuf;
my $PollHeader;
my %SID;
my %Heap;
my %Buf;
my $NextSID = 0;

POE::Component::Server::TCP->new
    (
     Port => $Port,
     ClientInput => sub {
	 my ($heap, $input, $id) = @_[HEAP, ARG0, ARG1];
	 if (defined $PollID && $id == $PollID) {
	     $PollHeap = $heap;
	     $PollBuf .= $input;
	     &doPoll;
	 } elsif (defined $SID{$id}) {
	     my $sid = $SID{$id};
	     $Heap{$sid} = $heap;
	     $Buf{$sid} .= $input;
	     &doSession($sid);
	 } elsif ($input =~ m@^GET /KLAB/poll @) {
	     if (defined $PollID) {
		 $heap->{client}->
		     put("HTTP/1.1 503 Service Unavailable\r\n\r\n");
		 $heap->{client}->shutdown_output;
		 return;
	     }
	     $PollID = $id;
	     $PollHeap = $heap;
	     $PollBuf = $input;
	     &doPoll;
	 } else {
	     $SID{$id} = $NextSID;
	     $NextSID = ($NextSID + 1) & 0xFFFF;
	     my $sid = $SID{$id};
	     $Heap{$sid} = $heap;
	     $Buf{$sid} = $input;
	     &doSession($sid);
	 }
     },
     ClientDisconnected => sub {
	 my $heap = $_[HEAP];
	 my $id = $heap->{client}->ID;
	 if (defined $PollID && $id == $PollID) {
	     undef $PollHeap;
	     undef $PollBuf;
	     undef $PollHeader;
	     undef $PollID;
	 } elsif (defined $SID{$id}) {
	     my $sid = $SID{$id};
	     undef $SID{$id};
	     undef $Heap{$sid};
	     undef $Buf{$sid};
	 }
     },
     ClientFilter => POE::Filter::Stream->new(),
    );
POE::Kernel->run;

わずか 70行にも満たないコードですが、 リレー サーバの中核の部分は、ほとんどこれで全てです。 いかに POE (Perl Object Environment) の 記述性が高いか分かりますね。

私は常日頃から プログラマの生産性は、ピンとキリでは 3桁の違いがある と主張しています。 この主張をもう少し詳しく言うと、 その 3桁のうち、プログラマの腕に純粋に依存する部分は 2桁ほどの違いで、 残り 1桁ぶんは解決すべき問題に応じていかに最適な道具を使うかの違い、 ということになります。 最適な道具を使いこなせるもの腕のうち、 ということもできますね。

上記 70行にも満たないコードですが、 実は命令文としてみると、 わずかに 2 つの命令文であることが分かります。 すなわち、

POE::Component::Server::TCP->new(...中略...);
POE::Kernel->run;

ですね。実質「POE::Component::Server::TCP->new(...中略...);」 だけと言ってもいいでしょう。 この命令文は、

POE::Component::Server::TCP->new
    (
     Port => $Port,
     ClientInput => sub {
	 ... クライアントから受信したデータの処理 ...
     },
     ClientDisconnected => sub {
         ... クライアントとの接続が切れたときの処理 ...
     },
     ClientFilter => POE::Filter::Stream->new(),
    );

という構造になっています。 つまり、クライアントからデータが送られて来たときに呼び出されるルーチンと、 クライアントとの接続が切れたときに呼び出されるルーチンを指定しておけば、 あとは POE がうまくやってくれる、というわけです。簡単でしょう?

リレー サーバにとって「クライアント」というと、 ブラウザか relayagent になります。 クライアントからの TCP/IPセッション一本一本に対して POE が ID を割り振っていて、 この ID を見ればどの TCP/IPセッションで送られて来たデータか分かります。

Perl の非同期I/Oモジュール POE を使って VPN-Warp relayagent を書いてみました」で 解説したように、 クライアントから送られて来た最初のデータが 「GET /KLAB/poll 」で始まっていれば、 そのクライアントは relayagent ですから、 以下のようにその ID ($id) を $PollID に代入しておきます。

	 elsif ($input =~ m@^GET /KLAB/poll @) {
	     if (defined $PollID) {
		 $heap->{client}->
		     put("HTTP/1.1 503 Service Unavailable\r\n\r\n");
		 $heap->{client}->shutdown_output;
		 return;
	     }
	     $PollID = $id;
	     $PollHeap = $heap;
	     $PollBuf = $input;
	     &doPoll;
	 }

同じ TCP/IPセッション (つまり $id == $PollID) で 続いて送られてきたデータは、 以下の部分で処理されます。

	 if (defined $PollID && $id == $PollID) {
	     $PollHeap = $heap;
	     $PollBuf .= $input;
	     &doPoll;
	 }

いずれの場合も、受信したデータはいったん $PollBuf に蓄えた上で、 「doPoll」ルーチンを呼び出します。

一方、ブラウザから送られてきたデータの場合は、 以下のようにセッションID ($SID{$id}) を順に割当てていきます。 「セッション」という単語が何度も出てきてややこしいのですが、 $id が POE が各 TCP/IPセッションに割当てた ID で、 各 TCP/IPセッションそれぞれに、 リレー サーバが 16bit の番号を割当てたのが VPN-Warp で言うところのセッションID ($sid = $SID{$id}) です。

	 else {
	     $SID{$id} = $NextSID;
	     $NextSID = ($NextSID + 1) & 0xFFFF;
	     my $sid = $SID{$id};
	     $Heap{$sid} = $heap;
	     $Buf{$sid} = $input;
	     &doSession($sid);
	 }

同じ TCP/IPセッション (つまりセッションID $sid が $SID{$id}) を通して 続いて送られてきたデータは、 以下の部分で処理されます。

	 elsif (defined $SID{$id}) {
	     my $sid = $SID{$id};
	     $Heap{$sid} = $heap;
	     $Buf{$sid} .= $input;
	     &doSession($sid);
	 }

いずれの場合も、受信したデータはいったん $Buf{$sid} に蓄えた上で、 「doSession」ルーチンを呼び出します。

つまり、relayagent から受信したデータは doPoll ルーチンで、 ブラウザから受信したデータは doSession ルーチンで、 それぞれ処理する、というわけです。 以下の図に示すように、 リレー サーバの役割は、 relayagent から受信した (ブロック化された) データを、 (ブロックを開梱しつつ) ブラウザへ送信し、 またブラウザから受信したデータを、 ブロック化して relayagent へ送ることですから、 doPoll および doSession が何をするためのルーチンか予想できますよね?

VPN-Warp セッション

まず doPoll を見ていきましょう。

sub doPoll {
    do {
	if (! defined $PollHeader) {
	    if ($PollBuf =~ /\r\n\r\n/) {
		$PollHeader = `;
		$PollBuf = ';
		$PollHeap->{client}->put("HTTP/1.1 200 OK\r\n\r\n");
	    }
	}
	return unless defined $PollHeader;

リクエストヘッダを全て読み込んでいない場合 (つまり $PollBuff に空行 \r\n\r\n が含まれていない場合) は、ここで終わりです。
$PollBuf に受信データが追加されて、ふたたび doPoll が呼ばれるまで待ちます。

リクエストヘッダを全て読み込んだ場合は、 $PollBuf からリクエストヘッダ部分を削除した上で、 次に進みます。

	my ($sid, $len, $data) = unpack("nna*", $PollBuf);
	return unless defined $sid && defined $len && $len ne "";

ブロック全体を読み込めていない場合は、ここで終わりです。 $PollBuf に受信データが追加されて、ふたたび doPoll が呼ばれるまで待ちます。 「ブロック」というのは VPN-Warp 用語で、 relayagent とリレーサーバとの通信は、 基本的にこの「ブロック」を単位にして行ないます。 ブロックは次のような可変長のデータです。

    ┌───┬───┬───┬───┬───┬─≪─┬───┐
    │セッションID│ データ長  │  可変長データ   │
    └───┴───┴───┴───┴───┴─≫─┴───┘
          2バイト         2バイト      「データ長」バイト

「セッションID」および「データ長」は、ビッグエンディアンです。 つまり上位バイトが先に来ます。 したがって、上記コードによって $sid, $len, $data にそれぞれ 「セッションID」「データ長」「可変長データ」が代入されます。

なお、データ長が 0 ないし負数の場合は、 「可変長データ」の部分は 0 バイトになります。 このような「可変長データ」がないブロックは、 コントロール用のブロックで、 EOF や Error などのイベントを伝えます。

	if ($len > 32767) {
	    $len -= 65536;
	    $PollBuf = $data;
	    if ($len == -1) {
		&doShutdown($sid);
	    }
	}

$len == -1 のときは、Error を伝えるコントロール ブロックなので、 「doShutdown」ルーチンを呼び出しています。

	elsif ($len > 0) {
	    return unless defined $data && length($data) >= $len;
	    ($data, $PollBuf) = unpack "a${len}a*", $data;
	    if (defined $Heap{$sid}) {
		$Heap{$sid}->{client}->put($data);
	    }
	}

$len > 0 のときは、 $sid で示されるブラウザに対して $data を送信します。 $len == 0 のときは、 EOF を伝えるコントロール ブロックなので、 「doShutdown」ルーチンを呼び出しています。

	else {	# len == 0
	    $PollBuf = $data;
	    &doShutdown($sid);
	}
    } while ($PollBuf ne "");
}

以上を、$PollBuf が空になるまで続けます。

doShutdown はブラウザとの TCP/IPセッションを shutdown するためのルーチンです。

sub doShutdown {
    my ($sid) = @_;
    if (defined $Heap{$sid}) {
	$Heap{$sid}->{client}->shutdown_input;
    }
}

次に doSession です。

sub doSession {
    my ($sid) = @_;
    if (defined $PollHeap) {
	my $req = $Buf{$sid};
	$Buf{$sid} = "";
	for my $block (unpack "(a2048)*", $req) {
	    $PollHeap->{client}->
		put(pack("nna*", $sid, length($block), $block));
	}
    }
}

ブラウザから送られてきたデータを、 2048 バイトずつ区切って「セッションID」「データ長」を 前につけることによってブロック化して、 relayagent に送信しています。

オリジナルの VPN-Warp を使ったことがある方は既にお気付きかも知れませんが、 上記 relayserver.pl は説明を簡単にするために機能をいくつか省いています。 例えば、オリジナルのリレー サーバは、 接続する際はクライアント認証が必須で、 同じクライアント証明書を提示した relayagent とブラウザを 結び付ける機能があるのですが、 上記 relayserver.pl はクライアント認証を行なわないので、 任意のブラウザから接続可能ですし、 同時接続が可能な relayagent は一つだけです。

腕に覚えのあるかたは、 オリジナルの VPN-Warp と同等の機能を実現するには どのような修正を加えればよいか、 考えてみてはいかがでしょうか? そして、 こういうことを考えることが好きなかた、 「いっしょにDSASつくりませんか?


hiroaki_sengoku at 06:36|この記事のURLComments(0)TrackBack(0)

多数の TCP/IP セッションを同時に維持する必要性などから、 非同期I/O が最近流行りのようです。 何をいまさら、という気もするのですが、 いわゆる「最新技術」の多くが 30年前の技術の焼き直しに過ぎない今日このごろなので、 非同期I/O 技術が「再発見」されるのも、 「歴史は繰り返す」の一環なのでしょう。 スレッドが当たり前の時代になってからコンピュータ技術を学んだ人にとっては、 (古めかしい) 非同期I/O が新鮮に映るのかも知れず、 なんだか「ファッションのリバイバル」に似ていますね。

Perl で非同期I/O 処理を手軽に行なうための枠組みとして、 POE: Perl Object Environment というものが あるようです。 POE を使うと、 あたかもスレッドを使っているような手軽さでプログラミングできます。 試しに VPN-Warp の relayagent を POE を使って書いてみました。 オリジナルの relayagent は C 言語で記述した 4000 行を超える プログラムなのですが、 Perl だと 200 行以下で一通り動くものが書けてしまいました (もちろん C 版の機能を全て実装したわけではありません)。

POE を触るのは今回が初めてだったので、 マニュアルをいちいち参照しながら書いたのですが、 なにせわずか 200 行ですから、 開発はデバッグ込みで 1 日かかりませんでした。 改めて Perl の記述性の良さと開発効率の高さに感動したのですが、 これだけ簡潔に書けてしまうと、 relayagent の機能を解説するときの教材としても使えそうです。

というわけで、 今までブラックボックスだった relayagent の中身の解説を試みたいと思います。 これから POE を使ってみようとする人の参考にもなれば幸いです。

VPN-Warp の relayagent とは、 以下の図のようにリレーサーバと Webサーバの両方へ接続して、 リレーサーバから受取ったリクエストを Webサーバへ中継するプログラムです。 http リクエストを受取ってサービスを行なうのですから、 サーバの一種と言えますが、 外部から接続を受付けるわけではなく、 リレーサーバと Webサーバの両方に対してクライアントとして振る舞う点が ユニークと言えるでしょう。

                      リレー            イントラ         イントラ
ブラウザ ─────→ サーバ ←──── relayagent──→ Webサーバ
            https     443番ポート                        80番ポート

http リクエストを受取って Webサーバへ中継するプログラムというと、 proxy サーバを思い浮かべるかも知れません。 proxy サーバはその名の通り、 ブラウザに対してはサーバとして振る舞います:

                                        proxy            イントラ
ブラウザ ──────────────→ サーバ────→ Webサーバ
                                        8080番ポート     80番ポート

proxy サーバが、ブラウザからの接続を受付けて、 それを Webサーバに中継するのに対し、 relayagent は自身では接続を受付けずに中継する、 という違いがお分かりでしょうか? relayagent は接続を受ける必要がないため、 ファイアウォールの内側など、 外部からアクセスできない場所で使うことが可能になっています。

なお、C 版の relayagent はリレーサーバに対して https で接続するのですが、 Perl 版 relayagent (以下 relayagent.pl) は、 説明の都合上 SSL 暗号化の機能を含んでいません。 実際に使うときは、 stone などで SSL 暗号化して リレーサーバに接続する必要があります。

         リレー                         イントラ         イントラ
         サーバ ←──── stone ←── relayagent──→ Webサーバ
         443番ポート       SSL化        Perl 版          80番ポート

例えば stone を

stone -q pfx=relay,5000005.pfx \
      -q passfile=relay,5000005-pass.txt \
      warp.klab.org:443/ssl localhost:12345 &

などと実行しておき、 relayagent.pl はリレーサーバに接続する代わりに、 localhost の 12345 番に接続します。

では、relayagent.pl を順に見ていきましょう。

#!/usr/bin/perl
use POE qw(Component::Client::TCP Filter::Stream);
my $IdleTimerMax = 6;	# 60 sec
&help unless @ARGV == 2;
&help unless shift =~ m/^(\w+):(\d+)$/;
my ($RelayHost, $RelayPort) = ($1, $2);
&help unless shift =~ m/^(\w+):(\d+)$/;
my ($WebHost, $WebPort) = ($1, $2);
my %WebHeap;
my $PollBuf;
my $PollHeap;
my $PollHeader;
my $IdleTimer;
my $DisconectTime = 0;

$RelayHost, $RelayPort は、 リレーサーバのホスト名とポート番号ですが、
前述したように stone 経由でリレーサーバにつなぐために、
$RelayHost = "localhost", $RelayPort = 12345 などとなります。また、 $WebHost, $WebPort は、 中継先となる (イントラの) Webサーバのホスト名とポート番号です。

続いて、リレーサーバへ接続する (直接の接続先は SSL 化を行なう stone ですが、 煩雑になるので以下 「リレーサーバ」 と略記します) ためのコードです:

POE::Component::Client::TCP->new
    ( RemoteAddress => $RelayHost,
      RemotePort    => $RelayPort,
      Connected     => sub {
	  $PollHeap = $_[HEAP];
	  undef $PollHeader;
	  $PollBuf = "";
	  $IdleTimer = $IdleTimerMax;
	  $PollHeap->{server}->
	      put("GET /KLAB/poll HTTP/1.1\r\nX-Ver: realyagent.pl 0.01\r\n\r\n");
      },
      ServerInput   => sub {
	  $PollHeap = $_[HEAP];
	  $PollBuf .= $_[ARG0];
	  &doPoll;
      },
      Filter        => POE::Filter::Stream->new(),
      Disconnected  => \&reconnectPoll,
    );

POE では、非同期に動く処理を、 処理ごとに分けて書くことができます。 各処理のことを「POEセッション」と呼びます。

上記は、リレーサーバへ接続する POEセッションの生成です。 接続先ホストおよびポートを、 それぞれ $RelayHost と $RelayPort に設定しています。

「Connected => sub {」から始まる部分が、 接続に成功したときに実行するコードです。 細かいところはさておき、 接続したら以下のリクエストをリレーサーバに送る、 という点は読み取れるのではないでしょうか。

GET /KLAB/poll HTTP/1.1
X-Ver: realyagent.pl 0.01

同様に、 「ServerInput => sub {」から始まる部分が、 通信相手 (リレーサーバ) からデータを受信したときに実行するコードです。 受信したデータは、 いったん変数 $PollBuf に溜めておいて、 続いて呼び出す doPoll の中で処理を行ないます。

以上からお分かりのように、 リレーサーバへデータを送るときは、
「$PollHeap->{server}->put(送るべきデータ);」を実行し、 リレーサーバからデータが送られてきた時は、 doPoll で受取ります。 とても見通しが良いですね。

各 POEセッションは、スレッドと同様、同一メモリ空間を共有しているので、 他の POEセッションが変更した変数の値を参照できます。 したがってどの POEセッションでもリレーサーバへデータを送ることができますし、 リレーサーバから受信したデータはどの POEセッションでも読むことができます。

続いて、もう一つ POEセッションを作ります。

POE::Session->create
    ( inline_states =>
      { _start => sub {
	  $_[KERNEL]->delay( tick => 10 );
        },
        tick => sub {
	    if ($IdleTimer > 0) {
		if (--$IdleTimer <= 0) {
		    &sendControl(0, -2);	# keep alive
		}
	    }
            $_[KERNEL]->delay( tick => 10 );
        },
      },
    );
$poe_kernel->run;
exit;

この POEセッションは 10秒に一回、 「tick => sub {」から始まる部分を実行します。 見ての通り、$IdleTimer の値を減らしていって、 0 になったら sendControl を実行します。 $IdleTimer は最初 6 ($IdleTimerMax) に設定されるので、 1 分ごとに sendControl を実行する、という意味ですね。

以上 2つの POEセッションは作成しただけで、まだ走り出していません。
その次の「$poe_kernel->run;」が各 POEセッションを走らせるための呼び出しです。 このルーチンは全ての POEセッションが終了するまで返ってきません。

さて、relayagent はリレーサーバとの接続を常時維持していますが、 無通信時間が続くと (通信経路中にあるファイアウォールなどに) 切られてしまう恐れがあるので、 keep alive ブロックを送信しています。 通信が行なわれていない時間を測るためのカウンタが $IdleTimer というわけです。

通信が行なわれない限り $IdleTimer は減り続け、 1 分経過すると sendControl(0, -2) を呼び出して keep alive ブロックを送信します。 sendControl はこんな感じ:

sub sendControl {
    my ($id, $control) = @_;
    $control += 65536 if $control < 0;
    $IdleTimer = $IdleTimerMax;
    if (defined $PollHeap && $PollHeap->{connected}) {
	$PollHeap->{server}->put(pack("nn", $id, $control));
    }
}

既に説明したように「$PollHeap->{server}->put(データ)」は、 リレーサーバにデータを送る呼び出しですから、 「pack("nn", 0, 65534)」が keep alive ブロックであることが分かります。

「ブロック」というのは VPN-Warp 用語でして、 relayagent とリレーサーバとの通信は、 基本的にこの「ブロック」を単位にして行ないます。 ブロックは次のような可変長のデータです。

    ┌───┬───┬───┬───┬───┬─≪─┬───┐
    │セッションID│ データ長  │  可変長データ   │
    └───┴───┴───┴───┴───┴─≫─┴───┘
          2バイト         2バイト      「データ長」バイト

「セッションID」および「データ長」は、ビッグエンディアンです。 つまり上位バイトが先に来ます。 データ長が 0 ないし負数の場合は、 「可変長データ」の部分は 0 バイトになります。

データ長が 0 ないし負数であるブロックは、 コントロール用のブロックで、 以下の意味を持っています:

データ長意味内容
0EOFWebセッションの終了を要求
-1ErrorWebセッションの異常終了を要求
-2Keep Alive無通信状態が続いたときに送信
-3X OFFWebセッションのデータ送信の一時停止を要求
-4X ONWebセッションのデータ送信の再開を要求

ブラウザ送ったリクエストを Webサーバに届け、 Webサーバのレスポンスをブラウザに返す一連の通信のことを、 ここでは「Webセッション」と呼ぶことにします。 つまり、 VPN-Warp が提供する仮想的な通信路 (トンネル) 上のセッションです。

VPN-Warp セッション

ブラウザがリレーサーバと通信するときの TCP/IPセッションと、 relayagent と Webサーバが通信するときの TCP/IPセッションを対応づけるのが、 セッションID です。 「セッション」という言葉が何度も出てきてややこしいですが、 「セッションID」の「セッション」は、 「Webセッション」の意味です。

リレーサーバと relayagent との間は、 複数の Webセッションを一本の TCP/IPセッションに相乗りさせるので、 そのとき各 Webセッションがこんがらないようにするために ブロックにはセッションID がつけられている、というわけです。

では、次はいよいよ relayagent の中核ルーチンである doPoll です:

sub doPoll {
    do {
	if (! defined $PollHeader) {
	    if ($PollBuf =~ /\r\n\r\n/) {
		$PollHeader = $`;
		$PollBuf = $';
	    }
	}
	return unless defined $PollHeader;
	my ($id, $len, $data) = unpack("nna*", $PollBuf);
	return unless defined $id && defined $len && $len ne "";
	if ($len > 32767) {
	    $len -= 65536;
	    $PollBuf = $data;
	    if ($len == -1) {
		&closeWeb($id);
	    }
	} elsif ($len > 0) {
	    return unless defined $data && length($data) >= $len;
	    ($data, $PollBuf) = unpack "a${len}a*", $data;
	    &reqWeb($id, $data);
	} else {	# len == 0
	    $PollBuf = $data;
	    &closeWeb($id);
	}
    } while ($PollBuf);
}

前述したように、relayagent はリレーサーバに接続したとき、 まず
「GET /KLAB/poll HTTP/1.1」から始まるリクエストヘッダを送ります。 するとリレーサーバは、 次のようなレスポンスを返します:

HTTP/1.1 200 OK
X-Customer: nusers=5&type=1&expire=1169696110&digest=3f6977eceb8c2c43e28e6026b08ba900

そしてこの後 (doPoll において「defined $PollHeader」が真のとき)、 リレーサーバと relayagent は、 前述したブロックを送受信することになります。

「my ($id, $len, $data) = unpack("nna*", $PollBuf);」の部分が、
リレーサーバから受信したブロックを、
「セッションID ($id)」 「データ長 ($len)」 「可変長データ ($data)」 に分解している処理ですね。 続いてブロックの処理が行なわれますが、 コントロールブロックに関する処理は割愛して、 可変長データが付いているブロックの処理を見ていきましょう。 ここで受信した可変長データは、 ブラウザが送信した http リクエストを 2048バイトごとに分割したものです。

つまりリレーサーバは、 ブラウザから https リクエストを受取るたびに「セッションID」を割り振ります。 そして、リクエストをブロックに分割して relayagent へ送信し、 逆に relayagent から受取ったブロックを 同じセッションID ごとに連結して、 http レスポンスとしてブラウザへ送信します。

したがって、 relayagent はリレーサーバから受取ったブロックを 同じセッションID ごとに連結して Webサーバへ中継し、 そのレスポンスをブロックに分割してリレーサーバへ送信すればよいことになります。

同じセッションID ごとに連結して Webサーバへ送信する処理が、 reqWeb です:

sub reqWeb {
    my ($id, $req) = @_;
    if (defined $WebHeap{$id} && $WebHeap{$id}->{connected}) {
	$WebHeap{$id}->{server}->put($req);
    } else {
	POE::Component::Client::TCP->new
	    ( RemoteAddress => $WebHost,
	      RemotePort    => $WebPort,
	      Connected     => sub {
		  $WebHeap{$id} = $_[HEAP];
		  $WebHeap{$id}->{server}->put($req);
	      },
	      ServerInput   => sub {
		  $WebHeap{$id} = $_[HEAP];
		  &sendRes($id, $_[ARG0]);
	      },
	      Filter        => POE::Filter::Stream->new(),
	      Disconnected  => sub {
		  &sendControl($id, 0);
	      },
	    );
    }
}

「POE::Component::Client::TCP->new」によって、 Webサーバと通信するための POEセッションを生成しています。 この reqWeb を実行しているのは、 リレーサーバとの通信を受け持つ POEセッションでしたが、 この POEセッションが新たに POEセッションを生成している点に注意してください。

新しく生成した POEセッションは、Webサーバと接続したとき (Connected)、
「$WebHeap{$id}->{server}->put($req);」を実行して リクエスト ($req) を Webサーバに送信します。 そして Webサーバからレスポンスを受信したとき (ServerInput)、 sendRes を実行します。

sub sendRes {
    my ($id, $res) = @_;
    $IdleTimer = $IdleTimerMax;
    if (defined $PollHeap && $PollHeap->{connected}) {
	for my $block (unpack "(a2048)*", $res) {
	    $PollHeap->{server}->
		put(pack("nna*", $id, length($block), $block));
	}
    }
}

sendRes は Webサーバからのレスポンス ($res) を 2048バイトごとに分割し、 セッションID ($id) とデータ長 (length($block)) を付加した ブロックとしてリレーサーバに送信します。

以上をまとめたのが、relayagent スクリプト です。 ここで解説した機能の他、 http リクエストヘッダの Host: フィールドを書き換える機能も追加しています。

C 版の relayagent に比べると、 http レスポンスの書き換え機能や、 http 以外のプロトコルを通す機能などがない点や、 高負荷時の性能の検証が充分行なえていない点など、 そのまま実運用に使用するには難しい点もありそうですが、 少なくとも プロトタイピングなどの目的 (あるいは教育などの目的) ならば 充分使えそうです。


hiroaki_sengoku at 07:13|この記事のURLComments(0)TrackBack(0)

La Fonera (FON ソーシャルルータ) って知っていますか?

FONは世界最大のWiFiコミュニティです。 誰もが「世界中どこからでもインターネットに無料で接続したい!」という 望みを持っているはずです。 そのようなメンバーが助け合ってWiFiを広めて行こう!ということを コンセプトに私たちは活動しています。 元々簡単なアイディアで始まったFONコミュニティ。 メンバーが作るWiFiインフラを用いて、 WiFiを世界中のどこからでも楽しめるようにしましょう!。
参加は簡単!FON取り扱い店でLa Foneraを購入して接続してスタートするだけ!

La Fonera は、FON のアクセスポイントであると同時に、 普通の (プライベートな) 無線LAN アクセスポイントとしても利用できます。 自宅などで無線LAN アクセスポイントを設置している人は多いと思いますが、 おそらくそのまま La Fonera で置き換えることが可能でしょう。

実際、私はそれまで使ってた 無線LAN ルータ WN-G54/R2 を La Fonera で置き換えてしまいました。 La Fonera が提供する二つのアクセスポイントのうち、 プライベート側を使えば、 自宅LAN に普通にアクセスできて、 いままで使っていた無線LAN ルータに比べて全く遜色ありません。 もちろん WPA2 (Wi-Fi Protected Access 2) 暗号化方式が使えます。

より正確に言うと、 La Fonera のプライベート側アクセスポイント (MyPlace) は、 自宅LAN とは異なるセグメントになります。 有線LAN と無線LAN 相互で自由に通信するためには、 多少の設定変更 (/etc/firewall.user に 2行ほど追加) が必要になります。

私の場合 WPA に対応していない古い無線LAN 端末 (Windows98マシン^^;) も 持っていて、 自宅の無線LAN を WEP から WPA2 に変更した (自宅LAN といえど、WEP を使うのはちょっと抵抗がありますよね?) 時点で、 お蔵入りにしていました。 La Fonera が提供するもう一つのアクセスポイント (パブリック側) を使えば、 直接自宅LAN へはアクセスできないもののインターネットへは接続できるし、 インターネット経由で自宅LAN に戻ってくる (もちろんインターネットからアクセス可能な部分に限定されますが) ことも可能なので、 La Fonera の導入によって古い無線LAN 端末も再び利用できるようになった、 というオマケがつきました。

さて、 この La Fonera で VPN-Warp が利用可能になったらどうなるでしょうか? 無線LAN ルータが VPN ゲートウェイの機能も持つわけです。 つまりインターネットに接続できる環境ならどこからでも、 自宅LAN へ手軽にアクセスできるようになります。

ここで La Fonera は固定 IP アドレスを持つ必要がないばかりか、 そもそもグローバルアドレスを持つ必要性すらない、 という点がミソです。 必要なことは、La Fonera からインターネットへ接続可能、ということだけです。 La Fonera をどこに設置しようと、 その設置した LAN に外部からアクセスすることができます。

以下は、La Fonera で VPN-Warp を使うようにするための手順です。 現状は多少の Linux の知識が必要となってしまうのですが、 要は relayagent プログラムを La Fonera にインストールするだけのことなので、 適切なインストーラさえ作ればいくらでも簡単な手順になることでしょう。 なので、難しそうだとあきらめるのではなく、 関心がある方はご連絡頂ければと思います。

まず La Fonera に ssh あるいはシリアルコンソールで ログインすることが必要となります。 おそらくこれが最大の難関でしょう (^^;)。

senri:/home/sengoku % ssh fonera
root@fonera's password:


BusyBox v1.1.3 (2006.11.21-19:49+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

 _______  _______  _______
|   ____||       ||   _   |
|   ____||   -   ||  | |  |
|   |    |_______||__| |__|
|___|

 Fonera Firmware (Version 0.7.1 rev 2) -------------
  *
  * Based on OpenWrt - http://openwrt.org
  * Powered by FON - http://www.fon.com
 ---------------------------------------------------
root@OpenWrt:~#

ログインさえ可能なら、あとはさほど難しくはありません。 まずネットワーク経由で La Fonera に relayagent を インストールするための準備をします。 具体的には、 以下のように /etc/ipkg.conf に「src gcd http://www.gcd.org/fonera」 を追加し、 「ipkg update」 を実行します。

root@OpenWrt:~# echo "src gcd http://www.gcd.org/fonera" >> /etc/ipkg.conf
root@OpenWrt:~# ipkg update
Downloading http://download.fon.com/release/fonera/0.7/packages/Packages
Updated list of available packages in /usr/lib/ipkg/lists/fon
Downloading http://www.gcd.org/fonera/Packages
Updated list of available packages in /usr/lib/ipkg/lists/gcd
Done.
root@OpenWrt:~#

http://www.gcd.org/fonera というのは、 私が最近始めた La Fonera 用の ipkg feed です。 つまり、上記のような設定をしておくと、 La Fonera にいろいろなソフトウェアを ネットワーク経由でインストールすることができるようになります。 もちろん ;-) VPN-Warp の relayagent も、 ここからインストールできます。 インストールに必要なコマンドは、 「ipkg install relayagent」だけです。 このコマンドを打ち込むだけで、 relayagent の実行に必要な OpenSSL ライブラリ等が 自動的にインストールされます。

root@OpenWrt:~# ipkg install relayagent
Installing relayagent (1.0.7) to root...
Downloading http://www.gcd.org/fonera/relayagent_1.0.7_mips.ipk
Installing libopenssl (0.9.8d-1) to root...
Downloading http://www.gcd.org/fonera/libopenssl_0.9.8d-1_mips.ipk
Installing zlib (1.2.3-3) to root...
Downloading http://www.gcd.org/fonera/zlib_1.2.3-3_mips.ipk
Configuring libopenssl
Configuring relayagent
Configuring zlib
Done.
root@OpenWrt:~#

次に、VPN-Warp の証明書をインストールします。 BIGLOBE の VPN ワープのページ などから入手した証明書とそのパスワードを記したファイルを、 La Fonera の /etc/warp ディレクトリへコピーしてください。 以下の実行例では relay,5000005.pfx が証明書のファイル、 relay,5000005-pass.txt がパスワードを記したファイルです。 5000005 というのは私が使用している証明書の番号なので、 実際に取得した証明書の番号で読み替えてください。

senri:/home/sengoku % echo "パスワード" > relay,5000005-pass.txt
senri:/home/sengoku % scp -p relay,5000005.pfx relay,5000005-pass.txt fonera:/etc/warp/
root@fonera's password: 
relay,5000005.pfx                             100% 4856     4.7KB/s   00:00
relay,5000005-pass.txt                        100%    9     0.0KB/s   00:00

次が最後の難関で、 VPN-Warp の設定ファイルを作成します。 /etc/warp/relayagent.cfg.sample に設定ファイルのサンプルがあるので、 これを /etc/warp/relayagent.cfg へコピーして、 vi エディタで編集します。

root@OpenWrt:~# cd /etc/warp/
root@OpenWrt:/etc/warp# ls -l
-rw-------    1 root     root            9 Jan  9 04:09 relay,5000005-pass.txt
-rw-------    1 root     root         4856 Jan  9 04:09 relay,5000005.pfx
-rw-r--r--    1 root     root         3290 Jan  9 07:47 relayagent.cfg.sample
root@OpenWrt:/etc/warp# cp relayagent.cfg.sample relayagent.cfg
root@OpenWrt:/etc/warp# vi relayagent.cfg

設定ファイル relayagent.cfg の中に、以下のような部分があるので、
「relay,0000000」の数字の部分を実際に取得した証明書
(私の場合は「relay,5000005」) の番号で置き換えます。

#--------------------------------------------------------------------
#
# -x  PFX 形式 クライアント証明書を指定
# -X  同パスワードファイルを指定
#
#--------------------------------------------------------------------

-x "/etc/warp/relay,0000000.pfx"
-X "/etc/warp/relay,0000000-pass.txt"

以上で設定ファイルの作成が完了しました。 あとは La Fonera を再起動する (裏面のリセットスイッチを押す) だけです。 再起動する代わりに、起動スクリプトを実行することによって relayagent を起動することもできます。

root@OpenWrt:~# /etc/init.d/N50relayagent start
root@OpenWrt:~#

では、ブラウザで https://warp.klab.org へアクセスしてみましょう。 La Fonera にインストールした証明書と同じ証明書、あるいは その子証明書を使ってアクセスしてください。 La Fonera の Web 設定ページが表示されたら成功です。

Web 設定ページが表示されるのは、 先ほど作成した設定ファイル /etc/warp/relayagent.cfg に、

#--------------------------------------------------------------------
#
# <relay サーバ名:ポート番号>  <転送先ホスト名:ポート番号>
#
# ※-p オプション指定時は下記の意味となる
#
# <プロキシサーバ名:ポート番号> <転送先ホスト名:ポート番号>
#
#--------------------------------------------------------------------

warp.klab.org:443 localhost:80

と書いてあるからです。 「localhost:80」だから La Fonera 内蔵の WWW サーバ (つまり設定ページ) ですね。 「localhost:80」の部分を、 自宅LAN 内の適当なサーバの「アドレス:ポート」で置き換えれば、 そのサーバにアクセスできますし、 通常の VPN-Warp と全く同様に、 任意のサーバに接続するように設定することもできます。


hiroaki_sengoku at 07:02|この記事のURLComments(0)TrackBack(1)

何をいまさら、という声が聞こえてきそうですが、 社内からの情報漏洩が問題となっている今だからこそ、 あらためて社内情報を扱うサーバを、 社内LAN に置くことの重要性を訴えたいと思います。

もともと、グループウェアといえば社内のサーバに置くのが当たり前だったのですが、 いろいろな情報がグループウェアに蓄積されるにつれ、 社内からだけでなく、社外にいるときもグループウェアにアクセスしたい、 というニーズが高まってきました。

グループウェアを社外からも利用するには、 サーバをどこに置くかで分類すると、 次の 3 通りの方法があります。

  1. 社内LAN に置く
    ファイアウォールに穴をあけて社内のサーバへアクセスできるようにするか、 あるいは VPN などの方法で社内からのアクセスを社内へ導く方法です。 正規のアクセス以外の、招かざる客を確実に排除できなければなりません。
  2. DMZ 上に置く
    社外からもアクセスできる非武装地帯 (DMZ) にサーバを置く方法です。 ファイアウォールで守られない場所に置くのですから、 サーバは自分自身を守ることができなければなりません。
  3. 社外に置く
    他社が提供するグループウェアASP サービスを利用する方法です。 情報管理を他人任せにできるので一番手間がかかりませんが、 万一情報漏洩が起きたときに誰が責任をどうやって取るのか、 契約等ではっきり決めておかないとトラブルの種となるでしょう。

いずれも一長一短があるので、いちがいにどれがいいとは言えませんが、 それぞれどのようなリスクがあるかきちんと把握した上で利用することが重要ですね。

手軽さだけで言えば、もちろん 方法3 が一番簡単なのですが、 手軽なだけにリスク管理が出来ていない人が利用すると、 情報のダダ漏れが起きます。

【警告】Googleカレンダーで情報流出? から引用:

無防備な人が多いには驚きます。 Googleカレンダーで、 まるでソーシャルブックマークみたいに公開設定をしている人が何人もいます。 実に詳細な仕事のスケジュールが公の場にさらされており

ということも実際に起きているようです。

じゃ、公開設定しなければ安心か、というとそうとも言えません。 別に ASP サービスを提供している会社を信用しないわけではないのですが、 サーバに情報をため込めば、サーバの運用管理を行なう過程で、 どうしたってその情報が漏れるリスクはあります。 万一漏れたときに ASP サービス提供会社がどう責任をとってくれるのか、 確認しておきたいところです。

他人任せにするのは、どうも気持ち悪い、という場合は 方法1 あるいは 方法2 を選択することになります。 方法2 は、ASP サービス提供会社が行なっているのと同等のことを 自社で行なうイメージですね。 自社で運用するので、情報漏洩が起きたときの責任の所在ははっきりしていて いいのですが、 どうやって外部からの攻撃を防ぐかが問題となります。

インターネット上でサーバを安全に運用することが困難だからこそ、 各種 ASP サービスが提供されているわけで、 きちんとしたサーバ運用管理体制を整えずに運用したりすると、 あらゆる攻撃を受けて侵入されてしまうかも知れません。 また、サーバそのものは運用できていても、 グループウェアの側に脆弱性があって、漏洩事故が起きてしまうケースもあります。 そもそも、きちんとサーバを運用できるだけの体制を整えられるのなら、 ASP サービスの提供側になったほうがよさそうです。

と、いうわけで結論は、方法1 の「グループウェア・サーバを社内に置こう!」です。 この方法のいいところは、社内で使っているグループウェア・サーバを、 変更なしにそのまま使えるという点で、 気をつけなければならないのは、正規のアクセス以外の不正侵入を どうやって確実に排除するか、という点です。

社内で使っているグループウェア・サーバをそのまま使うわけですから、 何らかの脆弱性があることを前提としなければなりません。 脆弱性がないなら DMZ に出しておけるわけで、 社内に置く最大のメリットは、多少の脆弱性は許容できるという点にあります。 だから、 正規ユーザ以外からのアクセスは決して、 社内サーバに到達できないようにしなければなりません。

では、どうやって不正アクセスを排除すればいいのでしょうか? ファイアウォールに穴を開ける方法にせよ、 VPN を使う方法にせよ、 何らかの「入口」を設置して、 そこで正規アクセスと不正アクセスを選別することになりますが、 この「入口」を適切に運用管理することは結構大変です。 不正アクセスを試みる人は、なんとかこの「入口」をだまして 通ろうとするわけで、 「入口」をきちんと運用管理する体制を整えなければなりません。

あれ? 結局これでは 方法2 と同じですね?

そこで、「VPNワープ」です (我田引水... ^^;)。 VPNワープは、この「入口」を提供する ASP サービスなんです。 グループウェア・サーバ自体は社内に置いて情報漏洩のリスクを抑えつつ (方法1 の長所)、 「入口」の運用管理だけ他社 (つまり KLab) 任せにして手軽さも確保する (方法3 の長所)、 方法1 と 方法3 のいいとこどりの「入口ASP」、それが VPNワープです。

今月末まで無料スタートキャンペーン中ですので、 ぜひこの「入口ASP」をお試し下さい。 BIGLOBE会員のかたは、「エージェント」と「SSL証明書」を ダウンロードするだけで利用できますし、 BIGLOBE会員でないかたも、 初期費用・月額費用が無料の 「コンテンツ」コースに入会すれば、 すぐ VPNワープをお試し頂くことが可能です。


hiroaki_sengoku at 13:56|この記事のURLComments(1)TrackBack(0)

昨日、NECビッグローブ株式会社と 共同で、BIGLOBE法人会員および個人会員向けに 「VPNワープ」の提供を 開始しました。 これまで VPN-Warp 入門編 6回、応用編 3回を書き綴ってきましたが、 「VPNワープ」サービス 開始により、 より多くの方に手軽に VPN-Warp を使って頂けるようになったと思います。

なお BIGLOBE会員でないかたも 初期費用・月額費用が無料の 「コンテンツ」コースに入会すれば、 VPNワープをご利用頂くことが可能です。

「設定10分ですぐ使える SSL VPN」を目指して、 「VPNワープ」で提供するリレー・エージェントは、 relayagent フリーダウンロード で公開しているものより、 さらに簡単な「簡易インストール方式」を採用しています。 10分で設定できるかは、もちろん個人差があるとは思いますが (^^;)、

  1. VPNワープのページの 管理者メニューの「購入」を選ぶ
    (現在、無料スタートキャンペーン実施中につき、10月末まで無料)
    指示にしたがって進んでいくと、 証明書 relay,4000017.pfx (数字の部分はユーザごとに異なります) が ダウンロードできるので、これをダブルクリックしてインポートする
  2. 「リレー・エージェント」インストール用プログラムをダウンロード
    このプログラム VPN-Warp_RelayAgent_Setup_Biglobe.exe をダブルクリックすると 「VPN ワープ relay エージェント(Biglobe版)」ウィザードが開くので、 以下のように、ウィザードの指示に従って必要項目を入力していく
    • イントラネットの Web サーバをアクセスするときの URL のサーバ名部分 (例えばトップページのURLが「http://intra/index.html」であれば「intra」) を入力
    • 証明書ストア内の証明書の一覧が表示されるので、 その中から 1. でインポートした証明書 (上述の例では relay,4000017) を 選択する
    • Windows にログオンするときのパスワードを入力 (リレー・エージェントが証明書ストアにアクセスする際、 ユーザ権限で Windows にログオンする必要があるため)
    • 「インストール」ボタンを押すと、インストールが行なわれ、 「relay エージェントサービスを開始しますか?」という 問合わせが表示されるので「はい」を押す

これだけで、証明書をインポートずみの PC であれば、ブラウザから
「https://biglobe.klab.org/」へアクセスするだけで
イントラネットの Web サーバにアクセスすることができるようになります。

「VPNワープ」で提供するリレー・エージェントも、 本ブログ上で公開している relay agent も、 相互に互換性がありますので、 「VPN ワープ」にて本ブログ上で公開している relay agent を使うことも可能です。 ただしこの場合、BIGLOBE への問い合わせはご遠慮下さい。 本 VPN-Warp ブログで公開している relay agent は、 本ブログの読者のかた限定で公開しているものであり、 この relay agent を使った際に生じた問題等は、 本ブログに対するコメント等で問い合わせて頂きたいと思います。

  • 入門編 (1) VPN-Warp の特長: ふつうの SSL VPN と比べて
  • 入門編 (2) VPN-Warp の特長: ssh のポートフォワードと比べて
  • 入門編 (3) stone & relayagent の設定方法
  • 入門編 (4) stone の代わりに OpenSSL の s_client を使ってみる
  • 入門編 (5) stone の代わりに普通の WWW ブラウザを使ってみる
  • 入門編 (6) WWW ブラウザを使う場合の注意点

klab_sengoku at 08:46|この記事のURLComments(0)TrackBack(0)

だいぶ間があいてしまいました (^^;) が、 VPN-Warp の応用例の第二回目を、お送りします。 前回は、「盗まれたノートPC を外部から操作する方法」という、 やや特殊な例だったので、 今回は、とてもオーソドックスに、 イントラの Windows マシンの「ファイル共有」を、 社外から利用できるようにする方法の紹介です。

その前に... そもそも Windows のファイル共有とは何なのか?を、 おさえておく必要があります。 といっても「Microsoft ネットワーク」は 仕様が完全に公開されているわけではありませんし、 過去のしがらみで無暗に複雑になってしまっているプロトコルなので、 深入りはしません (^^;)。 詳しく知りたい方は、

アンドキュメンテッドMicrosoftネットワーク
誰も知らなかった「ネットワークコンピュータ」の秘密
高橋基信 著, 翔泳社 (2002/06)

などを参照してください。

CIFS (Common Internet File System)

当初は TCP/IP とは似ても似つかない独自プロトコルとして始まった Microsoft ネットワークも、 TCP/IP がネットワークの事実上の標準になるに従って、 しだいに TCP/IP に適合するように変化してきました。 まず NetBIOS が TCP/IP の上で動くようになり (NetBIOS over TCP/IP, 略して NBT と呼ばれる)、 さらに TCP/IP に馴染まない点を整理して、 フツーの TCP/IP 上のプロトコルっぽくなったのが CIFS (Common Internet File System) です。

CIFS は NetBIOS名でコンピュータが指定できるなど、 TCP/IP の世界とは相容れない点がまだ残っているものの、 NetBIOS名を使わずに 「ホスト名」や「IP アドレス」でコンピュータを 指定することもできるので、だいぶ使いやすくなりました。 「ネットワーク コンピュータ」でコンピュータ名が表示されない (ブラウズ リストに含まれていない) コンピュータでも、 「\\192.168.1.1」などと指定してアクセスすることができるように なったわけです。

NetBIOS名の名前解決 (つまり、コンピュータ名からマシンを特定する方法ですね) には UDP/TCP 137番および UDP/TCP 138番が使われますが、 これを省略して、ファイル共有サーバの TCP 139番ポートへアクセスするだけでも、 ファイル共有を行なうことができます。 この TCP 139番ポートへのアクセスは、 SMB (Server Message Block) セッションと呼ばれます。

VPN-Warp 入門編 (3)」で説明したのと全く同様の方法で、 ローカルホストの 139番ポートを、 リモートホストの 139番ポートへ転送するようにしておけば、 ローカルホストの 139番ポートへ接続するだけで、 リモートホストへ SMB セッションを確立することができて、 ファイル共有が可能になる、というわけです。

ローカルホスト         リレー           リモートホスト
    stone ─────→ サーバ ←──── relayagent─→ ネットワーク アダプタ
   139番ポート …………………………………………………→ 139番ポート

転送先の「ネットワーク アダプタ」は、 ファイル共有サービスを行なっている IP アドレスを指定します。 後述するように 139番ポートのサービスは、 アダプタごとに有効/無効を指定できるので、 有効になっているアダプタを指定する必要があります。

Direct Hosting of SMB (Server Message Block)

Windows 2000 以降だと、 NetBIOS 名の解決を行なわない 「Direct Hosting of SMB」 というプロトコルも使えます。 TCP/IP で名前解決 (つまり、ホスト名から IPアドレスを求める方法) を行ない、 ファイル共有サーバの TCP 445番ポートへ接続して 即 SMB セッションを確立します。

「Microsoft ネットワーク」が理解しにくいプロトコルであった一因は NetBIOS と SMB が一体化していたことにあったわけで、 NetBIOS を必要としない SMB の登場は、 SMB が何であったかを明確化する意味も持っていたように思います。

話が脱線しますが、ふつう何かの複雑なシステムを作るときは、 単純なものから始めて、それらを一般化して標準技術として確立した上で、 それらを組合わせて複雑なシステムを構築していくものだと思いますし、 インターネットはそのようにして発展してきました。 「Microsoft ネットワーク」は逆の方向に発展 (つまり退化? ;) したのでしょうか? このような最初に唐突に複雑なものが登場する傾向は、 Windows に限らず他のプロプライエタリなソフトウェアに共通してみられる 傾向のようにも思われます。

「NetBIOS over TCP/IP を無効にする」設定

Windows XP 等で、 「ネットワーク接続」の各アダプタのプロパティの中の、 「インターネット プロトコル (TCP/IP)」のプロパティにて、 「詳細設定」を選択すると、 WINS タブの中に、 この「NetBIOS over TCP/IP を無効にする」という設定があります。

この設定 (以下、「NBT を無効にする」と略記) はどういう意味でしょうか? 実は、 「このアダプタにおいて『NetBIOS 付 SMB』サービスを行なわない」 という意味です。 「NetBIOS over TCP/IP」という表現で「SMBサービス」を指すのは 分かりにくいと思うのですが、それはサテオキ この設定を行なうと、 「NetBIOS 付 SMB」すなわち 137, 138番ポートと TCP 139番ポートを 使用しなくなります (つまり listen しなくなる)。

VPN-Warp で 139番ポートを転送するには、 139番ポートを空けておかねばなりませんから、 いずれかのアダプタでこの「NBTを無効にする」設定を行なう必要があります。 ファイル共有サービスを外部へ提供しない PC (ノートPC などでは安全のためにも共有サービスは提供すべきではないでしょう) ならば、 どのアダプタの NBTを無効にしても問題なさそうに見えますが、 「メイン」のアダプタ (正確に言えばデフォルトルートになっているアダプタ) の NBT を無効にすると、 SMB サービスへアクセスするクライアント機能まで無効にされてしまうので 注意が必要です。

「Microsoft ネットワーク」が分かりにくい理由の一つに、 「クライアント」と「サーバ」の機能がきちんと区別されていない、 という点もあるのではないかと思います。 「NBTを無効にする」という表現では、 SMB サービスを利用するクライアント機能を無効にするのか、 SMB サービスを提供するサーバ機能を無効にするのか判然としませんよね? 実際には「クライアント機能」も「サーバ機能」も 同時に無効にされてしまうので、 確かに表現としては間違いではないのですが...

クライアント機能まで無効にされては、 なんのために VPN-Warp で 139番をポートフォワードするのか分からなくなるので、 NBT を無効にする専用のアダプタを (メインのアダプタとは別に) 追加するのがよいでしょう。

まず 「コントロールパネル」から「ハードウェアの追加」を選び、 「新しいハードウェア デバイスの追加」をクリックして、 「一覧から選択したハードウェアをインストール」を選びます。

そして「ネットワーク アダプタ」の中から、 「Microsoft Loopback Adapter」を追加インストールします。 インストールした Loopback Adapter の「TCP/IP 詳細設定」を開くと、 「IP アドレスを自動的に取得する」がデフォルトになっていますが、 ループバックに DHCP サーバを走らせても仕方がないので、 「次の IP アドレスを使う」を選択して、 適当な IP アドレス、例えば「192.168.168.1」を設定します。 そしてもちろん、 WINS タブの中の「NetBIOS over TCP/IP を無効にする」を設定します。

「NetBios over Tcpip」ドライバを使わない設定

前述したように「NetBIOS 付 SMB」はアダプタごとに 無効にすることができるのですが、 「Direct Hosting of SMB」すなわち「NetBIOS 無し SMB」は 全てのアダプタをまとめて無効にすることしかできません。 しかも Microsoft 流 (?) に、 クライアント機能とサーバ機能の区別がついてませんから、 サーバ機能だけ無効にするということができないようです。

「コンピュータの管理」の「デバイス マネージャ」で、 「プラグ アンド プレイではないドライバ」 (「表示」メニューにて「非表示のデバイスの表示」を選択すると表示されます) の中から「NetBios over Tcpip」を選んで 「このデバイスを使わない(無効)」設定にすれば、 「NetBIOS 無し SMB」を無効にできますが、 これを設定してしまうと SMB に関する全ての機能が利用できなくなってしまうので 現実的ではありません。

繰り返しになりますが (くどい? ^^;)、 「NetBIOS 無し SMB」を有効/無効するための設定が、 「NetBios over Tcpip」ドライバを使う/使わない、という名称なのは、 なんとかならないのでしょうかねぇ? なんだかわざと分かりにくくしようとしているんじゃないかと 勘繰りたくなります。

したがって、 Windows PC の 445番ポートを転送させることは現実的ではありません。 私は Windows なノート PC 上で coLinux を走らせているので、 この Linux 上の 445番ポートを VPN-Warp でポートフォワードしたりしていますが、 まあそこまでやらなくても、139番をポートフォワードするほうがよいでしょうね。

VPN-Warp の設定

長々と Microsoft ネットワークについて説明してしまいましたが、 要約すれば Windows PC の Loopback Adapter の 139番ポートを VPN-Warp を使ってファイル共有サーバの 139番ポートへ転送すればよい、 ということになります。

そして (いままでの説明が長かったこととは裏腹に ;-)、 VPN-Warp 自体の設定はとても簡単です。 まずファイル共有サーバ側で走らせる relayagent の設定は次の通りです:

-c "C:/Program Files/KLab Security/VPNワープ relayエージェント/user.pem"
-k "C:/Program Files/KLab Security/VPNワープ relayエージェント/user.pem"
-n
relay.klab.org:443 192.168.1.129:139

フォワード先が 192.168.1.129 (この IP アドレスは私の PC の場合の例です) の 139番ポートになっている他は、 前回の「盗まれたノートPC を外部から操作する方法」と同様ですね。 「192.168.1.129」は、 「NetBIOS 付 SMB」サービスを行なっているアダプタの IP アドレスで 読み替えてください。 一般的には「ローカル エリア接続」などの名称になっているアダプタでしょう。

次に、ローカルホスト側、 つまりファイル共有サーバを利用するクライアント側 (ノートPC など) は、 stone を以下の設定で動かすだけです:

-q cert=user.pem
-q key=user.pem
relay.klab.org:443/ssl,http 192.168.168.1:139 "GET / HTTP/1.1"

これも、前回と異なるのは転送元の指定である「192.168.168.1:139」の 部分だけですね。 Loopback Adapter の IP アドレス (この例では 192.168.168.1) と、 139番ポートを指定します。


hiroaki_sengoku at 06:49|この記事のURLComments(0)TrackBack(1)

VPN-Warp relayagent を、この日記の読者のみなさま限定で公開します。
下記リンクからダウンロードできます。

上記パッケージは、VPN-Warp を読者のみなさまに評価して頂くことを 目的としておりますので、 使用した結果の影響につきましては責任を負いかねます。 以下の入門編、応用編をよくお読みの上、 万一の事態を想定したご利用をお願いします。

...とは言っても、もちろん (^^;)、 内容については万全を期して作成しております。 なにかお気付きの点がありましたら遠慮無くご指摘下さい。

  • 入門編 (1) VPN-Warp の特長: ふつうの SSL VPN と比べて
  • 入門編 (2) VPN-Warp の特長: ssh のポートフォワードと比べて
  • 入門編 (3) stone & relayagent の設定方法
  • 入門編 (4) stone の代わりに OpenSSL の s_client を使ってみる
  • 入門編 (5) stone の代わりに普通の WWW ブラウザを使ってみる
  • 入門編 (6) WWW ブラウザを使う場合の注意点
  • 応用編 (1) VPN-Warp 試用ライセンス提供開始のお知らせ
  • 応用編 (2) 盗まれたノートPC を外部から操作する方法

VPN-Warp を利用する際に必要となる SSL 証明書に関しては、 上記「応用編 (1) VPN-Warp 試用ライセンス提供開始のお知らせ」を参照願います。


hiroaki_sengoku at 07:17|この記事のURLComments(2)TrackBack(0)

VPN-Warp の応用例の第一回目は、 ノートPC に VPN-Warp の relayagent をインストールしておくことによって、 そのノートPC が万一盗まれたり紛失したりしたときに、 外部から操作できるようにしておく方法の紹介です。

VPN-Warp の使い方の基本的なところを説明した、
VPN-Warp 入門編 6 回シリーズも合わせてご参照下さい:

  • 入門編 (1) VPN-Warp の特長: ふつうの SSL VPN と比べて
  • 入門編 (2) VPN-Warp の特長: ssh のポートフォワードと比べて
  • 入門編 (3) stone & relayagent の設定方法
  • 入門編 (4) stone の代わりに OpenSSL の s_client を使ってみる
  • 入門編 (5) stone の代わりに普通の WWW ブラウザを使ってみる
  • 入門編 (6) WWW ブラウザを使う場合の注意点
  • 応用編 (1) VPN-Warp 試用ライセンス提供開始のお知らせ

relayagent をノートPC にインストールしておくと、 常にリレーサーバに対して https アクセスを試みます。 もちろん、ネットワークにつながっていなければアクセスは失敗するわけですが、 きょうび PC をネットワークにつながずに使うというのは、 かなりレアケースと言えるのではないでしょうか。 何をするにも WWW へのアクセスは必須ですからね〜。

で、WWW へアクセスすることができる環境 (正確に言うと https へアクセスできる環境) でありさえすれば、 たとえファイアウォールの中であろうと、 relayagent はリレーサーバに接続することができます。

そして、relayagent がリレーサーバに接続しさえすれば、 そのノートPC がどこにあろうと、リモートから操作することができます。 例えば Windows 標準のリモート デスクトップや、 VNCなどの リモート コントロール ソフトウェアを使って、 例えば重要ファイルを (漏洩する前に) 消すなり、 ハードディスクごと消去してしまうなりできるでしょう。 あるいは、 そのノートPC を拾った人へのメッセージをデスクトップに表示しておくことにより、 もしかしたら返してもらえるかもしれません。

というわけで、早速設定方法です。 まず relayagent の設定ファイルは次のようになります。

-c "C:/Program Files/KLab Security/VPNワープ relayエージェント/user.pem"
-k "C:/Program Files/KLab Security/VPNワープ relayエージェント/user.pem"
-n
relay.klab.org:443 localhost:3389

「-c」オプション、「-k」オプションで、 それぞれ公開鍵と秘密鍵 (この場合は同じファイル名ですね) を指定し、 「-n」オプションは、 relayagent をポートフォワーダとして使うための設定ですね (入門編 (3) を参照してください)。 フォワード先は、「localhost:3389」つまり リモート デスクトップ サーバのポートです。 VNC を利用する場合は、3389番ポートの代わりに 5900番ポートなどを指定することになるでしょう。

設定ファイルである「C:\Program Files\KLab Security\VPNワープ relayエージェント\relayagent.cfg」を 直接編集してもいいのですが、 Windows 版 relayagent では、 「コントロールパネル」の「プログラムの追加と削除」から 「VPNワープ relayエージェント」の「変更」を行なうことで GUI で設定変更することもできます。 この場合、設定ウィザードが表示されますから、

対象サーバ:      localhost
対象ポート:      3389
relay サーバ:    relay.klab.org

HTTP 以外のプロトコルを中継する

公開鍵:  C:/Program Files/KLab Security/VPNワープ relayエージェント/user.pem
秘密鍵:  C:/Program Files/KLab Security/VPNワープ relayエージェント/user.pem

を、それぞれ指定することにより、設定ファイルの変更が行なわれます。 あとは、「コンピュータの管理」の「サービス」で 「Relay Agent Service」を「開始」するか、 あるいは Windows を再起動するだけです。 これで、ノートPC 側の設定は全て完了です。

次に、このノートPC を外部からコントロールする側の PC (ここではデスクトップPC と呼ぶことにしましょう) の設定です。 設定といっても、stone を以下の設定で実行するだけです:

-q cert=user.pem
-q key=user.pem
relay.klab.org:443/ssl,http localhost:3388 "GET / HTTP/1.1"

SSL証明書 user.pem は、relayagent の設定で使ったものと同じものを使います。

これで、デスクトップPC の 3388番ポートが、 ノートPC の 3389番ポートへ、フォワードされます。 しかも、ノートPC がどこにあろうと、間にファイアウォールがあろうと、 ノートPC から WWW へアクセスできる限り、ポートフォワードが常に行なわれます。

したがって、あとはデスクトップPC 上で「リモート デスクトップ接続」を 実行して「localhost:3388」へ接続すれば、 ノートPC を自在に操作できるというわけです。 今回紹介した例では「リモート デスクトップ」を使いましたが、 「VNC」やその他のリモート コントロール ソフトウェアでも全く同様に 使うことができます。 また、SSL 証明書は前述したようなファイル(user.pem) で置いておくのではなく、 Windows の証明書ストアや、USBトークン、ICカードなどの、 よりセキュアな場所に置いて利用することもできます。


hiroaki_sengoku at 09:54|この記事のURLComments(0)TrackBack(0)
このエントリーを含むブックマーク 2006年05月06日

VPN-Warp の使い方の基本的なところを、
VPN-Warp 入門編 6 回シリーズで説明してきました:

  • 入門編 (1) VPN-Warp の特長: ふつうの SSL VPN と比べて
  • 入門編 (2) VPN-Warp の特長: ssh のポートフォワードと比べて
  • 入門編 (3) stone & relayagent の設定方法
  • 入門編 (4) stone の代わりに OpenSSL の s_client を使ってみる
  • 入門編 (5) stone の代わりに普通の WWW ブラウザを使ってみる
  • 入門編 (6) WWW ブラウザを使う場合の注意点

これからの「VPN-Warp 応用編」では、VPN-Warp を利用した実運用例を 紹介していきます。VPN-Warp は、 KLab(株) および KLabセキュリティ(株) のネットワーク インフラを支える 基盤技術の一つとなっており、 様々な用途に使われていて、 かつ長期にわたる安定運用実績があります。

とはいっても、実際に VPN-Warp を利用してみないことには 絵に描いた餅でイマイチ実感がわかないと思いますので、 この「仙石浩明CTO の日記」の読者のみなさま限定で、 VPN-Warp 試用ライセンスをご提供します。

試用をご希望のかたは、 vpnwarp-blog@klabsecurity.com 宛に、

  • どのように VPN-Warp を利用してみたいか
  • 「VPN-Warp入門編」を読んだ上での質問 and/or 感想
  • 簡単な自己紹介

を送って頂ければ、relayagent (Windows 用 or Linux 用) と、 VPN-Warp を利用するための SSL証明書をお送りします。 試用期間はとりあえず一ヶ月程度を考えていますが、 ご利用方法によっては延長も可能です。 また、商用ライセンスをご希望のかたは別途ご相談下さい。

なお stone については、 stone 公式ページ から Windows 用のバイナリ、 ないし Linux 用などのソースコードをダウンロードできますので、 そちらをご利用下さい。


hiroaki_sengoku at 07:46|この記事のURLComments(0)TrackBack(0)
このエントリーを含むブックマーク 2006年05月02日

前回は、WWW ブラウザで VPN-Warp へアクセスする方法を紹介しました。 基本的には https://リレーサーバのホスト名/パス という URL を WWW ブラウザでアクセスすれば、 relayagent に設定したフォワード先の WWW サーバへつながるわけですが、 二点ほど注意すべき点があります。

  1. 「https」と「http」の違い
  2. ホスト名の違い

WWW サーバに直接アクセスする場合の URL を、 例えば http://intra/path としましょう。 この場合、WWW サーバへ送られるリクエストヘッダは、 次のようになります(説明の都合上大幅に単純化しています):

GET /path HTTP/1.1
Host: intra

一方、VPN-Warp 経由 (つまり、ブラウザ → リレーサーバ → relayagent → WWW サーバ) でアクセスする場合に、 WWW サーバへ送られるリクエストヘッダは、URL が https://relay.klab.org/path ですから、 次のようになります:

GET /path HTTP/1.1
Host: relay.klab.org

両者を比べると、「Host: 」フィールドの部分が異なりますね。 多くの WWW サーバには、バーチャルホストと呼ばれる機能があって、 一台の WWW サーバで、いろんなホスト名の URL を受け付けて、 ホスト名に応じて異なるページを見せることができるわけですが、 要はこの「Host: 」フィールドを見て、 配信するコンテンツを切替えているわけです。

したがって、「Host: relay.klab.org」のままでは、 「intra」のコンテンツを見ることができないケースが多いでしょう。 この (2) ホスト名の違い の問題を解決するために、 relayagent には「Host: 」フィールドの書き換え機能があります。 この場合でしたら、 「-h intra」オプションを追加指定することにより、 リクエストヘッダの「Host: 」フィールドを「intra」に書き換えます。

これで少なくともリクエストヘッダは、直接アクセスする場合と、 VPN-Warp 経由でアクセスする場合が同じになりました。 コンテンツの HTML 文書で URL を絶対指定したりしない限りは、 普通にブラウズできるでしょう... か?

コンテンツが全て静的なページから構成されているのであれば、 その通りなのですが、 普通は PHP や Java などを使用して動的に生成するページもあるでしょう。 その場合、PHP インタプリタやサーブレットコンテナは、 いま生成中のページの URL がなんであるか把握していて、 ページを出力するときに、絶対指定を出力してしまうケースがあるので 注意が必要です。

特に、(1) 「https」と「http」の違い は留意しておくべきでしょう。 WWW サーバにとっては、80番ポートでアクセスを受け付けているわけですから、 PHP インタプリタないしサーブレットコンテナは、 「http」なページを出力中だと認識しているはずです。 ところが WWW ブラウザにとっては、アクセスしている URL は「https」です。 WWW サーバがレスポンス中に「http」な URL を出力してしまうと、 ブラウザはその URL をたどれなくなります。

とはいえ、動的なページを出力する際に絶対 URL を指定してしまうと、 コンテンツの URL を変更したいときも不便ですし、 そもそも相対 URL で済むところをわざわざ絶対指定する必然性もないので、 普通の Web アプリケーションを使う限りはあまり問題とはならないようです。 WWW サーバのレスポンスを全て監視して、 適宜 URL の書き換えを行なえば解決できる問題ではあるのですが、 ごく少数の絶対 URL の書き換えのためだけに、 全てのレスポンスを監視するのは、 パフォーマンスの点で割が合わないと言えそうです。

むしろ問題となるのは、リダイレクトです。 リダイレクトの場合、WWW サーバは次のようなレスポンスヘッダを返します (説明のため大幅に単純化しています):

HTTP/1.1 301 Moved Permanently
Location: http://intra/path

このような 301 レスポンスを受け取ると、 WWW ブラウザは「Location: 」フィールドで指定された URL のページを 表示しようとします。 そして「Location: 」フィールドは絶対 URL で指定されます。

WWW サーバは、ページの URL は「http://intra/...」である と認識していますから、 同じホスト名の URL へリダイレクトを行なう場合 「Location: 」フィールドには「http://intra/...」が指定されます。 これをそのまま WWW ブラウザに伝えてしまうと、 WWW ブラウザは「http://intra/...」すなわち VPN-Warp を介さずに 直接 WWW サーバへアクセスしようとしてしまいます。

この問題を解決するために、 relayagent にはレスポンスヘッダの「Location: 」フィールドを書き換える機能が あります。 すなわち「-H」オプションを指定すると、 「Location: 」フィールドのホスト名とフォワード先のホスト名 (今回の例の場合は intra ですね) が一致する場合、 それをリレーサーバの URL へ書き換えます。 前述したレスポンスヘッダの場合であれば、

HTTP/1.1 301 Moved Permanently
Location: https://relay.klab.org/path

に書き換えるわけです。これでめでたく WWW ブラウザは リダイレクト先のページも、VPN-Warp 経由でアクセスするようになります。


hiroaki_sengoku at 15:13|この記事のURLComments(0)TrackBack(0)
プロフィール
2000年、KLab株式会社取締役CTOに就任。1995年以来、TCP/IPパケットリピータ「stone」や、Palm上の時刻表ツール「Time Table Viewer」などを開発・発表する。また、堅牢で安定したサイトgcd.org を運営し、会員にサービスを提供。そこで得たサーバー構築ノウハウを日経Linuxで2000年4月から2年間連載
Categories
Blog内検索
人気記事
Archives
Ranking
KLabについて
KLab株式会社は、携帯電話の基盤技術から各種ソリューション、コンテンツ企画など多くのサービスを提供している会社です。
最新記事
最新コメント
最新トラックバック
blogranking.net
QRコード
QRコード