仙石浩明の日記

システム構築・運用

2006年7月10日

一つのIPアドレスで djbdns (tinydns, dnscache) を走らせる (3) dnscache で別ポートを使う

前回は、tinydns を IP アドレス指定せずに起動したいケースについて述べた。 前々回で述べたように、 djbdns はネームサーバ tinydns と、 キャッシュサーバ dnscache が完全に分離した独立のプログラムになっていることが 大きな特徴であるが、 tinydns を IP アドレス指定せずに起動しようとすると、 同じポート番号を使う dnscache を起動することができなくなってしまう。

そもそも論で言えば、 ネームサーバとキャッシュサーバは全く異なるサービスを行なうサーバなのだから、 同じポート番号を使うこと自体が間違っているのである。 もしキャッシュサーバが 53番以外のポートを使うのであれば、 tinydns も dnscache も IP アドレスを指定せずに同じマシンで起動できるし、 これ以上シンプルな解決策はないであろう。

ならば dnscache を 53番とは異なるポートで起動しよう。 ただし、dnscache は 53番ポートを bind(2) するように ハードコーディングされているので、 若干パッチをあてる必要がある。

--- dnscache.c.org        Mon Feb 12 06:11:45 2001
+++ dnscache.c        Sun Jul  9 06:43:23 2006
@@ -390,6 +390,7 @@
 {
   char *x;
   unsigned long cachesize;
+  unsigned long port;
 
   x = env_get("IP");
   if (!x)
@@ -397,16 +398,19 @@
   if (!ip4_scan(x,myipincoming))
     strerr_die3x(111,FATAL,"unable to parse IP address ",x);
 
+  x = env_get("PORT");
+  if (x) scan_ulong(x,&port); else port = 53;
+
   udp53 = socket_udp();
   if (udp53 == -1)
     strerr_die2sys(111,FATAL,"unable to create UDP socket: ");
-  if (socket_bind4_reuse(udp53,myipincoming,53) == -1)
+  if (socket_bind4_reuse(udp53,myipincoming,port) == -1)
     strerr_die2sys(111,FATAL,"unable to bind UDP socket: ");
 
   tcp53 = socket_tcp();
   if (tcp53 == -1)
     strerr_die2sys(111,FATAL,"unable to create TCP socket: ");
-  if (socket_bind4_reuse(tcp53,myipincoming,53) == -1)
+  if (socket_bind4_reuse(tcp53,myipincoming,port) == -1)
     strerr_die2sys(111,FATAL,"unable to bind TCP socket: ");
 
   droproot(FATAL);

すなわち、環境変数「PORT」にポート番号を設定した場合は、 53番ポート番号の代わりに設定されたポート番号を使えるようにする。 例えば 2053番ポートで dnscache を起動する。 もちろんリゾルバは、こんな事情はお構い無しに 53番ポートへ問合わせるので、 それを 2053番ポートへ振り向ける (リダイレクトさせる) 必要がある。

Linux には、 パケット フィルタリング フレームワーク があって、 ネットワーク アドレス変換を自在に行なうことができる。 だから、53番ポートへのアクセスを 2053番ポートへリダイレクトする、 などということは iptables コマンドを実行するだけで手軽に実現できる。

例えば、dnscache と同じマシン上のリゾルバから 53番ポートへの接続を、 2053番ポートへリダイレクトするなら、

iptables -t nat -A OUTPUT -j REDIRECT -p udp --dport 53 --to-port 2053 \
        -s 127.0.0.1 -d 127.0.0.1

などと iptables を実行するだけである。 「-p udp --dport 53」の部分で、 UDP 53番ポートへのアクセスが対象であることを指定し、 「--to-port 2053」の部分で、 リダイレクト先が 2053番ポートであることを指定している。

また、「-s 127.0.0.1 -d 127.0.0.1」の部分が、 接続元 (つまりリゾルバ) と接続先 (つまり dnscache) の IP アドレスの指定である。 接続元が LAN 内の他のマシンの場合も許可するなら、 この部分を例えば「-s 192.168.1.0/24 -d 192.168.1.1」に変更し、 「OUTPUT」の代わりに「PREROUTING」チェインを指定して iptables を実行すればよい (後述)。

さて、これだけだと実は問題がある。 dnscache は当然のことながら自ドメインのホスト名を解決するには 同じマシン上の tinydns へアクセスする必要があるのだが、 上記のように iptables を実行すると、 この dnscache から tinydns へのアクセスまでリダイレクトしてしまう。 すなわち dnscache が localhost の 53番ポートへ、 自ドメインに関する問合わせを行なうと、 それが 2053番ポートへリダイレクトされ、 結果 dnscache (つまり自分自身) に届いてしまう。 これでは自ドメインのホスト名の解決ができない。

したがって、dnscache からのアクセスの場合だけは、 リダイレクトが行なわれないようにしなければならない。 これを実現するには、 iptables のパラメータに「-m owner ! --uid-owner Gdnscache」 を追加すればよい。 これは、アクセス元のユーザが「Gdnscache」ならばリダイレクトを行なわない、 という指定であり、 Gdnscache は dnscache を実行しているユーザID である。

以上をまとめると、 次のような sh スクリプトになる。 これをマシンのブート時に実行すればよい。

#!/bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin
nsredirect="-t nat -j REDIRECT --dport 53 --to-port 2053"
iptables -p udp $nsredirect -A OUTPUT -s 127.0.0.1 -d 127.0.0.1 \
        -m owner ! --uid-owner Gdnscache
iptables -p tcp $nsredirect -A OUTPUT -s 127.0.0.1 -d 127.0.0.1
if [ -n "$INNER_NETWORK" -a -n "$INNER_DNSCACHE" ]; then
        nsredirect="$nsredirect -A PREROUTING \
                -s $INNER_NETWORK/$INNER_NETMASK -d $INNER_DNSCACHE"
        iptables -p udp $nsredirect
        iptables -p tcp $nsredirect
fi

$INNER_NETWORK」と「$INNER_NETMASK」に、 LAN のネットワークアドレス (例えば 192.168.1.0) とサブネットマスクを それぞれ指定する。 また、「$INNER_DNSCACHE」は キャッシュサーバ (つまりこのマシン) の IPアドレスである。

なお、Linux のパケット フィルタリングでは、
接続元が同一ホストの場合は OUTPUT チェインが使われるので、
-s 127.0.0.1」を指定するときは「-A OUTPUT」を指定して OUTPUT チェインへ追加し、
接続元が LAN 内の他のマシンの場合は PREROUTING チェインが使われるので、
-s $INNER_NETWORK/$INNER_NETMASK」を指定するときは 「-A PREROUTING」を指定して PREROUTING チェインへ追加している。

Filed under: システム構築・運用 — hiroaki_sengoku @ 06:43
2006年7月9日

一つのIPアドレスで djbdns (tinydns, dnscache) を走らせる (2) IPアドレス指定しないサービス起動

前回は djbdns の特徴について述べた。 djbdns の大きな特徴は、 キャッシュサーバ (dnscache) とネームサーバ (tinydns) が分離している、 という点であり、 ネームサーバだけを不特定多数に対して公開することにより、 BIND など他のネームサーバがかかえている潜在的リスクを完全に回避している。

dnscache と tinydns を走らせるための、 固定的な IP が確保できるのであれば話は簡単なのであるが、 複数の (固定的な) IP アドレスを使いにくいマシン環境もある。

例えばノートPC は、移動中など、スタンドアロンで動かすこともあり、 この場合、IP アドレスは 127.0.0.1 の一つだけである。 LAN に接続したり、あるいはダイアルアップ接続すると、 もう一つ IP アドレスを持つことになるが、 この IP アドレスは固定ではなく、 接続のたびに動的に割当てられることが多いだろう。 仮に毎回同じ IP アドレスが割当てられるのだとしても、 スタンドアロン状態の時は、 その IP アドレスが利用できないという点で、 固定的な IP アドレスが利用できるとは言いがたい。

Linux 等では alias IP アドレスを設定することによって 固定的に IP を割当てておくこともできる。 例えば loopback インタフェース lo に対して

onohara:/root # ifconfig lo:0 127.0.0.2

などと別の IP アドレスを追加することができる。 マシン内部で利用するサービスならこれでもいいが、 外部に対して公開するサービスではこの方法は使えない (ノートPC で外部にサービスしてどうするん?という突っ込みは言わないお約束 ;)。

ノートPC 以外でも、 例えば小規模サイト (家庭LAN など) における ゲートウェイマシンなどにおいても、 昨今だとインターネットへの接続は PPPoE を使うことが多いだろう。 グローバル IP アドレスは接続時のみ割当てられる。 サイト外に公開するネームサーバは、 このグローバル IP アドレスで動かすのが自然であるが、 PPPoE 接続が切れている場合にどうするか考えておく必要がある。

ノートPC にしろゲートウェイマシンにしろ、 外部に対して接続しているときのみ割当てられる IP アドレスを利用しようとすると、 接続時のみ tinydns を起動する、 という方法を採らざるを得ない。 たとえば接続が成功したときに実行されるスクリプト (/etc/ppp/ip-up など) で tinydns を起動し、 接続が切れたときに実行されるスクリプト (/etc/ppp/ip-down など) で tinydns を終了させる、 という方法が一般的だろう。

しかしながらこの方法だと、 接続が切れているときは tinydns も動いていないわけで、 サイト内に対して別途 tinydns を立ち上げる必要がある。 また、 接続のたびにサーバプログラムを起動するということは、 それだけトラブルの可能性が高まるということであり、 できればもっとシンプルにしたい。 対外サービスを行なうサーバプログラムには、 常に安定して動いていてほしいわけで、 起動・停止を繰り返す運用よりは、 ずーっと立ち上げっぱなしの方が好ましいだろう。

UNIX の場合、 動的に IP アドレスを割当てられたときに、 サーバプログラムで IP アドレスを bind(2) しなおさないで済む方法というと 一つしかない。 つまり IP アドレスを指定せず (INADDR_ANY) に bind(2) する方法である。

senri:/root # netstat -nap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address      Foreign Address    State      PID/Program name
        ...
udp        0      0 :::53              :::*                          1240/tinydns
tcp        0      0 0.0.0.0:53         0.0.0.0:*          LISTEN     1242/tcpserver
        ...

「Local Address」に「:::53」とか「0.0.0.0:53」と出ているのが、 IP アドレスを指定せずに走らせているサーバプログラムである。 このようなサーバプログラムは、 マシンに割当てられているどの IPアドレスに対する接続も受け付けることができる。 つまり新たに動的に IPアドレスが追加されても、 サーバプログラムを立ち上げ直す必要がない。

だから tinydns は IPアドレスを指定せずに、 つまり環境変数 IP に 0.0.0.0 (INADDR_ANY) を設定して立ち上げることが望ましい。

ところが、このようにしてしまうと、当然のことながら 同じ 53番ポートを使う dnscache を同じマシンで立ち上げることが できなくなってしまう。

そもそも、キャッシュサーバ (dnscache) とネームサーバ (tinydns) では 目的が違うのにもかかわらず、 同じポート番号 53番を使うのが問題なのである。 最初から別のポート番号を割当てておけば、 このような問題で悩まなかったものを... RFC 883 が悪い~
と言っても始まらない (^^;) ので、 なんとか tinydns と dnscache を IP アドレスを指定せずに立ち上げる方法を 考えてみたい。

続きは次回に...

Filed under: システム構築・運用 — hiroaki_sengoku @ 07:10
2006年7月8日

一つのIPアドレスで djbdns (tinydns, dnscache) を走らせる (1) djbdns の特徴

djbdns は、 とてもシンプルなネームサーバなので、 動作が予測しやすいという特徴を持っている。 複雑なプログラムにありがちな、 謎な現象とも無縁。 ネームサーバというと BINDが有名だが、 BIND はとても巨大なプログラムで、 特に高負荷時に理解しがたい挙動をする。 私は 3年半ほど前に BIND の異常動作 (高負荷環境下だとサービス起動に異常に時間がかかる) で徹夜する羽目に陥って以来、 BIND を捨てて djbdns を使うようになった。

djbdns の大きな特徴の一つに、 キャッシュサーバ (dnscache) とネームサーバ (tinydns) が分離している、 という点がある。 キャッシュサーバ、すなわちリゾルバからの問合わせを受けて、 他のネームサーバに再帰的に問合わせを行なうサーバは、 不特定多数からの問合わせを受付けるべきではない。 なぜなら、キャッシュサーバはその仕組み上、 攻撃に対して脆弱であり、 不特定多数に対してオープンにするのであれば、 嘘のレコードを覚え込まされてしまう 恐怖に怯え続けなければならない。

一方、ネームサーバは本来的に不特定多数に対してサービスすべきものである。 他ドメインからの問合わせ (主に他ドメインのキャッシュサーバからの問合わせ) に対して答えなければ役目を果たせない。 つまり、

  • キャッシュサーバは自ドメインのユーザからの問合わせに答えるサーバ
  • ネームサーバは他ドメインからの問合わせに答えるサーバ

であって、全く異なる役目のサーバである。 両機能を (BIND のように) 一つのプログラムで実現することは 大変なリスクをともなう。

私も djbdns に乗り換える前は BIND を使っていたのだが、 キャッシュサーバとネームサーバを分離し、 セキュリティを向上させるために様々な工夫をした。 この辺りの顛末は、 日経Linux に書いた連載の中でも紹介している:

2000年 5月号 第2回 「ネーム・サーバ (前編)」 pp143-148 (HTML 版)
2000年 6月号 第3回 「ネーム・サーバ (後編)」 pp133-140 (HTML 版)

djbdns だと、この連載で書いたような工夫をしなくても、 同等以上のセキュリティをデフォルトで実現できるので、 とても楽である。

キャッシュサーバとネームサーバそれぞれを別のマシンで動かすのであれば、 素直に djbdns を動かすだけで完璧なのであるが、 一台のマシンで両者のサーバを動かそうとすると、 少々工夫が必要である。 もし、そのマシンが複数の NIC (ネットワーク インタフェース カード) を 持っていて、 各 NIC に固定的に IP アドレスを割当てているなら、 話は簡単である。 異なる NIC それぞれに、 キャッシュサーバないしネームサーバをそれぞれ立ち上げればよい。 ところが IP アドレスが固定でない場合は少しやっかいである。

続きは次回に...

Filed under: システム構築・運用 — hiroaki_sengoku @ 08:34
2006年6月29日

jabber サーバの死活確認スクリプト

jabber.jp と Google Talk との相互接続開始」で jabber.jp の紹介をしたのが原因というわけでもないのだろうが、 一時的に jabber サーバが過負荷になってサービス不能状態に陥ってしまった。 この jabber.jp は KLab のサーバマシン群の中では低性能なマシンを使っていたので、 この機会にと、もう少しマシなマシンへ引越しさせた。 が、負荷の問題というよりは、サーバプログラム自体に、 なにかのタイミングでハングするバグがあるようだ。 他のソフトウェアへの乗り換えも含めて対策を検討中であるが、 とりあえずハングしていないかの死活確認は必須だろう。

まず CPAN から Net::Jabber をインストール。 「Google Talk を流れるデータを見る」に、
「Net::Jabberは,インストールしようと思ってもmake testでひっかかってしまってうまくインストールできません.」と書いてあったのが 気になりながら (^^;) 作業していると...

cpan> install Net::Jabber(改行)
        ...
---- Unsatisfied dependencies detected during [R/RE/REATMON/Net-XMPP-1.0.tar.gz] -----
    XML::Stream
Shall I follow them and prepend them to the queue
of modules we are processing right now? [yes] (改行)
Running make test
        ...
t/buildxml......ok
t/load..........ok
t/parse_node....ok
t/parse_tree....ok
t/tcpip.........ok
t/tcpip2ssl.....ok 2/3

ありゃ、止まってしまった。 XML-Stream-1.22/t/tcpip2ssl.t を見ると、

my $status = $stream->Connect(hostname=>"obelisk.net",
                              port=>5223,
                              namespace=>"jabber:client",
                              connectiontype=>"tcpip",
                              ssl=>1,
                              timeout=>10);

などと書いてある。 つまり、jabber サーバである「obelisk.net」を相手に 接続テストを行なおうとして止まってしまっている。 おそらく obelisk.net が相手にしてくれないのだろう (5222番ポートの方は相手をしてくれるようだ)。 とりあえず obelisk.net の代わりに手元の jabber サーバを指定して、 テストを成功させ、無事インストール完了。

とりあえず、さくっと死活確認スクリプトを書いてみる:

#!/usr/bin/perl
@RCPTS = ('jabber-error@gcd.org',);
use Net::Jabber qw(Client);

&ckjabber("jabber.gcd.org", 5222, "test", "XXXXXXXX", "ckjabber.$$");
&ckjabber("jabber.jp", 5222, "test", "YYYYYYYY", "ckjabber.$$");

sub ckjabber {
    my ($server, $port, $username, $password, $resource) = @_;
    my $client = new Net::Jabber::Client();
    my $connected = 0;
    my $authenticated = 0;
    my $recv_subject;
    my $recv_body;
    my $subject = "check $server";
    my $body = "This is a test message from $resource.";
    my $onConnect = sub { $connected++; };
    my $onAuth = sub {
        $authenticated++;
        $client->MessageSend(to => "$username\@$server/$resource",
                             subject=> $subject, body => $body, );
    };
    my $onMessage = sub {
        my ($sid, $message) = @_;
        $recv_subject = $message->GetSubject();
        $recv_body = $message->GetBody();
        $client->Disconnect();
    };
    $client->Connect(hostname => $server);
    $client->SetCallBacks(onconnect => $onConnect, onauth => $onAuth,
                          message => $onMessage, );
    $client->Execute(username => $username, password => $password,
                     resource => $resource,
                     hostname => $server, port => $port,
                     register => 1,
                     connectsleep => 0, connectattempts => 1, );
    my $error;
    if (! $connected) {
        $error = "can't connect to $server";
    } elsif (! $authenticated) {
        $error = "authentication failure";
    } elsif ($recv_subject ne $subject || $recv_body ne $body) {
        $error = "get '$recv_subject' & '$recv_body'";
    }
    if ($error) {
        print STDERR "$server error\n$error\n";
        &mail("$server error", localtime(time) . "\n$error\n", @RCPTS);
    }
}

sub mail {
    my ($subject, $body, @rcpts) = @_;
    open(INJECT,"|/var/qmail/bin/qmail-inject " . join(" ", @rcpts)) || die;
    print INJECT "From: root\nTo: ", join(", ", @rcpts), "\n",
        "Subject: $subject\n\n$body";
    close(INJECT);
    if ($? << 8) {
        print STDERR "Fail to send mail to ", join(", ", @rcpts), "\n";
        exit 1;
    }
}

自分自身にメッセージを送ってみて、 同じ文面が返ってくるか確認するだけの単純なテストスクリプトだが、 jabber サーバがハングしたら検知してメールを送ってくれるだろう。 とりあえず 5 分に一度実行するように cron に仕掛けた。

Filed under: システム構築・運用 — hiroaki_sengoku @ 07:44
2006年6月23日

jabber.jp と Google Talk との相互接続開始 hatena_b

jabber.jpGoogle Talk と 相互接続できないのが某所で問題になっている。」 昨日、そんな声が社内から聞こえてきました。 jabber.jp というのは 2001年6月のサービス開始以来、 KLab で運用している jabber (通信に XMLプロトコル XMPP を使う、 インスタント メッセージング サービス) サーバです。 KLab 社内公式 IM (インスタント メッセンジャー) として大いに活用していますが、 社外のかたにも開放しています。

その時は、 「え? Google ? あそこって他サイトとの相互接続ってやってなかったんでは?」 と、思わず答えてしまいましたが、 いつのまにか 相互接続を開始していたんですね。 知らなかった... orz
jabber.org などとは 相互接続できていたので、 Google Talk と接続できないのはアチラの問題、と思い込んでいました (_O_)。

多くの方々に利用して頂いている jabber.jp で調査を行なうよりは、 (落ちても誰も文句言わない ;) 自宅サイト gcd.org も Google Talk と接続できないという 問題をかかえていたので、gcd.org で調査を始めました。

通信できないときに最初にすべきことと言えば、 まずはパケットが届いているか調べることですね。 tcpdump でパケットダンプを取りつつ、 Google Talk (gmail.com) に jabber クライアントでログインして GCD (jabber.gcd.org) へチャットしてみました。

senri:/home/sengoku # tcpdump -i ppp0 port 5269
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ppp0, link-type LINUX_SLL (Linux cooked), capture size 96 bytes
07:07:11.672591 IP iproxy.google.com.32489 > gcd.org.5269: S 2133894537:2133894537(0) win 5720 <mss 1430,sackOK,timestamp 379820136 0,nop,wscale 0>
07:07:13.004593 IP gcd.org.5269 > iproxy.google.com.32489: S 1097254147:1097254147(0) ack 2133894538 win 5608 <mss 1414,sackOK,timestamp 401538947 379820136,nop,wscale 2>
07:07:11.837330 IP iproxy.google.com.32489 > gcd.org.5269: . ack 1 win 5720 <nop,nop,timestamp 379820153 401538947>
07:07:11.837527 IP iproxy.google.com.32489 > gcd.org.5269: P 1:142(141) ack 1 win 5720 <nop,nop,timestamp 379820153 401538947>
07:07:11.837574 IP gcd.org.5269 > iproxy.google.com.32489: . ack 142 win 1670 <nop,nop,timestamp 401538988 379820153>
07:07:11.837762 IP gcd.org.5269 > iproxy.google.com.32489: P 1:187(186) ack 142 win 1670 <nop,nop,timestamp 401538988 379820153>
07:07:12.002559 IP iproxy.google.com.32489 > gcd.org.5269: . ack 187 win 5720 <nop,nop,timestamp 379820169 401538988>
07:07:12.003738 IP iproxy.google.com.32489 > gcd.org.5269: P 142:242(100) ack 187 win 5720 <nop,nop,timestamp 379820170 401538988>
07:07:12.046764 IP gcd.org.5269 > iproxy.google.com.32489: . ack 242 win 1670 <nop,nop,timestamp 401539040 379820170>
07:07:13.486820 IP gcd.org.41294 > 216.239.57.83.5269: S 1003009747:1003009747(0) win 5656 <mss 1414,sackOK,timestamp 401539400 0,nop,wscale 2>

最初 Google 側から GCD の 5269番ポートへ接続があって、 それに答えて GCD から Google へ dialback 接続を行なう... ところが、それに対する応答が無い。 実際、netstat で調べてみると SYN_SENT のままです。

senri:/home/sengoku # netstat -nap | grep s2s
tcp        0      0 0.0.0.0:5269            0.0.0.0:*               LISTEN      14525/s2s
tcp        0      1 60.32.85.216:41294      216.239.57.83:5269      SYN_SENT    14525/s2s
...

見るからに、216.239.57.83 へ接続を試みていることが間違いのようです。 216.239.57.83 というのは gmail.com の A レコードの一つですね。

senri:/home/sengoku % host gmail.com.
gmail.com has address 64.233.161.83
gmail.com has address 216.239.57.83        ←コレ
gmail.com has address 64.233.171.83
gmail.com mail is handled by 50 gsmtp163.google.com.
gmail.com mail is handled by 50 gsmtp183.google.com.
gmail.com mail is handled by 5 gmail-smtp-in.l.google.com.
gmail.com mail is handled by 10 alt1.gmail-smtp-in.l.google.com.
gmail.com mail is handled by 10 alt2.gmail-smtp-in.l.google.com.

では、jabber サーバ間の通信では接続先をどのように探せばよいのだっけ、と 調べてみると、 SRV レコードに サーバのホスト名とポート番号を 登録しておくことになっているようです。 以前は A レコードを使っていたような気がするのですが、 A レコードを直接使ってしまうと、 jabber ID (JID) のドメイン名と jabber サーバのホスト名を一致させる必要があって 柔軟性を欠くから、 SRV レコードに変更したということなのでしょう。

私の自宅サイトのような小規模サイトなら jabber.gcd.org というホスト名で jabber サーバを立ち上げることはさほど問題ではありませんが、 Google のような大規模サイトだと、gmail.com というホスト名で jabber サーバを立ち上げてしまうと運用が面倒なことになりそうです。 gmail.com の SRV レコードを引いてみると、 xmpp-server.l.google.com という応答が返ってきました。 つまり (JID のドメイン名である) gmail.com とは 異なるサーバ xmpp-server.l.google.com で jabber サーバを動かしているということです。

senri:/home/sengoku % host -t srv _xmpp-server._tcp.gmail.com.
_xmpp-server._tcp.gmail.com has SRV record 5 0 5269 xmpp-server.l.google.com.
_xmpp-server._tcp.gmail.com has SRV record 20 0 5269 xmpp-server1.l.google.com.
_xmpp-server._tcp.gmail.com has SRV record 20 0 5269 xmpp-server2.l.google.com.
_xmpp-server._tcp.gmail.com has SRV record 20 0 5269 xmpp-server3.l.google.com.
_xmpp-server._tcp.gmail.com has SRV record 20 0 5269 xmpp-server4.l.google.com.
% host -t a xmpp-server.l.google.com.
xmpp-server.l.google.com has address 64.233.167.125

というわけで通信できない原因が分かったので、 あとは GCD の jabber サーバに SRV レコードを参照させればいいだけですが、 幸い現在使っている jabberd2 サーバは SRV レコードを参照する機能を持っていました。 なので設定ファイル resolver.xml に次の行を追加するだけです:

  <lookup>
    <srv>_xmpp-server._tcp</srv>
    <srv>_jabber._tcp</srv>
  </lookup>

また、SRV レコードを参照する jabber サーバ&クライアントのために、 ネームサーバに SRV レコードを登録しておいたほうがいいでしょう。 GCD ではネームサーバとして djbdns を使っているので、 次の行を tinydns のレコードファイルに追加します:

:_jabber._tcp.jabber.gcd.org:33:\000\012\000\000\024\225\006jabber\003gcd\003org\000
:_xmpp-server._tcp.jabber.gcd.org:33:\000\012\000\000\024\225\006jabber\003gcd\003org\000
:_xmpp-client._tcp.jabber.gcd.org:33:\000\012\000\000\024\146\006jabber\003gcd\003org\000

以上の修正を行なった上で、 GCD と Google Talk それぞれに jabber クライアントでログインして、 チャットしてみると、あっさりつながりました。 続いて同様の修正を jabber.jp に対しても行ないました。

というわけで、約5ヶ月の間ご迷惑をおかけしましたが、 jabber.jp と Google Talk は、 ようやく本日より相互接続できるようになりました。

Filed under: システム構築・運用 — hiroaki_sengoku @ 13:18
2006年6月11日

TCP/IP を使った DNS 問合わせ

DNS問合わせというと、 ネームサーバの UDP/IP の 53番ポートへ問合わせるのが一般的であり、 TCP/IP の 53番ポートはゾーン転送のみに使われることが多い。 しかし RFC (RFC1035 および RFC2136) 上は TCP も、UDP と同様、 通常の問合わせにも利用することができることになっているし、 多くのネームサーバでそのような実装になっている。

手近なネームサーバが利用できない環境 (某ホテルの無線LAN 環境など ;) で、 やむを得ず遠方のネームサーバを利用する際などに、 TCP を使って問合わせをすると、 ポートフォワードすることもできて便利である。

拙著「ネーム・サーバ (前編)」に書いたようにネームサーバには二種類あって、 ここで言うネームサーバは、 「/etc/resolv.conf に登録できるネーム・サーバー」のことである。 つまり、リゾルバから問い合わせがあると, 他のネーム・サーバーに問い合わせて結果を返してくれるタイプである。 一般にはキャッシュ ネーム サーバと呼ばれることが多いようだ。
ちなみにネームサーバには他に、 「答えを知っている問い合わせに対しては答えてくれるけれど, 知らない場合は,どこどこへ聞けと冷たく言い放つタイプ」がある。
また、キャッシュ ネーム サーバの一形態として、 他のキャッシュ ネーム サーバへ問合わせを転送する フォワード専用型キャッシュネームサーバもある。

広く普及しているネームサーバ実装の一つである djbdns には、 UDP 問合わせに失敗したり、 あるいは 512バイト以上の問合わせを行なう際は、 TCP にて問合わせを行なう機能があるが、 最初から TCP しか使わない (キャッシュ) ネームサーバがあると便利だろう。 すなわち、 遠方のネームサーバへ TCP で問合わせを転送する、 フォワード専用型サーバである。

フォワード専用型 キャッシュ ┌→ルート (root) ネームサーバ
リゾルバ──→キャッシュネームサーバ──→ ネームサーバ─┼→ネームサーバ
UDP TCP  ├→ネームサーバ
 └→

といった感じで TCP専用サーバを用いる。 この TCP専用サーバの作り方は簡単で、 djbdns の場合ならば次のようなパッチをあてるだけである。

dns_transmit.c で定義されている dns_transmit_start() 関数の末尾部分で、
    if (len + 16 > 512) return firsttcp(d);
    return firstudp(d);
となっている部分を、
    /* if (len + 16 > 512) */ return firsttcp(d);
    return firstudp(d);
で置き換える。

このようなパッチをあてた dnscache プログラムを、 環境変数 FORWARDONLY をセットした状態で呼び出せば、 「servers/@」に指定したキャッシュネームサーバへ TCP で問合わせを転送する、 フォワード専用型キャッシュネームサーバになる。

Filed under: システム構築・運用 — hiroaki_sengoku @ 08:59
2006年5月29日

ウォッチドッグ タイマ hatena_b

一週間ほど休暇で自宅を留守にしていたら、 運悪くちょうど半ばあたりで自宅の Linux サーバ (二台ある GCD ゲートウェイのうちの片方) がハングしてしまった。 きっかけは毎晩ハードディスの内容をバックアップするために動かしている rsync だったので、 メモリ不足 ? かなにかの原因でカーネルのバグを引き当ててしまったのか?

安定版でないカーネルを使っていて再びハングする危険もあるので、 早急にバージョンアップが必要なのだが、 その前に新しいカーネルで GV-MVP/RX2W を動かせるようにしないと... (^^;)
# ぱ研を参考にさせてもらっています (_O_)

GCD のゲートウェイは VRRP で二重化してあるので、 片方がハングしても問題無いのではあるが、 手動リセットするまでハングしっぱなしというのでは、 留守の期間が長いと二台ともハングする可能性を否定できない。
# UUCP スプールなど二重化されていないものもあるので、 全く問題が無いというわけでもない

そこでハングしてもなるべく自動的に復帰できるように ウォッチドッグ タイマ (Watchdog Timer) を仕掛けることにした。 つまり一定期間内にタイマがリセットされなければ、 ハングしたと見なして問答無用でカーネルを落とす仕掛けである。

ソフトウェアにどんなトラブルが起きても確実に再起動を行なわせるには、 ハードウェアで物理的にリセット スイッチを押すハードウェアを用いるのが 一番であるが、まずはお手軽にソフトウェア版を利用してみることにした。 ソフトウェアによるウォッチドッグ タイマの場合、 カーネル パニックが起きると当然機能しなくなるわけだから、 パニック時は自動的に再起動するように設定しておく必要がある。 rc.local 等で、

echo 60 > /proc/sys/kernel/panic
echo 1 > /proc/sys/kernel/panic_on_oops

を行なっておけば、パニック時は 60秒で再起動するようになる。

次に、ソフトウェア版ウォッチドッグ モジュール (softdog) をインストールする。 softdog.ko を /lib/modules/current 以下に置き、 /etc/modprobe.conf に以下の行を追加するだけ。

alias char-major-10-130 softdog
options softdog soft_margin=3600

ウォッチドッグ タイマというと、 普通は 60秒くらいに設定しておくものだとは思うが、 自宅サーバの場合、一時間くらいハング状態が続いてもそんなに困らない ;) のと、 あまりタイマの間隔が短すぎると、不用意に再起動してしまう恐れもあるので、 3600秒 (一時間) に設定している。 つまり一時間以内にタイマをリセットしないと、自動再起動が行なわれる。

後は適当な方法で、 /dev/watchdog に適当な文字 (ただし「V」を除く) を書込むだけである。 例えば、定期的に実行される sh スクリプトに、

echo "@" > /dev/watchdog

という行を追加しておくだけでよい。

Linux カーネルのドキュメント: linux/Documentation/watchdog/watchdog.txt には、

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
        int fd=open("/dev/watchdog",O_WRONLY);
        if(fd==-1)
        {
                perror("watchdog");
                exit(1);
        }
        while(1)
        {
                write(fd,"\0",1);
                fsync(fd);
                sleep(10);
        }
}

というサンプルが載っているが、タイマをリセットするプログラムを 動かしっぱなしにする方法だと、 ハングの仕方によってはこのプログラムが動き続ける (つまり自動再起動が行なわれない) 危険がある。 私の自宅サーバの場合は、たとえカーネルが生きていても、 リモートから ssh ログイン出来ない状態なら使い物にならないわけで、 外部から ssh ログイン出来る場合のみタイマのリセットが行なわれるようにした。 つまり、外部から

ssh server "echo @ > /dev/watchdog"

に相当することを定期的に (一時間以内の間隔で) 行なえばよい。 この場合、/dev/watchdog に文字を出力する都度、 /dev/watchdog をクローズすることになり、

SoftDog: Unexpected close, not stopping watchdog!

というメッセージが syslog に出力されるので、 果たしてきちんとタイマのリセットが行なわれているのか不安になるが、 softdog モジュールのソースを見ると、 このメッセージが出力される場合は常に 「softdog_keepalive()」呼び出しが行なわれているので問題ない。

Filed under: システム構築・運用 — hiroaki_sengoku @ 07:51
2006年5月19日

VPN-Warp relayagent フリー ダウンロード hatena_b

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 試用ライセンス提供開始のお知らせ」を参照願います。

Filed under: システム構築・運用 — hiroaki_sengoku @ 07:17
2006年5月9日

盗まれたノートPC を外部から操作する方法 (VPN-Warp 応用編 2) hatena_b

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カードなどの、 よりセキュアな場所に置いて利用することもできます。

Filed under: システム構築・運用 — hiroaki_sengoku @ 09:54
2006年5月6日

VPN-Warp 応用編 (1)

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@klab.org 宛に、

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

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

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

Filed under: システム構築・運用 — hiroaki_sengoku @ 07:46
2006年5月2日

VPN-Warp 入門編 (6)

前回は、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 経由でアクセスするようになります。

Filed under: システム構築・運用 — hiroaki_sengoku @ 15:13
2006年4月26日

VPN-Warp 入門編 (5)

前回は、リレーサーバへのアクセスは https なので、 stone 以外のソフトウェアも使えるというお話をしました。 一例として OpenSSL 付属の s_client を使ってみたわけですが、 https を扱うクライアントソフトウェアといえば普通は WWW ブラウザですね。 というわけで今回は WWW ブラウザでアクセスする方法を紹介します。

ブラウザで https://relay.klab.org/ をアクセスすると、 次のようなリクエストヘッダが SSL で暗号化されて、 リレーサーバ relay.klab.org へ送信されます。

GET / HTTP/1.1
Host: relay.klab.org
User-Agent: Mozilla/5.0
...
(空行)

リレーサーバは SSL クライアント認証を要求するので、 ブラウザは証明書ストアにある SSL クライアント証明書を使用して 認証を行ないます。 利用できる SSL クライアント証明書が複数ある場合は、 ブラウザが証明書の選択画面をユーザに提示し、ユーザが選ぶことになります。

リクエストヘッダを受け取ったリレーサーバは、 同じ証明書を持つ relayagent からの接続が存在すれば、 その relayagent との通信を確立させるのでした (VPN-Warp 入門編 (3)を参照してください)。

つまりリクエストヘッダに続いて送られるデータがポートフォワード先に送信され、 ポートフォワード先から返されたデータが、ブラウザに届きます。

が、ちょっと待ってください。 前回までに説明してきたポートフォワードの場合、 リクエストヘッダ (のようなもの) は stone が挿入した、 いわばダミーのリクエストヘッダでした。 ダミーですから、リクエストヘッダ (のようなもの) は ポートフォワード先に送信する必要がなかったのですが、 ブラウザを使う場合は、リクエストヘッダはホンモノです。 リクエストヘッダも込みでポートフォワード先の WWW サーバに送る必要があります。

VPN-Warp 入門編 (3)で 説明した relayagent の設定では、 「-n」オプション (relayagent をポートフォワーダとして使う、という意味) を 指定したことを覚えていますでしょうか? 実は、この「-n」オプションというのが、 relayagent にリクエストヘッダ (のようなもの) を取り除くことを指示する オプションなのです。 だから「-n」オプションを指定しなければ、 リクエストヘッダも含めてポートフォワード先 (つまり WWW サーバ) に送られます。

例えば relayagent の設定ファイルは次のような感じになります:

-k private.pem
-c cert.pem
relay.klab.org:443
intra:80

intra:80 がポートフォワード先の WWW サーバですね。 このような設定で relayagent を実行しておくことにより、 ブラウザが https://relay.klab.org/ へアクセスすると、 リクエストヘッダ込みで intra:80 へアクセスが行なわれます。

これで問題なくブラウザで intra:80 へアクセスできる...
場合もあるとは思いますが、 二点ほど注意すべき点があります。

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

少々ややこしい問題ですので、次回まわしにしましょう。

Filed under: システム構築・運用 — hiroaki_sengoku @ 08:11
2006年4月21日

VPN-Warp 入門編 (4)

前回は、VPN-Warp をポートフォワーダとして使う場合の、 基本的な設定方法について説明しました。 ローカルホストの 8080番ポートを、 リモートの intra:10080 へフォワードする場合に、 ローカルホストで stone を走らせる方法について説明したわけですが、 stone はもともと VPN-Warp 専用のソフトウェアというわけでは決してなく、 stone は汎用リピータです。

つまり stone を使わなくても、 https リクエストをリレーサーバに送信することができるソフトウェアなら なんでも使うことができます。

試しに OpenSSL 付属の s_client コマンドを使ってみましょう:

% s_client -cert relay,10020-cert.pem -key relay,10020-priv.pem -connect relay.klab.org:443
CONNECTED(00000003)
        ...
subject=/C=JP/postalCode=106-6122/ST=Tokyo/L=Minato-ku/streetAddress=6-10-1 Roppongi/streetAddress=Roppongi-Hills Mori-Tower 22F/O=K Laboratory Co.,Ltd/OU=HTTP/OU=InstantSSL/CN=relay.klab.org
        ...
---
GET / HTTP/1.1

HTTP/1.1 200 OK

:irc.klab.org NOTICE AUTH :*** Looking up your hostname...
:irc.klab.org NOTICE AUTH :*** Found your hostname

最初の行の s_client コマンドの実行と、 後ろの方の「GET / HTTP/1.1」および続く改行二回が手で入力した部分で、 あとは s_client コマンドの実行結果です。 クライアント認証を行なうため、s_client の引数に証明書の

公開鍵 (-cert オプション)
relay,10020-cert.pem
秘密鍵 (-key オプション)
relay,10020-priv.pem

を指定しています。s_client は SSL 接続した後、サーバ証明書の内容などが、 どばっと出力されますが、その後「---」を表示してセッションが確立します。

そして、リクエストヘッダ (のようなもの)

GET / HTTP/1.1
(空行)

を送ると、レスポンスヘッダ (のようなもの)

HTTP/1.1 200 OK
(空行)

が送られてきて、ポートフォワード先とのセッションが確立します。 この例の場合では、確立した直後にデータが送られてきていますね:

:irc.klab.org NOTICE AUTH :*** Looking up your hostname...
:irc.klab.org NOTICE AUTH :*** Found your hostname

これは、KLab 社内の IRC サーバから送られてきたデータです。 KLab では技術的な打ち合わせは IRC で済ませてしまうことも多く、 隣の席にいる人との打ち合わせでさえ、IRC で行なうケースがあります。 IRC で話しておけば文字で残りますし、 リモートにいる人も議論に参加できるので、とても便利です。

KLab 社内 IRC はあまりに常用されているので、 いつでもどこでも IRC に参加しておきたいわけで、 そういうとき VPN-Warp はとても便利です。 つまり、stone をノートPC 上で NT サービスとして走らせておくだけで、 ノートPC がインターネットにつながってさえいれば、 ダイアルアップでつながっていようと無線LAN でつながっていようと関係なく、 6667番ポートを常に社内IRC サーバへポートフォワードすることができて、 ノートPC 上の IRC クライアントで localhost:6667 へ接続して 常にチャット状態にしておける、というわけです。

Filed under: システム構築・運用 — hiroaki_sengoku @ 12:56
2006年4月19日

PPPoE を横取りするには?

GCD LAN内で Real Player コンテンツを再生しようとしたら、 GCDゲートウェイで、外向きポート554番をブロックしていたので、 とりあえず特定の接続先だけ解除する。

これでコンテンツを見られると、思って再生ボタンを押すと、 いきなりゲートウェイがネットワークから見えなくなった。 NICごと死んだのか、IP, IPv6 ともに反応なし。 二つあるNICの両方が無反応なので、 原因はドライバより上のレベルにありそう。 あいにくゲートウェイにはディスプレイカードを入れていないので、 シリアル(RS-232C)経由でログインしてみると、 iptables のログが延々と出力されている。 大半が、root ネームサーバのポート53番から。 つまり DNS 問い合わせに対する応答パケット。 ということは、PPPoE が生きている。これはマズイ。

機能しなくなったゲートウェイが PPPoE をつかんだままでは、 MASTER に昇格したもう片方のゲートウェイが PPPoE できない。 正確に言えば、PPPoE 自体はできるのだがプロバイダがどちらの ゲートウェイへパケットを送ってくれるかは運次第。 事実、ほとんどのパケットは機能しなくなったゲートウェイ側に 届いたので、GCD からインターネットへの通信が事実上不可能になった。

冗長構成を組むとき最も難しいのが、いかにして中途半端な死に方を 回避するかだが、死んだゲートウェイに PPPoE を止めさせるのも、 なかなか難しそうだ。

  1. PPPoE している機能不全状態のゲートウェイには頼らず、
  2. PPPoE 先のプロバイダ側には手が出せない、

と仮定すると、どうすればいいだろうか。 機能不全状態のゲートウェイのNIC を LAN から切り離す仕掛けを 組み込むとかだろうか? (どうやって? ^^;)

Filed under: システム構築・運用 — hiroaki_sengoku @ 09:52
2006年4月18日

VPN-Warp 入門編 (3)

前回は、ssh のポートフォワーダと比較して、 VPN-Warp には次の特長があることを説明しました。

  1. ログインアカウントが不要
  2. アクセス対象はリレーサーバのみ

例えばローカルホストの 8080番ポートを、 リモートの intra:10080 へフォワードする場合、 次のように、ローカルホストで stone を、リモートホストで relayagent を、 それぞれ走らせます。

ローカルホスト         リレー           リモートホスト
    stone ─────→ サーバ ←──── relayagent─→ intra
  8080番ポート …………………………………………………→ 10080番ポート

stone も、relayagent も、リレーサーバに対しては 「クライアント」であることに注意してください。 また、ローカルホスト、リモートホスト、リレーサーバ、 いずれにおいても、ssh の場合のようなログインは必要ありません。

必要なのは、SSL クライアント認証だけで、 stone/relayagent はクライアント証明書を使って リレーサーバへアクセスする、というわけです。 実際、リレーサーバは stone/relayagent にとって、 普通の https サーバのように見えます。 リレーサーバは、stone/relayagent 双方から SSL 接続を受付け、 もし同一のクライアント証明書を持っている組があれば、 それらを結び付ける役割を果たします。

具体的な設定方法

stone/relayagent それぞれの具体的な設定方法を見ていきましょう。

stone の設定

stone の設定ファイルは次のような感じになります:

-q key=private.pem
-q cert=cert.pem
relay.klab.org:443/ssl,http localhost:8080 "GET / HTTP/1.1" --
  • -q key= から始まる行は、SSL 証明書の秘密鍵ファイル、
  • -q cert= から始まる行は、SSL 証明書の公開鍵ファイルの指定です。
  • relay.klab.org:443 はリレーサーバの指定、
  • 最後の localhost:8080 がローカルホストで listen するポートの指定です。

このような設定を行なうと、 stone は 8080番ポートで受けた接続を、 「https リクエストに乗せて」リレーサーバへ転送します。

「https リクエストに乗せて」というのはどういうことかというと、 例えば 8080番ポートに接続して「abcdefg」というデータを送信したとすると、 stone は以下のようなデータを、SSL で暗号化してリレーサーバへ送ります:

GET / HTTP/1.1

abcdefg

あまり「https リクエスト」のようには見えませんが (^^;)、 最初の一行がリクエストヘッダで、 空行をあけて、リクエストボディが続いている、と見なすこともできます。

実をいうとリクエストヘッダ (のようなもの) を送り終わった時点で、 任意のデータの送受信が可能で、 送信したデータデータはそのまま relayagent へ伝わります。 つまり、 8080番ポートが、relayagent へフォワードされる、というわけです。

relayagent の設定

relayagent の設定ファイルは次のような感じになります:

-n
-k private.pem
-c cert.pem
relay.klab.org:443
intra:10080
  • -n は、relayagent をポートフォワーダとして使う、という意味です。
  • -k から始まる行は、SSL 証明書の秘密鍵ファイル、
  • -c から始まる行は、SSL 証明書の公開鍵ファイルの指定です。
  • relay.klab.org:443 はリレーサーバの指定です。
  • 最後の intra:10080 がポートフォワード先の指定です。

このような設定で relayagent を実行すると、 relayagent は、リレーサーバに対して以下のような「ポーリング」を https を装って行ないます。

GET /KLAB/poll HTTP/1.1
X-Ver: 2 relayagent.cpp,v 1.32 W (TAKUMI)

「X-Ver:」の次の数字「2」で、relayagent - リレーサーバ間の プロトコルのバージョン番号を伝えています。 続く「relayagent.cpp,v 1.32 W (TAKUMI)」は単に relayagent のバージョンを (トラブル時の解析用として) 伝えているだけで、通常は参照されません。

このリクエストヘッダ (のようなもの) を送り終わると、 リレーサーバは次のようなレスポンスを返します:

HTTP/1.1 200 OK
X-Customer: nusers=10&type=3&expire=1262271599&digest=0123456789abcdef0123456789abcdef

この、https レスポンスヘッダ (のようなもの) を受信した後、 relayagent はレスポンスボディ待ち状態 (ポーリング状態) になります。 この状態の時、 同じ証明書を持つ stone からの接続があると、 その情報がレスポンスボディとして送られてきて、 relayagent はそれを受けて intra:10080 へ接続を行ない、 stone と intra:10080 との間の通信が確立する、というわけです。

Filed under: システム構築・運用 — hiroaki_sengoku @ 16:42
« Newer PostsOlder Posts »