Firewall とは? [1994.8]

組織外と IP によるネットワーク接続をもっているとき,利便性と安全性の両立は困難な課題である.企業の研究所のように,外部とできるだけ自由にアクセスしたいという研究者の要求と,守秘や侵入防止を確保しなければならない会社の責務はしばしば対立する.この妥協は,自由なアクセスを許すホストを限定し,このホストの侵入防止対策を十分に強化することである.この外部,内部の両方からアクセスできるホストを firewall と呼んでいる (異国との通商のための出島を思い浮かべよう).

Firewall には,専用のルータマシンでなく,複数のネットワークインタフェースをもったホストがよく使われる. 単純には,このホストがルータの役目をしないようにデータの転送機能を止め,また,不要なネットワークデーモンも止める.外部接続をしたい利用者はこのホストにアカウントをもらい,このホストにログインして,外部へアクセスする.専用ルータは,IP ルータと呼ばれるように,IP より上位のレイヤは関知しない.つまり TCP やアプリケーションは単なるデータとして扱うのが本来の機能である.アプリケーションの呼び出し口である TCP ポートを認識して,これと IP アドレスの組み合わせでデータの転送を制限する機能が備わっているものもあるが,個々の設定を行なうのはたいへん煩雑である.

Firewall ホストにログインしなくても,firewall を越えられるようにするプログラムもいろいろ作られている.どれも Firewall ホストでデーモンプログラムを動かしておき,このプログラムがデータを一旦受取り,改めて外部,また内部へ送出する仕組みである.Proxy (代理) サーバと呼ばれる.アプリケーションレイヤまでデータをすべて処理するので速度的には不利だが,利便性を高めることが可能だ.SOCKS は UNIX のソケットインタフェースに firewall ホストのデーモンとの通信処理を組み込んだもので,rftp, rtelnet といったアプリケーションプログラムで外部とアクセス可能となる.TIS FWTK (TIS FireWall Toolkit) には,ftp や sendmail , rlogin といった proxy サーバや,ネットの受付である inetd 経由のアクセスを制御する wrapper が含まれている

Firewall を設けての外部接続は有効で現実的な手段であるが,アカウントの貸し借りといった内部利用者のモラルのない行動には無力である.利用者全員にある程度の知識とモラルを求めるのは,利便性 / 安全性の両立以上に困難な課題なのだろうか.


† fwtk はかなり変わったようです.単独の wrapper プログラムはなくなっています.[2003.7.10]


メールが送れないのですが? [1994.10]

メールが相手に届いたか? それを読んでくれたか? これを確実に知るには,相手に電話をかけて尋ねるのが一番だ. 実践している人もちらほら見かける.メールにかかわるトラブルは無数にあるが,まずメールが返された場合:

返ってきたメールには原因が記されているはずだ.しっかり読もう.そこに "Host unknown" や "User unknown" と書かれたときは,まず To:,CC: のアドレスを確かめよう.co.jp を cp.jp と書いてしまうような例が大変多い.誤解しやすいのは,User unknown の場合も,メールを差し戻すホストは相手ホストのひとつ手前のホストであることだ (IP 接続されているとき).相手のホストに送り付ける前に,相手に受取りアドレスを確認するので,エラーメールは配送の途中のホストから戻る.マシンやネットの不調と誤解する人が多いが,すなおにメッセージを読もう."unknown mailer error" の場合は,受取り人の設定ミスであることが多い.メールを自動的にフォルダーに分けるといったため .forward ファイルにプログラムやシェルスクリプトを実行する設定をする.このコマンドがまちがっているときこのエラーとなる.

"too many hops (XX max)" の場合は,戻ってきたメールに含まれる「送れなかったメッセージ」のうち Received: の行に注目する.Received: はそのメール配送に関わったホストが順に記録されている.同じホストが何度も現われたなら,それはメールがループしたのだ.二つのホストの .forward でお互いを指定してピンポンしたり,いくつものホストを経由して元に戻ったりしていないか.同じホストがないのに,too many hops になるときには,(XX max) の数字がいくつかを確かめよう.この数字分だけホストを通過するとループとみなされる.数字が小さいとき (例えば 17) には正常な配送でありながら,エラーになってしまう.そのホストの管理者に修正してもらうか,配送の経路を変更するしかない.メーリングリストでは,往復のホスト分が合算されるので発生しやすい.メーリングリスト開設ホストで Received: ヘッダを消去するといった処理をしてもらおう.

いつも送っていた相手へのメールが突然戻るようになり,"Host unknown (Name Server: XXX: no data known)" といったエラーメッセージがあったら,これはネットワークの管理システムのデータが狂った可能性がある.これは全体に影響が大きいので管理者も気づくだろう.1時間ほど待って送り直してみよう.それでもだめなら管理者に知らせよう.アドレスの書きまちがいではないね?


シェルスクリプトを書くときの注意点は? [1994.11]

シェルスクリプトは豊富なプログラム制御構造,データ構造,文字列処理,例外処理等を備えたたいへん実用的なプログラム言語である.UNIX の豊富なコマンドがシェルスクリプトのライブラリと考えれば,こんな強力な言語はない.ここでは Bourne Shell を対象とする.

[おまじない] スクリプトファイルの先頭から #! ではじまる次の文字列を書く.#! /bin/sh - ファイルモードが実行可能で文字列が先頭にあれば,このファイル名を打つだけで UNIX カーネルが確実に /bin/sh を呼び出してこのスクリプトを与えてくれる.

[$$] スクリプトで作業用のファイルを作る場合,ファイル名の一部にこの $$ を含めよう.$$ はスクリプトを実行している sh のプロセス番号に置き換えられるので,スクリプトが同時に実行されても作業ファイルがぶつかることがない.たとえ自分だけのためのスクリプトでもプログラムから実行したり,メールの処理に使えば同時に走る可能性がある.

[クオート] スクリプトに現われる * や ? は対話的シェルと同様にそのパターンにマッチしたファイル名に展開される.cat $1 > /tmp/t.$$ とあって,第 1引数を表わす $1 が * に置き換えられたら,これがさらにファイル名に展開される.これを "$1" のようにダブルクオート "" で囲めば,ファイル名への展開はされない.また,set -f を実行しておけばやはりファイル名への展開はされない.さらに $1 を $1 のままにするにはシングルクオートでこれを囲む.

[ヒアドキュメント] 出力のファイルへの追加は >> で指示するが,これの逆向き << がヒアドキュメントである.例えば mail foo <<XX の行を書くと,その後スクリプトに XX だけの行が現われるまでの行が mail への入力となる.この中でも $1等の展開は行なわれる.逆に,シングルクオートで囲んでも展開される.この展開を止めるには,mail foo <<'XX' のように << の直後の語を '' でクオートする.

[後始末] スクリプトにバグがあったり,割り込みキー (Control-C など) で実行を打ち切ったとき,作業ファイルを消去するといった後始末をしなければいけない.これには trap コマンドで

trap "/bin/rm -f $tmp; exit 0" 0 1 2 15

のようにする.指定した番号のシグナル 1 2 15 を受けたときの動作を定める.シグナル番号 0 は正常終了時にも実行することを示す.

[セキュリティ] シェルスクリプトファイルに set user id ビット (s ビット) を設定してはいけない.重大なセキュリティーホールとなる可能性があるので特に注意.

[例題] /etc や /usr/bin, /usr/ucb で file * | grep script とすると,コマンドのいくつかが実はシェルスクリプトであったことを発見するだろう.どれもよくできた例題となる.


kill コマンドとは恐い名前ですね [1995.1]

"kill - terminate a process with extreme prejudice" は,1986年の日付のある kill の man ページのタイトルである.走っているプロセスを止めるのが kill コマンドの第一義であるからそれなりの名前といえよう.プロセスを止めるには "kill -9 プロセス番号" とだけ思ってはいけない.kill の第1引数にはシグナルを指定する.シグナルは数字でもよいが,-1 は HUP, -2 は INT, -9 は KILL, といったようにそれぞれ名前がついている.kill -l (エル) で表示される.HUP は HangUP のことで,端末の電源が切れたことを意味する.INT は Interrupt で,端末からの割り込み (伝統的には DEL, 最近は Control-C を打つ) を表わす.つまりシグナルとはプロセスに対しての通知であり,kill コマンドを使えば任意のシグナルを通知できる.そして,-9 はそのシグナルのひとつにすぎず,それが強制終了に割り当てられているのだ.

シグナルには,端末から送られるもの (HUP, INT, QUIT),プログラム実行中に浮動小数点例外や,割り当てられていないアドレスをアクセスした,など,ハードウェアで検出されるもの (FPE, SEGV),alarm システムコールで設定したタイマ時刻が到来したことを告げるもの (ALRM),パイプで繋がれた自分より前のプロセスの終了通知,子プロセスの状態変化など,関係プロセスの変化をカーネルが知らせてくれるもの (PIPE, CHLD) などさまざまな用途に使われている.シグナルの種類も V7のころの倍ほどになった.

プロセスは,シグナルごとにシグナルを受けた時点で起動する関数を登録することができる (シグナルをキャッチすると呼ぶ).また無視するよう設定できる.これらには signal システムコールを使う.キャッチも無視もしていないシグナルが起きたときは,シグナルごとに決められている動作となる.INT なら強制終了,QUIT なら core を作成して強制終了となっている.シグナルをキャッチして起動する機能にはいくつか慣例がある.HUP シグナルは設定ファイルを読み直してのプログラムの再起動に割り当てる.プログラムの強制終了にあたって,作業ファイルを消去するなど後始末をして正常終了扱いをするには TERM シグナルに設定する.kill コマンドのシグナル引数のデフォルトはこの TERM である.kill -9 より先に kill だけを実行すべきだ.なお,この -9, KILL シグナルはキャッチも無視もできない.

走らせたプログラムが子プロセス,孫プロセスをフォークしているとき,また,パイプでいくつものプログラムを繋げたとき,これらすべてにシグナルを送るには,kill でプロセス番号を並べる代わりに最初に走らせたプロセスの番号にマイナスをつけて kill コマンドに与えることができる.負の番号は ps -l (ps -j のこともある) で見ることができるプロセスグループ番号と解釈され,グループのプロセスすべてに送られる.


sync を3回打つ人を見かけるのですが [1995.2]

ワークステーションのコンソールで shutdown コマンドを打つ前に,シンク ! シンク ! シンク ! とつぶやいている人は空調のしっかり効いた部屋で大型計算機に並んで鎮座していた,RT や VMS でない UNIX という壊れやすいシステムの世話をしていたか,その弟子であろう.UNIX やネットワークの管理術は,マニュアルではなく徒弟制で伝わるので,シンクのおまじないは脈々と続く.

UNIX はディスクアクセスを減らすため,ディスクのファイルブロックやディスク管理のブロック (i-node, スーパーブロック) の写しを主記憶におき,ディスク操作を写しの操作で代行する.主記憶のディスクブロックを実際にディスクに書き出すのが sync システムコールで,これを30秒ごとに呼ぶのが update デーモンであり,1回呼び出すのが sync コマンドである.ディスクの使用量などを表示する df コマンドのようにファイルシステムに関わるコマンドは,内部で操作の前に sync システムコールを呼び出すよう推奨されている.マシン停止をする shutdown コマンドも,もちろん内部で呼び出している.ところが,当時の UNIX には shutdown 自体が存在しなかった.筆者が伝授されたマシン停止法は,「スーパーユーザになり,システムをシングルユーザモードに移行させ,sync を何回か打ったのち,BREAK キーを押す.以降,電源切断順序にのっとる」である.BREAK キーではなく Control-P のマシンもあった.これを押すとマシンの ROM モニタのモードになった.shutdown や /etc/halt は内部で sync システムコールを呼んでいるが,ディスクバッファを大量に用意してあると実際に書き出すのにはある程度の時間がかかる.sync システムコールは書き出し終了を待たずにリターンするので,sync の後何秒か待っても書き出されたどうかはわからない.そうと知ってのシンク ! シンク ! シンク ! はおまじないだけではなさそうだ.

当時の UNIX は logout コマンドもなかった.必要がなかったためである.ログインシェルは /bin/sh のみの時代である.シェルで Control-D キー (End-of-Text) を押せば入力完了であるから sh プロセスは終了する.ログインシェルが終了すれば,その端末には login: が表示され,次のユーザを待つ.これは logout に他ならない.そしてもうひとつ,コマンド login を実行するのだ.プロセスが UNIX, という思想が貫かれている.ネットワーク,ウィンドウシステムなど,多くの機能を実現するため UNIX もずいぶん拡張されてきたが,それでも UNIX であり続けているのは初期に確固たる思想があったからだろう.


UNIX にはお化けがいるのですか? [1995.3]

夜中に小人がでてきて原稿を上げてくれるという,うらやましい環境があるらしいが,UNIX にもたくさんのサポータがいる.マシンの boot 時に起動されるのサポートプロセスを一括してデーモン (daemon) プロセスと呼ぶ.デーモンは自分から積極的に動くものではない.普段は,ネットワーク経由での接続依頼や,決められた時刻になる,といった事象が起きるのを待っている.事象が起きれば,決められたプログラムを起動したり,fork システムコールで自分の分身を生み出して仕事をやらせる.自分ではすぐに受付業に戻る.代表的なデーモンは,cron (clock daemon),lpd, inetd, update といったところである.デーモンとなるプログラムは,長時間走り続けることを前提とした設計が必要である.malloc だけして free しない (free できない) とか,一時ファイルの始末をつけないようなプログラムは問題外である.共有資源アクセスのためのロック,マシンの reboot への対処など,十分に気を使った設計が必要である.

デーモンに限らず,プロセスは fork システムコールで生まれる.そして自ら exit システムコールで終了するわけだが,exit してもすぐに成仏はできない.exit で,プロセスはまずゾンビ (zombie) 状態になる.ゾンビプロセスは,カーネルのプロセステーブル,スタック領域の一部などを残し,他の資源を返却する.よって,このゾンビはもはや動き回ることはない.そして,生みの親が看取ってくれるのを待つのである.親プロセスには,テーブルから,exit システムコールの引数である終了ステータス値が伝わる.また,そのプロセスが使用した CPU 時間などの課金情報は親プロセスに加算される.ゾンビ状態のプロセスは親プロセスで wait システムコールが呼ばれると成仏する.親が先に終了しているときには,init (PID = 1) プロセスが親に代わって看取ってくれる.

コマンド名で有名なのは,狼男ならぬ猫男 catman だろう. 満月の夜には奇妙な振る舞いをする,とかつてのマニュアルには記載されていた.UNIX のコマンド名は短すぎて憶えにくいと言われる.確かに学習の敷居が高いが,パイプを連ねてスマートに目的を達するようになったころ,短い名前の利点に気づくだろう.


   タイトル一覧  ホーム
webmaster