daemontools/tcpserverによるデーモン管理

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

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

目次


【Part 2 デーモン管理の基幹ツール】

DJBツールには、それ単体で利用するのではなく、他のデーモンプログラムを起
動したり制御したりするものがある。これにより日常的な管理作業を効率化した
り、デーモンの起動に制限を掛けセキュリティを高めることができるようになる。
Part 2では、各種デーモン起動を一元的に管理するための daemontools と、ネッ
トワークサービスデーモンの起動制御を行なうための tcpserver について解説
しよう。Part3以降で解説するツールも、daemontools/tcpserver と組み合わせ
ることで最大限のパワーを引き出せるようになる。

■
■daemontools
■

●daemontoolsとは

Unixマシンが稼動しているとき、その上では様々な種類のサービスデーモンプロ
グラムが動いている。たとえば、仮名漢字変換エンジン、リモートログイン用デー
モン、メイルやWebサービスに関連したネットワークサービスデーモン、などな
ど数え挙げれば10や20は思いつくだろう。これらのデーモンプログラムはそれぞ
れ全く違う働きをするために作られたものであるが、プログラムの起動と停止、
という点に着目すると、どれも概ね次のような性質を持っているといえる。

	・動作定義ファイルが決まっていて、管理者が事前にサイトに合わせて
	  そのファイルを修正し、決められた場所に置く必要がある。

	・システム起動時にそのデーモンプログラムを起動する

	・動作定義ファイルを書き換えたらなんらかの手順で動作中のプログラ
	  ムに設定変更を通知する

	・プログラムのバージョンアップなどにより、動作中のデーモンプログ
	  ラムを止める(killする)ことがある

これらの共通した作業をする場合でも、利用するOSやデーモンプログラムによっ
て具体的な操作方法は大きく異なる場合がある。管理しているホストが多くなれ
ばなるほど操作方法を思い出すのが大変になる。

【ケース1】
	ネームサーバの設定ファイルを書き換えた。設定の読み直しをするには
	どうしたら良いだろう。

【ケース2】
	同様のことをするのに OpenSSH の場合はどうしたら良いだろう。

【ケース3】
	現在動作中のデーモン foo に HUP シグナルを送りたい。PIDを記録し
	たファイルもないようだ。AIXの場合どうやってPIDを調べればいいのだ
	ろう。

【ケース4】
	新しいデーモン foo を導入したい。NetBSDでシステム起動時に foo も
	起動されるようにするには、どこにどのように起動スクリプトを書けば
	良いのだろう。

【ケース5】
	では foo を RedHat Linux, Solaris8, IRIX にも導入したい場合はそ
	れぞれどうしたら良いだろう。

【ケース6】
	デーモン bar を導入したら、たくさんのログがでるのでファイルシス
	テムが溢れないか心配になった。ログローテートをさせたいのだが、
	FreeBSDの場合、HP-UXの場合はそれぞれどうやるのがもっとも楽なのだ
	ろうか。

【ケース7】
	業務で必要なデーモン baz を起動させておく必要があるのだが、開発
	中のプログラムなので異常終了することがある。bazの動作を常に監視
	していて、落ちたらすぐに起動したいのだが、どうしたら良いだろう。

いくつか実際の環境でありがちなケースを列挙したが、それぞれ何も調べずにす
ぐに具体的な操作方法を思いつくだろうか。筆者は無理である。少なくとも、ps 
コマンドやsyslogdのマニュアルを参照することだろう。あまり管理する機会の
無いシステムに触る場合はしばらく /etc ディレクトリで grep して rc ファイ
ル群をながめたりして、そのシステムの「御作法」を読み取る作業が必要となる。
/etc/rc.local に書けば良いシステムもあれば、特定のディレクトリにスクリプ
トを置き、引数として start と stop を理解するように書かねばならないシス
テムもある。

いずれにしても、Unix上でデーモンプログラムを利用する場合には、おおざっぱ
に言って

	作法の違うシステムの数 × 起動方法の違うデーモンプログラムの数

だけの操作方法を把握しておく必要があると言える。利用しているシステムが現
在一つだとしても、将来別の種類のUnixに興味が出てくる可能性もある。

これらの管理者の悩みを軽減することを目的としているものが daemontools で
ある。管理者として得られるメリットは、上記の問題点の裏返しとなる。

	* デーモンの起動/停止/設定ファイルの読み直し などを統一的インタ
          フェースで行なえる
	* 動作中のプログラムにシグナルを送るのが簡単
	* ログローテートを自動的に行なってくれる
	* プログラムが常時起動しているように監視してくれる

ここでは「デーモンプログラム」に関して適用できる利点と書いたが、自作プロ
グラムを常に起動しておきたい場合などにも応用が可能である。


●daemontoolsの動作概念

daemontoolsは、起動・停止・シグナル送信、などの処理を行なう必要のあるデー
モンをディレクトリ単位で管理する。ひとつのデーモンプログラムをフォアグラ
ウンドで起動するためのシェルスクリプト、ログを取る必要がある場合はそのた
めのスクリプトとログ採取ディレクトリなどをまとめて一つのディレクトリに納
めておく。そしてこのディレクトリをdaemontoolsが常に監視するディレクトリ
に置くことで、そのデーモンプログラムが自動的に起動される。

---[図 い]------------------------------------------------------------

    svscanプログラム
          ↓監視
   +---- /service ----------------------------------+
   |                                                |
   | +------------------+  +-------------------+    |
   | | デーモン起動	|  | デーモン起動      |    |
   | | シェルスクリプト |  | シェルスクリプト  |    |
   | +------------------+  +-------------------+    |
   |    (↑superviseから      (↑superviseから	    |
   |     自動起動される)         自動起動される)    |
   +------------------------------------------------+

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

具体的には、【図 い】のようにdaemontools附属の svscan プログラムが
/service ディレクトリを常に監視している。そこにデーモン起動に必要な情報
が詰められたディレクトリ(ディレクトリ名は任意)を置くと、svscan はこのディ
レクトリを supervise コマンドに渡す。superviseはディレクトリ内にある 
"run" というファイル名を起動スクリプトとして起動する。これにより指定した
デーモンプログラムが動きだすことになる。さらに、svc コマンドを利用すると、
run スクリプトによって起動されたデーモンプロセスに特定のシグナルを送信し
たりすることが簡単にできるようになる。

daemontoolsによるデーモンプログラムの管理方式を導入するための手順は以下
のようになる。

	1. daemontoolsのインストール

	   これは次節で解説する。

	2. デーモン管理ディレクトリの作成

	   daemontoolsでは、デーモンの起動に関する情報を一つのディレクト
	   リにおさめて管理する。その中に
	   "run" というファイル名でデーモンを起動するシェルスクリプトを
	   作成する。必ずデーモンがフォアグラウンドプロセスとして起動さ
	   れるようにする。

	3. svscanプログラムの起動

	   svscanは、デーモン管理ディレクトリの中に存在するディレクトリ
	   を常に監視していて、起動すべきサービスデーモンを自動的に	即座
	   に起動してくれる。管理ディレクトリとして /service ディレクト
	   リを作成し、これをsvscanコマンドに与える。

	4. デーモン起動情報ディレクトリの /service への配置

	   実際にはデーモン起動情報ディレクトリの実体を/service に置く必
	   要はなく、容量に余裕のある別のパーティションに置いて、そこへ
	   のシンボリックリンクを /service/ に作成するという形で管理する
	   ことになるだろう。たとえば、/usr/local/djbdns を管理用ディレ
	   クトリとしたなら

	     # ln -s /usr/local/djbdns /service

	   とするだけでsvscanが(5秒以内に)自動的にこれを検出し、ただちに
	   起動する。

それでは各ステップの手順を解説しよう。


●daemontoolsのインストール

執筆時の最新版は daemontools-0.76 で、
http://cr.yp.to/daemontools/install.html
から入手できる(または本誌付録CD-ROMにも格納)。

ソースアーカイブ(daemontools-0.76.tar.gz)を展開し、インストールの
ためのスクリプトを起動する(コラム1 参照) 。


-------[コラム1 /package hierarchy ]----------------------------------

筆者自身、必要なときにしかDJBのWebページを見に行かないので、いつ頃からそ
うなったのかは把握していないが、最近のDJBのソフトウェアには /package
hierarchy というポリシーが課せられている(下記URL参照)。

http://cr.yp.to/unix.html (Filesystem layout)
http://cr.yp.to/slashpackage.html

これは、システムにインストールするパッケージを「効率的」に管理するための
ファイルシステム内での配置方法に関する方法論である。daemontools でもバー
ジョン0.76ではこの /package hierarchy 方式にしたがった構成となった。以前
のDJBツールでおなじみの

	# vi conf-cc
	  (インストールパスを決定)
	# make setup check

という方式はもはや使えず、

	# package/install

とすると必要なコマンド群が /command ディレクトリ以下にインストールされ、
そこにある各コマンドが /usr/local/bin にシンボリックリンクされる。くわし
くは上記URLページを見れば分かるが、これは全てのパッケージは /package ディ
レクトリに特定のルールにしたがってインストールし、そのうちユーザが利用す
るコマンドは /command ディレクトリにも用意するというものである。

本来DJB特集を組むのなら、DJBの提唱する /package hierarchy に則って解説を
進めるべきなのかもしれない。そこが一番悩んだ点であるが、今回はあえてこの
hierarchy を無視する方法を選んだ。実際問題としてこのポリシーを抵抗無く受
け入れられる管理者はほとんどいないのではないだろうか。「一貫したポリシー」
がDJBツールの良いところである反面、感覚的に敬遠される一因となっているの
も否めない。従うことに意味のあるポリシーは受け入れ、従わなくても実害のな
いポリシーは受け入れないという方針で解説中のインストールを進める形を採っ
た。自分の好みの場所にインストールして是非 daemontools の恩恵を実感して
みて欲しい。

もちろん、/package hierarchy を全面的に受け入れてインストールするのも選
択のひとつである。その場合、http://cr.yp.to/daemontools/install.html に
ある手順どおりにすれば良い。ただし、今回紹介するツール群のうち、/package
hierarchy に入るのは daemontools-0.76 だけである。

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


	# mkdir -p /package
     	# chmod 1755 /package
	# mv daemontool-0.76.tar.gz /package
	# cd /package
	# tar vzxpf daemontool-0.76.tar.gz
	# cd admin/daemontools-0.76
	# package/install

上記のコマンドをスーパーユーザ権限で実行すると /command, /package ディレ
クトリにインストールされ、システムの初期化ファイル(/etc/inittab または 
/etc/rc.local)にも daemontools サービスの起動(svscanの起動)が登録される
ので、再起動すれば利用できるようになる。デフォルトのインストール場所で問
題ない場合は上記手順で完了となるので、次節まで読み飛ばして頂いた構わない。

もし、別のディレクトリにまとめてインストールしたい場合は少し工夫が必要と
なる。まず最初の作業をスーパーユーザではなく一般ユーザ権限で以下のように
する。

	(ソースアーカイブのあるディレクトリで)
	% tar vzxpf daemontool-0.76.tar.gz
	% cd admin/daemontools-0.76
	% package/install

一般ユーザ権限では /command, /package ディレクトリに書けないので、コンパ
イルまでがうまく行く。インストールすべきコマンドは ./package/commands ファ
イルに書かれているので、ここに列挙されている実行プログラムをインストール
したいディレクトリにコピーすればよい。実際にコピーしたものが ./command 
ディレクトリにできているのでこの中のファイル全てをインストールしたい場所
にコピーするのが最も簡単だろう。ここでは /usr/local/daemontools/bin にイ
ンストールする場合を例にすると、以下のような手順となる。

	# mkdir -p /usr/local/daemontools/bin
	# cp command/* /usr/local/daemontools/bin

続いて svscan プログラムを起動するためのスクリプトを作成する。これは
command/svscanboot スクリプトを参考にインストールディレクトリに応じて書
き換えたものである。

例: ---[ /etc/startsvscan ]----- ここから

#!/bin/sh
PATH=/usr/local/daemontools/bin:/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin

exec </dev/null
exec >/dev/null
exec 2>/dev/null

/usr/local/daemontools/bin/svc -dx /service/* /service/*/log

env - PATH=$PATH svscan /service 2>&1 | \
env - PATH=$PATH readproctitle service errors: ................................................................................................................................................................................................................................................................................................................................................................................................................

----------------------------------------------------- ここまで

スクリプト2行目のPATH変数の設定は、システムで利用する主要なコマンドサーチパ
スに加え daemontools をインストールしたディレクトリを必ず含めるように注
意する。また、スクリプト最終行のピリオドは、svscanの状態を表示するための
ローテート領域(後述)というダミー文字列で十分なカラム数だけ書くことが求め
られている。ちなみにサンプルスクリプトではピリオドが400個となっているが
必ずしも厳密に個数合わせする必要はない。

ここで作成したスクリプトがシステム起動時に実行されるようにスタートアップ
ファイルを修正する(この部分は後回しにして、後述する全ての動作実験が済ん
でからにしても良いだろう)。

スタートアップファイルにはいくつかの流儀がある。システムに応じて適宜修正
するファイルが異なる。

	* /etc/inittab ファイルのあるシステム(SystemV系Unix, Linux全般)

	  /etc/inittab ファイルに以下の行を追加する(註: この内容はソース
	  ディレクトリの package/boot.inittab にある)

	  ------------- inittab 追加分 ここから ------------------
SV:123456:respawn:/etc/startsvscan
	  -------------  ここまで --------------------------------

	* /etc/rc.local ファイルのあるシステム(伝統的BSDシステム)

	  /etc/rc.local ファイルに以下の行を追加する(註: この内容はソー
	  スディレクトリの package/boot.rclocal にある)

	  ------------- rc.local 追加分 ここから ------------------
csh -cf '/etc/startsvscan &'
	  -------------  ここまで --------------------------------


最後に daemontools が各種デーモンを管理するときに使用するディレクトリを
作成する。このディレクトリは先に説明した /etc/startsvscan スクリプト内で
指定しているディレクトリである。

	# mkdir /service
	# chmod 755 /service

/service が各種デーモンの監視ディレクトリとなる。監視ディレクトリを / パー
ティションに作ることに抵抗感やファイルシステムの容量などの不安感を覚える
かもしれないが、

	* daemontools自身もデーモンとして動作するため、そのプロセスが
	 「掴む」ファイルシステムは / パーティションだけのほうが望ましい
	* 各種デーモンの起動・停止処理を行なうときにコマンドラインでこの
	  管理ディレクトリを指定するのですばやくタイプできるパス名の方が
	  効率的である
	* /service ディレクトリに作成するのは通常シンボリックリンクだけ
	  なのでディスク使用量はわずかである

などの観点から、daemontoolsを(本運用で)利用する場合にはデフォルトのまま 
/service ディレクトリを監視ディレクトリとすることを強くお勧めする。

デーモン監視ディレクトリを作成したらシステムを再起動するか、以下のコマン
ドによりsvscanプログラムを起動する。

	# csh -cf '/usr/local/daemontools/bin/svscanboot &'


●daemontoolsによるデーモン管理

まず最初に考えなければならないのはシステムで利用しているデーモンのうち、
どれを daemontools で管理するかを決定することだろう。冒頭で説明したよう
に daemontools を利用することで得られるメリットはいくつかあるのだが、あ
らゆるデーモンプログラムを daemontools 管理にできるとは限らないし、しな
い方が望ましいものもある。一概には言えないが、以下のようなプログラムが
daemontoolsに向いているものだろう。

	* DJBツール(管理しやすいように設計されている)
	* フォアグラウンドで動く(動かせる)もの
	* 常に起動している必要性の高いもの
	* 過負荷などで異常終了しやすいもの

なお、フォアグラウンドプロセスとして動かすことのできないデーモンプログラ
ムでも、fghackコマンド(後述)を利用してdaemontools管理下に置ける場合があ
る。実際に試してみるのが確実だろう。繰り返しになるが、現在 /etc/rc* など
で起動しているデーモンプログラムも、なるべくdaemontools管理下に移動して
おくのは意味がある。そうしておけば、将来別のシステムを使っても、全く同じ
管理手順でそのデーモンプログラムを導入できるというメリットが得られる。

システムで利用するデーモンプログラムを daemontools の管理下に置く場合の
初期手順はおおむね次のようになる。

	1 デーモンプログラムを起動するためのスクリプトなどを含んだディレ
          クトリを作成する(以下これを「サービスディレクトリ」と呼ぶ)

	2 サービスディレクトリ内に run スクリプトを作成し、そこにデーモ
          ンプログラムをフォアグラウンドで起動するコマンドラインを書く

	3 ログを取りたい場合はサービスディレクトリ内に log ディレクトリ
	  を作成しログを取るためのコマンドラインを run スクリプトに記述する

	4 実際にデーモンがフォアグラウンドで起動できるか確認する

	5 うまくいったら /service(監視ディレクトリ)にシンボリックリンクを
	  張る

ここでは、例として /usr/local/foo ディレクトリにインストールしたデーモン
プログラム(図 ろ)を daemontools 管理下に置く場合の作業を示そう。

---[図 ろ]------------------------------------------------------------

       /usr/local/foo/
		   |
		   +--/bin/
		   |    +--- fooclient
		   |    +--- fooconfcheck
		   |    +--- foostat
		   |	+--- fookill
		   |
		   +--/sbin/
		   |    +---- food
		   |
		   +--/man/
		   :    +---/man1/
		   :	:    +--- fooclient.1
		   :	:    :
----------------------------------------------------------------------

【前提】--------------------------------------------------------------
fooサービスプログラム(群)の、常駐デーモンプログラムは sbin/food コマンド
で、起動すると自動的にバックグラウンドプロセスに移行する。ただし、-d オ
プションを付けると各種メッセージを標準エラー出力に出し、フォアグラウンド
プロセスで動くようになる。
----------------------------------------------------------------------

この前提に書かれたような動作はをするデーモンプログラムは比較的多いだろう。
実際に 1〜5 の手順を進めてみよう。

1. サービスディレクトリの作成

   後日管理しやすいように、デーモンプログラムと同じ名前のディレクトリを
   作成する。場所はどこでも構わない。ここでは、/usr/local/foo/ に作ろう。

	# mkdir /usr/local/foo/foo

2. run スクリプトの作成

   /usr/local/foo/foo ディレクトリ内に、run という名前のスクリプトを作成
   し、フォアグラウンド起動するコマンドラインを記述する。このときに foo
   サービスプログラムが動作するために必要な環境変数が定義されるように注
   意する。とくに重要なのはPATH変数だろう。これを忘れて run スクリプトが
   うまく動作せず、何度も起動を繰り返されるトラブルが起こりがちである。

	# cd /usr/local/foo/foo
	# touch run
	# chmod +x run
	# vi run
	  (runスクリプトを編集…たとえば以下の内容)
---[ /usr/local/foo/foo/run ]-----------------------------------------
#!/bin/sh
PATH=/bin:/usr/bin:/usr/local/foo/bin:/usr/local/foo/sbin
exec 2>&1
exec food -d
----------------------------------------------------------------------

   スクリプトの最後の行で実際のデーモンプログラムを起動することになるが、
   このとき exec でデーモンプログラムを起動する。これにより、スクリプト
   を実行しているプロセスIDがデーモンプログラムに引き継がれる。すると、
   svc コマンド(後述)を利用してデーモンプロセスに直接シグナルを送信でき
   るようになる。

3. ログディレクトリの作成

   サービスデーモンが出力するログを採取したい場合はサービスディレクトリ
   内にlogという名前のディレクトリを作成し、そこに run スクリプトを置く。
----
脚註:
daemontools-0.70 ではサービスディレクトリにstickyビットを立てる必要があっ
た(mode 1755)。0.70を利用する場合はlogディレクトリを作るだけでなく
chmod 1755もすることに注意する。
----
   daemontools 付属の multilog コマンドを利用するとログ管理がやりやすい。
   multilogコマンドは標準入力から受け取った内容(行)にタイムスタンプを付
   けたり、特定のパターンにマッチするものを選択したり破棄したり別ファイ
   ルに書き出すなどの機能を持つ。最初はあまり凝らずに全ての出力をログファ
   イルに書き出してみよう。log/run スクリプトは次のようにする。

	# mkdir /usr/local/foo/foo/log
	# cd /usr/local/foo/foo/log
	# touch run
	# chmod +x run
	# vi run
	  (runスクリプトを編集…たとえば以下の内容)
---[ /usr/local/foo/foo/run ]-----------------------------------------
#!/bin/sh
exec multilog t ./main
----------------------------------------------------------------------

   これにより、foodデーモンプログラムの出力したメッセージ全てが
   /usr/local/foo/foo/log/main/current ファイルに書き出される。

4. 動作確認

   実際にrunスクリプト経由でデーモンプログラムを起動してみる。

	# cd /usr/local/foo/foo
	# ./run

   プロセスがフォアグラウンドで起動し、実際に本来供給されるべきサービス
   が利用できているかを確認する。確認できたら停止して次の手順に進む。

5. 監視ディレクトリへのシンボリックリンクの作成

   作成したサービスディレクトリへのシンボリックリンクを監視ディレクトリ
   に作成する。

	# ln -s /usr/local/foo/foo /service

   5秒以内にfooサービスが起動する。



●svcコマンドによるデーモン制御

ひとたび /service ディレクトリにサービスディレクトリを置くと、svscanによっ
てsuperviseが起動され、さらにsuperviseによってrunスクリプトが実行される。
以後はそのデーモンをsvcコマンドで制御できるようになる。以下の説明では、
/serviceディレクトリ内に作成したシンボリックリンクのことを単に「サービス」
と呼ぶことにする。たとえば今回の例では /service/foo が「サービス」に該当
する。

svc コマンドは

	# svc  オプション  サービス

の形式で起動する。指定したオプションに応じて、サービス指定されるデーモン
プロセスにシグナルなどを送ったりすることができる。オプションは以下の通り
である。

	-u  デーモンを上げる(Up)。
	-d  デーモンを落とす(Down)。再起動しない。
	-o  デーモンを上げる。ただしデーモンが止まっても再起動しない(Once)。
	-p  デーモンに STOP シグナルを送る(Pause)。
	-c  デーモンに CONT シグナルを送る。
	-h  デーモンに HUP シグナルを送る。
	-a  デーモンに ALRM シグナルを送る。
	-i  デーモンに INT シグナルを送る。
	-t  デーモンに TERM シグナルを送る。
	-k  デーモンに KILL シグナルを送る。
	-x  supervise がただちに終了するよう指示する(eXit)。
	    何かトラブルが起きたとき以外はこのオプションは
	    利用しないはずである。

よくある使用例としては、サービスデーモンの設定ファイルを修正したあとに、
それを通知するために走行中のデーモンプロセスにHUPシグナルを送る、という
作業があるが、そのような場合は

	# svc -h /service/foo

のようにすれば良い(註: root ユーザのコマンドサーチパス($PATH)に
daemontoolsをインストールしたディレクトリを含めておくのも忘れずに)。

サービスを再起動するには

	# svc -t /service/foo

とする。-t オプションでTERMシグナルが送られるので、サービスデーモンは一
度止まるが、superviseによってすぐに再起動される。

サービスを停止したいときは

	# svc -d サービス

とする。リブート後を含め本当に完全に停止したいときはサービスの管理ディ
レクトリに down という名前のファイルを作成してからsvc -d する。

	# touch /service/foo/down
	# svc -d /service/foo

また、サービスを落とし復活する予定もない場合は以下のようにシンボリックリ
ンクも消去してサービスを抹消する。

	# cd /service/foo
	# rm /service/foo
	# svc -dx . log


●daemontoolsのコマンド群

daemontoolsに付属する、その他のコマンド群についても使い方を解説しておこ
う。

【svok】

	# svok サービス

の形式で利用して、ディレクトリに対応するサービスデーモン(を監視している
superviseプロセス)が起動しているかどうか確認する。起動していれば終了コー
ド0を、していなければ100を返す。

【svstat】

	# svstat サービス

という形式で起動して、サービスデーモンの起動に関する統計情報を出力する。
良く利用するものとしては、現在daemontools管理下に置いているサービス全て
が正常稼動しているかどうか見るときに

	# svstat /service/*

とする。その結果
	/service/axfrdns: up (pid 229) 197662 seconds
	/service/dnscache: up (pid 232) 197662 seconds
	/service/ipopd: up (pid 224) 197663 seconds
	/service/smtpd: up (pid 226) 197663 seconds
	/service/tinydns: up (pid 231) 197662 seconds

のように全てが同じ秒数だけ上がっていれば正常だが、何度やっても

	/service/axfrdns: up (pid 229) 197662 seconds
	/service/dnscache: up (pid 232) 0 seconds
	/service/ipopd: up (pid 224) 197663 seconds
	/service/smtpd: up (pid 226) 197663 seconds
	/service/tinydns: up (pid 231) 1 seconds

のように、起動秒数が0〜1秒のものがある場合は、サービスデーモンの異常終了
とsuperviseによる自動再起動をくり返していることの現れである。このような
場合は直ちにサービスを止めて run スクリプトや、デーモンの設定ファイルな
どを見直そう。上記の出力例の場合であれば、dnscache, tinydns に設定ミスの
可能性があるので、

	# touch /service/dnscache/down /service/tinydns/down
	# svc -dx /service/dnscache /service/tinydns

としてから問題解決に当たる。解決したら down ファイルを削除しサービスを再
開する。

	# rm /service/dnscache/down /service/tinydns/down
	# svc -u /service/dnscache /service/tinydns

【svscan】
superviseのさらに上位に位置するデーモンとして、複数のデーモンの起動状態
を監視し、常に上がっているようにコントロールする。慣習的に /service を利
用する。svscan起動時には環境変数PATHにdaemontoolsのコマンド群のあるディ
レクトリを含める必要がある。

【fghack】
通常 supervise が起動する ./run スクリプトではデーモンプログラムをフォア
グラウンドで起動することが期待されている。自動的にバックグラウンド動作に
移行するデーモンプログラムをsuperviseで管理したいときは起動スクリプト
(run)のデーモン起動をfghack経由で行うようにするとうまくsuperviseで管理で
きるようになる場合もある。具体的には run スクリプトで

	#!/bin/sh
	echo Starting foo daemon...
	exec fghack food

などのようにする。ただし、superviseからこのデーモンにシグナルを送ること
はできなくなる。fghackはデーモンプロセスとの間にパイプを形成して、そのパ
イプがcloseされるまで動き続ける。通常子プロセスは親プロセスが開いたパイ
プを継承し、子プロセス終了までこれが閉じられることは無い。しかし、ものに
よってはありとあらゆるファイルディスクリプタをcloseしてしまうデーモンも
あり、この場合fghackもうまく行かない。そのような場合は、ファイルディスク
リプタ0をcloseしていない可能性に賭けて

	#!/bin/sh
	exec fghack baddaemon <&-

とするとうまく行くこともある。ただし、これでフォアグラウンドで動くように
見えても、サービスデーモン本来の動きが正常にできないものもあるかもしれな
い。そのような場合は、fghackで四苦八苦するよりもソースプログラムをハック
してバックグラウンドに落ちないように変えてしまった方が手っ取り早い。

【pgrphack】
新しいプロセスグループを生成してからプログラムを起動する。サービスデーモ
ンの種類によっては、自分が受け取ったシグナルを同じプロセスグループのもの
全てに送るという行儀の悪いものがある。そうしたデーモンにシグナルを送ると
svscanなどもまるごと落ちてしまうことになるので、pgrphackを利用する。run
スクリプトを以下のようにする。

	#!/bin/sh
	echo starting foo daemon...
	exec pgrphack food



【multilog】
標準入力をさまざまなログに出力する。multilogは通常 supervise によって自
動起動される /service/foo/log/run スクリプトで起動される。起動は

	multilog スクリプト

という書式で行ない、「スクリプト」の部分にはログを採取する場合のアクショ
ンを決定するキーワードを複数記述できる。アクションを複数書いた場合先頭か
ら順に全てが評価される。以下、multilogの解釈するアクションを列挙する。

  ・タイムスタンプ関連

	* t

	アクション t は、入力行全ての行頭にTAI64N形式のタイムスタンプを
	付加する。たとえば、入力行が

	a
	b
	c

	だった場合、各行がmultilogに渡された時刻に応じて以下のようなタイ
	ムスタンプが付けられる。

	@400000003cbfe11501936b64 a
	@400000003cbfe11607ceb5ec b
	@400000003cbfe11619e52874 c

	@記号に続く24桁の16進値がTAI64N形式による時刻表記である。これは、
	後述するtai64nlocalコマンドで人間の理解できる時刻表記に変換する
	事ができる。アクション t は必ずmultilogに与えるスクリプトの先頭
	に置かなければならない。

  ・ログの出力と自動ローテート

	* <ディレクトリ>

	ピリオド(.)またはスラッシュでディレクトリ名を指定すると、
	multilogはそのディレクトリにログファイルを格納する。ディレクトリ
	が存在しない場合はmultilogが作成する。このディレクトリ内の
	current という名前のファイルに現在のログ出力が随時追加されていく。
	current ファイルが巨大になった場合は自動的に別のファイルにリネー
	ムされ、新たに current ファイルが作られる。古くなったログファイ
	ルの名前は、そのファイルへのログ出力が完了した時点のタイムスタン
	プで始まるものになる。

	* sサイズ

	アクション s はすぐ後に「サイズ」を記述して、ログファイルの最大
	サイズを指定する。multilogは、実際にはこのサイズきっかりでローテー
	トするわけではなく、改行を受け取ったときに最大サイズまで残り2000
	バイトを切っていたらローテート処理をする。最大サイズとして指定で
	きるのは 4096〜16777215バイトで、デフォルトの最大サイズは99999バ
	イトである。

	* n世代数

	アクション n はすぐあとに「世代数」を指定して古くなったログファ
	イルを何個保持するか指定する。アクション n は、それよりあとに書
	いた <ディレクトリ> アクションに対して有効となる。multilogは、
	currentファイルをリネームしたあとで「世代数」で指定した個数以上
	ファイルがあった場合に古いファイルから削除する。指定できる数値は
	2以上で、デフォルトのログファイル個数は10である。

	* !プロセス

	アクション ! はすぐあとに「プロセス」を指定して、最大サイズ(付近)
	に達した current ログファイルを古いログファイルに移動するときに、
	単にリネームするのではなく外部プロセスに渡した結果を保存する。た
	とえば各ログファイルに行番号を付けたものを古いログファイルに残し
	たい場合には以下のような log/run スクリプトとなるだろう。

	#!/bin/sh
	exec multilog !"cat -n" ./main

	「プロセス」がスペースやパイプライン(|)などの文字を含む場合は、
	クォートすることに注意する。

  ・行の選択

	* -パターン
	* +パターン

	アクション - は直後に「パターン」を指定して、入力行のうち「パター
	ン」にマッチする行をログ出力対象から除外する。同様にアクション + 
	は直後に「パターン」を指定して、「パターン」にマッチする行を選択
	する。パターンには * とそれ以外の文字が指定でき、* は任意の文字
	列にマッチする。ただし、* の直後に文字を指定した場合、* はその文
	字以外の文字列にマッチする(最短マッチ)。パターンマッチングは、行
	全体に対して行なわれる。したがって、

	+hello

	というアクションは "hello" という行を選択するが、"hello world"
	という行は完全マッチしないので選択しない。また、アクション t で
	各行にタイムスタンプを付加している場合は、「パターン」にもタイム
	スタンプ部分を追加する必要がある。たとえば、あるログ行 

	fatal: out of memory

	にマッチするものを選択したい場合タイムスタンプを付加すると

	@400000003b4a39c23294b13c fatal: out of memory

	のようになるので、この行にマッチするもののみをログ採取したい場合
	は、

	multilog t '-*' '+* fatal: *' ./main

	とする。+ の直後に指定した "* " がタイムスタンプとその直後の空白
	にマッチする。


  ・ステータスファイル

	* =ファイル

	アクション = は直後に「ファイル」を指定して、その時点で選択され
	ている行の内容で「ファイル」の中味を置き換える。ただし、長すぎ
	る行は1000バイト(+1バイトの改行)に丸められる。たとえば、

	multilog -* +STAT* =log/status

	というアクション列を与えると、"STAT" という語で始まる行が来るた
	びに、その内容を log/status にコピーする。

  ・警告

	* e

	アクション e は、その時点で選択されている行の内容(最大200バイト)
	を標準エラー出力に書き出す。

最初に紹介した log/run スクリプトに記述した

	exec multilog t ./main

は、全ての行にタイムスタンプを付けて、log ディレクトリ内の ./main ディ
レクトリにログ出力することを意味していたということになる。

【readproctitle】
標準入力から得た文字列を、メモリ上で最新メッセージを保持するローテート領
域にコピーし、psコマンドで見たときに読めるようにする。

	# readproctitle 固定文字列(群) ローテート領域

という書式で起動する。最後の引数「ローテート領域」に標準入力がコピーされ
る、標準入力から来た新しい行は「ローテート領域」の右の方からローテートし
ながら埋め込まれていく。このコマンドは、svscanプログラムの起動時に利用さ
れているので、実際の使用例として参考にすると良いだろう。

【tai64n】
標準入力の各行に対してその行を読んだときのタイムスタンプをTAI64N形式でつける。


【tai64nlocal】
TAI64N形式のタイムスタンプ文字列を可読形式に変換する。入力行のうち、行頭が
@で始まるTAI64N形式のタイムスタンプだった場合、tai64nlocalはそれをISOフォー
マットのローカルタイム表示(YYYY-MM-DD HH:MM:SS.SSSSSSSSS)に変換する。

【setuidgid】

	# setuidgid アカウント プログラム

という書式で起動し、指定した「アカウント」の UID, GIDに setuid/setgid し
たうえで別の「プログラム」を起動する。daemontools経由で起動するサービス
デーモンはrootで動かす必要があるものも多いだろうが、ログ採取をする
multilogプロセスはたいていroot権限で動かす必要は無い。このようなときには
ログ採取専用アカウントを作り、

	#!/bin/sh
	exec setuidgid ログアカウント multilog t ./main

などのようにすると良い。

【envuidgid】

	# envuidgid アカウント プログラム

という書式で起動し、指定した「アカウント」のUIDとGIDを、それぞれ環境変数
UID, GIDに設定して別のプログラムを起動する。

【envdir】

	# envdir ディレクトリ プログラム

という書式で起動し、指定したディレクトリにあるファイル名と同じ環境変数の
値を、そのファイルの内容にセットしたうえで別のプログラムを起動する。ファ
イルが0バイトならその環境変数自体を未定義にする。

【softlimit】

	# softlimit オプション プログラム

の書式で起動し、「オプション」で指定したリソースリミットを設定した上で
「プログラム」を起動する。オプションには以下のものが任意個指定できる。

	-m N	-d N -s N -l N -a N を指定したのと等価
	-d N	1プロセスあたりのデータセグメントリミットをNバイトに制限
	-s N	1プロセスあたりのスタックセグメントリミットをNバイトに制限
	-l N	1プロセスあたりのロックされた物理ページサイズをNバイトに
		制限(*)
	-a N	1プロセスあたりのセグメントサイズをNバイトに制限(*)
	-o N	1プロセスあたりのファイルディスクリプタ数をN個に制限(*)
	-p N	1ユーザIDあたりのプロセス数をN個に制限

	-f N	出力ファイルサイズをNバイトに制限
	-c N	コアダンプファイルのサイズをNバイトに制限


	-r N	常駐サイズをNバイトに制限。ただし、物理ページが溢れない
		限り強制されない。
	-t N	CPUタイムをN秒に制限。ただし、N秒後にプロセスが SIGXCPU
		シグナルを受信することが無い限り強制されない。

	(*)印はOSによっては制限を掛けられないことがある。

【setlock】

	# setlock オプション ファイル プログラム

の書式で起動し、指定した「ファイル」をロックファイルとして別のプロセスを
排他起動する。オプションには以下のものがある。

	-n	遅延させない。他プロセスにロックされていたらsetlockは即
		座に諦める
	-N	遅延させる(デフォルト)。他プロセスにロックされていたら
		setlockはロックが獲得できるまで待つ
	-x	ロックファイルが開けない(または作成できない)場合、
		setlockは終了コード0でexitする。
	-X	ロックファイルが開けない(または作成できない)場合、
		setlockはエラーメッセージとともに終了コード非0でexitする
		(デフォルト)。


●daemontools導入のまとめ

最後に、daemontoolsを利用したデーモン起動の作業行程をまとめておこう。

	1. そのデーモン専用サービスディレクトリを作る。
	2. サービスディレクトリ内に run という名前のスクリプトを作る。
	   runスクリプト作成における注意点は以下のとおり。
	   * デーモンをフォアグラウンドで動かす
	   * exec でrunスクリプトのプロセスを置き換えて起動する
	   * 起動するコマンドが確実に見つかるよう、フルパスで記述するか
	     PATH変数を確実にセットする
	3. ログを取りたい場合はサービスディレクトリ内に log ディレクトリ
	   を作り、さらにそこに run スクリプトを置く
	4. 手動で run スクリプトを起動しフォアグラウンドで正常に動くこと
	   を確認
	4. 最後に /service ディレクトリにシンボリックリンクを張る

これらを確実にこなして行けば、どんなOSどんな環境でも統一的な操作でデーモ
ン管理が効率的に行なえるようになるだろう。


■
■tcpserver
■

●tcpserverとは

tcpserverは、ucspi-tcp パッケージに含まれるコマンドである。ucspi-tcp は、
UCSPI(UNIX Client-Server Program Interface) を実装したもので、UNIX上で動
作するクライアントサーバコミュニケーションのコマンドラインインタフェース
を供給するプログラム(群)である。これを利用するとネットワーク間通信を意識
しないプログラムでもネットワークサーバとして動かせるようになる。極端な例
では、シェルスクリプトでもネットワークサービスデーモンとして動かすことが
可能になったりする。さらにたとえば、tcpserver コマンドを利用して

	% tcpserver 0 5555 ruby

とすると、rubyプログラムがtcp/5555番ポートで待ち受けて、ネットワーク越し
にRubyインタプリタを利用できるようになる(あまり意味は無いが…)。このよう
に、クライアントサーバプログラムのネットワーク通信部分のみのインターフェー
スを切り放すことで処理本体のプログラムの作成が容易になる。

ucspi-tcpはプログラミングのときに役に立つツールであると同時にネットワー
クサービスデーモンを起動するときにクライアントホストに対してアクセス制限
を掛けたりするなどの機能も持っている。本特集では、システム管理者の観点か
らの利用について解説する。

●tcpserverによるパケットフィルタリング

ネットワークサービスデーモンを起動する場合には、常に起動状態にしておくス
タンドアロン起動と、外部からの接続要求があってから起動するオンデマンド起
動がある。伝統的には後者は inetd スーパーデーモンを利用してきた。inetdは
/etc/inetd.conf にあるポート番号とそれに対応するサービスデーモンの一覧を
元に代理でソケット接続要求を待ち受け、実際に接続が来たときに本来のサービ
スデーモンプログラムを起動する【図 は】。

---[図 は]-----------------------------------------------------------------

                       外部からのソケット接続要求
                       ↓              ↓                ↓
  +-------------+--------------+----------------+-----------------+-----+
   inetdが監視  | tcp/21(ftp)  | tcp/23(telnet) | tcp/110(pop3)   | ... |
                +--------------+----------------+-----------------+-----+
 対応するデーモン      ↓              ↓                ↓
 をinetdが起動        ftpd          telnetd             pop3d
--------------------------------------------------------------------------

非常に無駄のないサービス提供形態ではあるが、物騒になった近年のインターネッ
ト世界ではいくつか問題が出てきた。伝統的なinetdでは接続しに来た全てのク
ライアントホストに平等にサービス提供するので、悪意に満ちたクラッカーにも
門戸開放することになる。これに対処するため、近年のPC-UNIXでは
tcp_wrappers 付属の libwrap をリンクしたinetdを利用することで、接続クラ
イアントのアドレスに応じたアクセス許可/不許可などを定義できるようにして
いるものが多い。これで随分とセキュリティは向上するのだが、実際のところOS
標準の inetd には極度に負荷を掛けるとサービス提供不能状態に陥るものもあ
るので、万全とはいえない。これを解決してくれるのが tcpserver である。

tcpserverは、接続クライアントのアドレスに基づくアクセス制限だけでなく、
同時に接続できるクライアント数の制限なども可能である。inetdが全てのサー
ビスポートをひとつのinetdプロセスで待ち受けていたのとは対象的に、
tcpserverはひとつのサービスデーモンごとにひとつのtcpserverが接続待ち受け
を行なうようになっている【図 に】。

---[図 に]-----------------------------------------------------------------

          外部からのソケット接続要求
       ↓             ↓           ↓
  +-----------+ +-----------+ +-----------+
  |  tcp/21   | |  tcp/23   | | tcp/110   |
  |(tcpserver)| |(tcpserver)| |(tcpserver)|
  +-----------+ +-----------+ +-----------+
       ↓             ↓           ↓
      ftpd          telnetd       pop3d
---------------------------------------------------------------------------

●tcpserverの利点

現状では inetd+tcp_wrappers を使用して、/etc/hosts.allow ファイルなどで
アクセス制限を掛けている管理者が多いのではなかろうか。実際のところ筆者も
長い間 inetd+tcp_wrappers を利用していて、tcpserver に完全移行したのはつ
い1年程度前のことである。一般的でないかもしれないが、何故tcpserverに移行
したかを紹介することで、tcpserverの利点が伝わるのではないかと思い、以下
に列挙してみた。

	* 管理法式の統一性

	  システムによって inetd に libwrap が入っているかどうかが異なる
	  ので、それによって設定を変えなければならない。tcp_wrappers の
	  新規インストールは「make一発」で行かないのでそれなりに面倒。

	* アクセス制限ファイルの更新しやすさ

	  tcpserver用のアクセス制限ファイルはcdbデータベース形式に変換し
	  たものを利用する。当初は(hosts.allowを直接読むtcp_wrappersに比
	  べて)手間が掛かるのではと感じていたが、実際は手動でアクセス制
	  限記述を追加することはまれで、(POP before SMTPのように)動的な
	  制御記述の追加を行なうことのほうが多い。そのような場合、必ずア
	  クセス制限ファイルをアトミックに更新できることが保証されている
	  tcpserverの方が、下手に「ファイルロック」などを考えなくて済む
	  ぶん管理しやすい。

	* メモリ vs. 性能

	  当初はひとつのサービスにひとつのtcpserverが張りつくことに「もっ
	  たいない」的感覚を覚えていたが、昨今のハードウェア事情では1MB
	  に満たないようなプロセスをけちってセキュリティを落とすのはどう
	  かと思えるようになった。

	* 導入試験のやりやすさ

	  新しいネットワークサービスデーモンを導入するとき、コマンドライ
	  ンで「tcpserver 0 ポート番号 プログラム」と打てばすぐに実験で
	  きるのはありがたい。

そして今回の主題に即して考えると、daemontoolsとの親和性の高さも無視でき
ないだろう。tcpserver では一つのコマンド起動が一つのデーモン起動に直結す
るため、daemontoolsのrunスクリプトに取り込むのに適している。さらにsvcコ
マンドを利用することで、ネットワークデーモンの一時停止や再開なども手軽に
行なえる。


●ucspi-tcpの導入

原稿執筆時の ucspi-tcp 最新版は ucspi-tcp-0.88 で、
http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz
から入手できる。

ソースアーカイブを展開し、その中に含まれる conf-* ファイルを確認しよう。

	# gzip -dc ucspi-tcp-0.88.tar.gz | tar xvpf -
	# cd ucspi-tcp-0.88
	# head conf-*
	==> conf-cc <==
	gcc -O2
	
	This will be used to compile .c files.
	
	==> conf-home <==
	/usr/local
	
	This is the ucspi-tcp home directory. Programs will be installed in
	.../bin.
	
	==> conf-ld <==
	gcc -s
	
	This will be used to link .o files into an executable.

このうち変更する必要性が比較的高いのは conf-home だろう。conf-home はイ
ンストールプリフィクスを決定するためのファイルで、デフォルトは
/usr/local となっている。この場合、ucspi-tcp の各コマンドは
/usr/local/bin にインストールされる。これを変えたいときは conf-home ファ
イルの先頭を書き換える。

ただし、インストール先を /usr/local/bin 以外にした場合

	
	! daemontools(svscan) がデフォルトで持っている	!
	! コマンドサーチパス($PATH)にも注意する!	!

ことを忘れないでいただきたい。svscan起動時のデフォルトの$PATHには
/usr/local/bin が含まれているが、ucspi-tcp のインストール先を変えた場合
は、そのままでは tcpserver が起動できなくなり supervise が無限にrunスク
リプトを走らせ続けるトラブルが起こりがちなので注意する。

インストールする場所が決まったら、以下の手順でインストールが完了する。

	# make
	# make setup check

●tcpserverの起動

tcpserverは、

	# tcpserver [オプション] ホスト ポート番号 プログラム

という書式で起動する。「ホスト」は起動するホストが持っているネットワーク
インタフェースのうち、接続待ち受けを行なうIPアドレスを指示する。複数のネッ
トワークインタフェースをもつ場合、全てのIPアドレスでサービス要求を受けた
い場合は「ホスト」の値として 0 を指定する。

「ポート番号」はサービスに割り当てられたポート番号で、整数または 
/etc/services にあるサービス名を指定する。

「プログラム」は対応するネットワークインタフェースに接続要求があったとき
に起動すべきサービスデーモンプログラムである。

tcpserver のオプションには以下のものがある。

  ・一般的オプション
	-q		エラーメッセージなどを表示しない。
	-Q		エラーメッセージを表示する(デフォルト)。
	-v		冗舌(verbose)モード。エラーメッセージと
			ステータスも表示する。

  ・接続関連のオプション
	-c 数値		指定したデーモンを同時に起動する上限の個数。
	-x cdbファイル	tcprulesコマンドで作成した「cdbファイル」のルー
			ルに従う。このルールにより、接続の許可・拒否、環
			境変数の設定を決められる。「cdbファイル」は
			tcpserver動作中でも、tcprulesコマンドで安全に
			更新できることが保証されている。
	-X		-x オプションと組み合わせて、「cdbファイル」
			が存在しない場合でも接続を受け付けるように指示す
			る。-X なしの場合「cdbファイル」が無いと全ての接
			続を拒否する。
	-B バナー	接続確立直後にクライアントに送られるバナー文字列
			を指定する。
	-g GID		接続を受けた後に設定するグループIDを指定する。
	-u UID		接続を受けた後に設定するユーザIDを指定する。
	-U		-u $UID -g $GID と等価。
	-1		接続待ち受け準備が整ったら標準出力にローカルポー
			ト番号を出力する。
	-b 数値		TCP SYNのバックログを約「数値」個に制限する。OS
			によっては暗黙のうちに5に制限されている場合があ
			る。
	-o		IPオプションを残す。もしクライアントがソースルー
			ティングオプションをつけていたらそのまま送り返す
			(ただし危険)。
	-O		デフォルト。IPオプションを除去する。
	-d		クライアントの応答が遅いときにデータ送出を遅延さ
			せる。現在は既定値でONだが将来変えるかもしれない
			ので、この機能を望む場合は明示的に-dを指定する。
	-D		データ送出を遅延させない。-dの逆。

  ・データ収集関連オプション
	-h		デフォルト。クライアントのホスト名をDNSから引き、結
			果を環境変数 TCPREMOTEHOST に代入した上でデーモ
			ンを起動する。
	-H		-hによるDNS検索を行なわない。TCPポート53番(DNS)
			用のデーモンを起動する場合にはループを防ぐため
			に必ずこれを指定すること。
	-p		ホスト名をDNS検索したあと、そのホスト名でIPアド
			レスをDNS検索する。得られたIPアドレス群のいずれ
			もがクライアントのIPアドレスと一致しない場合は環
			境変数 TCPREMOTEHOST を削除する。
	-P		-pの逆。
	-l ローカル名	ローカルホスト名のDNS検索をせず、かわりに「ロー
			カル名」を利用する。普通は 0 などを指定する。TCP
			ポート53番用のデーモンを起動する場合はループを防
			ぐために必ずこれを指定すること。
	-r		デフォルト。クライアントから $TCPREMOTEINFO を得るこ
			とを試みる。
	-R		-rの逆。TCPポート53番と113番(AUTH)用のデーモンを
			起動する場合はループを防ぐため必ずこれを指定する
			こと。
	-t 数値		$TCPREMOTEINFOの取得のタイムアウトを「数値」秒に
			設定する。デフォルトは26秒。

●tcpserverのアクセス制御ファイル

tcpserver起動時に -x オプションでアクセス制御ファイルを指定する。 これは、
cdb形式(バイナリファイル)のものを指定することになっていて、実際に人間が
ルールを作成する場合にはテキスト形式で記述し、それをtcprulesコマンドで
cdb形式に変換する。

アクセス制御ファイルは一行にひとつ、クライアントのIPアドレスを示すパター
ンとそれにたいするアクションを記述する。つまり、各行は

	アドレスパターン:アクション

という形式になる。左辺で指定したパターンにマッチするアドレスからの接続要
求があった場合に右辺のアクションを取る。「アドレスパターン」の部分にはホ
スト名、またはIPアドレスが指定できる。ただし、パターンとしてホスト名やド
メイン名を指定する場合は、

	* tcpserver が -h オプションつきで起動されたとき(デフォルト)
	* IPアドレスからの逆引きが成功したとき

に限られることに注意しなければならない。もし、ホスト名ベースのマッチング
を行なう場合にはイコール記号(=)を前置する。ホスト名部分には、ホスト名の
FQDNもしくは、ピリオドで始まるドメイン部を記述できる。ドメイン部を指定し
た場合はそのドメインに属する全てのホストがマッチする。

	例
	=venus.example.com:allow
	=.example.net:deny		(*.example.net全てにマッチする)

逆にIPアドレスベースでパターンを指定する場合は、行頭からIPアドレスを記述
する。末尾をピリオドにした場合は残るオクテットは全てにマッチする。また、
二つの数値をハイフン(-)で繋いだ場合は範囲指定になり、二つのIPアドレスの
間に含まれるもの全てがマッチする。

	例
	192.168.:allow			(192.168.*.*全てにマッチする)
	172.16.9.10-20:allow		172.16.9.10, 172.16.9.11,…
					172.16.9.20 全てにマッチする
	10.0.3-5.:allow			10.0.3.*, 10,0.4.*, 10.0.5.*
					全てにマッチする


「アクション」の部分にはallow(許可) または deny(拒否) が指定できる。さら
に直後に環境変数への値の代入を指示することもでき、その場合はカンマで区切っ
て「変数="値"」の形式を列挙すれば良い。クォートする部分にダブルクォート
を含ませたいときは、「変数=/値/」のようにダブルクォート以外の任意の文字
で挟めば良い。

ルールセットの解釈は「ファーストマッチ」で行なわれる。先頭から順にパター
ンマッチングが行なわれ、マッチするものが見つかった時点で右辺のアクション
を取り、残りの行は解釈されない。

●tcprulesコマンド

テキスト形式で記述したルールファイルをcdb形式に変換するのがtcprulesコマ
ンドである。tcprulesコマンドは以下のように利用する。

	# cat ルールファイル | tcprules cdbファイル テンポラリファイル

tcprules コマンドは標準入力からルール定義を読み、変換結果を「テンポラリ
ファイル」に書き出す。そして変換がうまく行った場合に「cdbファイル」にリ
ネームする。cdbファイルの更新はアトミックに行なわれるので、tcpserverが
cdbファイルを利用している最中でも安心してルールファイルを更新できる。

●tcprulescheckコマンド

cdb形式に変換したルールファイルが機能しているか確認するためのコマンドが、
tcprulescheckである。

	# tcprulescheck cdbファイル

のように起動し、そのときにセットされている環境変数 TCPREMOTEIP または
TCPREMOTEHOST の値をcdbファイルと照らし合わせて、どのルール行が適用され
るかを出力する。実際に利用するときには、

	sh# TCPREMOTEIP=192.168.3.4 tcprulescheck foo.cdb

のように起動することになるだろう。

●daemontoolsとの連系の実際

次のパートで解説するdjbdnsなどは、daemontools用の run スクリプトを自動生
成してくれるので心配無用だが、その他のネットワークサービスデーモンを
daemontools+tcpserver 経由で起動する場合、自前で run スクリプトを書かな
ければならない。ここでは、POPサーバを例に実際にrunスクリプトを作成する方
法を解説しよう。

たとえば、次のような条件でPOPサーバを起動したいものとしよう。

	---[条件 ほ]--------------------------------------------------
	ポート番号		110(POP3)
	プログラム		/usr/local/etc/ipop3d
	設定したい環境変数	LAN内なら 環境変数 INTRANET をセット、
				LAN以外なら何もセットしない
	--------------------------------------------------------------

これをdaemontools経由で必要な起動させるためには以下のステップを取ること
になる。

	1. アクセス制限ルールファイルの作成
	2. tcpserverからの起動実験
	3. runスクリプトの作成
	4. /service ディレクトリへのシンボリックリンク作成

以下、順を追って作業例を示そう。なお、以下の例では daemontools と
ucspi-tcp のコマンド群がインストールされているディレクトリにコマンドサー
チパスが通っているものと仮定する。

  ・アクセス制限ルールファイルの作成

  まず、全てに先立ち、daemontoolsで管理するにふさわしい「サービスディレ
  クトリ」を作成しよう。ここでは /var/qmail/pop というディレクトリを
  作成しそこで作業するものとする。

	# mkdir /var/qmail/pop

  このディレクトリ内に、条件 ほ に従うルールファイルを作成しよう。ファイ
  ル名は何でも良い。ここでは poprule としよう。

	# cd /var/qmail/pop
	# vi poprule
	  (以下の内容にする)
---[ /var/qmail/pop/poprule ]-----------------------------------------
#ローカルホストはINTRANET
127.0.0.:allow,INTRANET=""
#192.168.0.* もINTRANET
192.168.0.:allow,INTRANET=""
#それ以外は何もセットせずallowのみ
:allow
----------------------------------------------------------------------

  これをcdbファイルに変換する。

	# cat poprule | tcprules poprule.cdb tmpfile

  ただし、この作業は設定作業のときに頻繁に行なうことが予想されるので、
  Makefileを作っておくと良いだろう。

	# vi Makefile
	  (以下の内容にする)
---[ /var/qmail/pop/Makefile ]-----------------------------------------
#↓ucspi-tcpのコマンド群をインストールしたディレクトリ
TCPDIR=/usr/local/bin
TR=${TCPDIR}/tcprules

all:	poprule.cdb

poprule.cdb:	poprule
	cat poprule | ${TR} $@ tmpfile
----------------------------------------------------------------------

  ・アクセス制限ルールのテスト

  作成したルールの動作確認には tcprulescheck コマンドを利用する。
  tcprulescheck は、起動時の環境変数 TCPREMOTEIP, TCPREMOTEHOST,
  TCPREMOTEINFO の値とルールファイル(cdb形式)を照らしあわせて、実際に
  tcpserverが取る挙動を知らせてくれる。たとえば、上記の poprule.cdb を利
  用する場合で、クライアントアドレス 192.168.0.5 と 10.0.0.5 からアクセ
  スがあった場合の挙動を調べたい場合は以下のようにする。

	# TCPREMOTEIP=192.168.0.5 tcprulescheck poprule.cdb
	  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	rule 192.168.0.:
	set environment variable INTRANET=
	allow connection
	# TCPREMOTEIP=10.0.0.5 tcprulescheck poprule.cdb
	  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	rule :
	allow connection

  前者のみ、環境変数INTRANETがセットされていることに着目する。繰り返しに
  なるが環境変数TCPREMOTEHOSTは、tcpserverが名前解決する場合(-hオプショ
  ン)のみホスト名がセットされることに注意する。

  ・tcpserverからの起動実験

  作成した poprule.cdb を指定して、tcpserverを起動してみる。

	# tcpserver -R -xpoprule.cdb 0 110 /usr/local/etc/ipop3d

  別のホストから110番ポートにアクセスしつながるか確認する。

	別ホスト% telnet サーバホスト 110

  うまく行ったらコマンドライン起動した tcpserver は C-c で止める。

  ・runスクリプトの作成

  上記のコマンドラインを再現する形で run スクリプトを作成する。

	# vi Makefile
	  (以下の内容にする)
---[ /var/qmail/pop/run ]-----------------------------------------
#!/bin/sh
# ↓これらのディレクトリはインストール状態により適宜修正すること
DAEMONTOOLS=/usr/local/daemontools/bin
UCSPI=/usr/local/bin
exec env - \
PATH=/bin:/usr/bin:/usr/sbin:$UCSPI\:$DAEMONTOOLS \
tcpserver -R -xpoprule.cdb 0 pop3 /usr/local/etc/ipop3d 2>&1
----------------------------------------------------------------------

  run スクリプトは、svscanによって起動されることになるので、rootユーザが
  コマンドライン作業をするときの$PATHとは違う値になっていることに注意す
  る。このスクリプトにあるように、環境変数PATHを定義することを忘れない。

  続いて手動でrunスクリプトを起動して、tcpserverをコマンドライン起動した
  ときと同様の結果が得られるか確認する。

	# ./run

  これで問題なく110番ポートにアクセスできるようであれば最後の手順に進ん
  で良いだろう。

  ・/service ディレクトリへのシンボリックリンク作成

  svscanに認識させるため、シンボリックリンクを作成する。

	# ln -s /var/qmail/pop /service

  5秒以内にpopサービスが上がるだろう。

	# svstat /service/pop
	/service/pop: up (pid 66856) 7 seconds


●まとめ

統一的管理環境が得られるdaemontoolsと、高度なセキュリティチューニングが
できるtcpserver。これらを組み合わせることで、高セキュリティ・低管理コス
トのサーバを作ることが可能となる。ぜひ一日も早く導入して、未来に導入する
であろう全てのデーモンの管理を楽にしてしまおう。


yuuji@gentei.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]