例 rc.firewall.txt で一番最初に目に付くのが設定 (Configuration) セクションだ。ここは各自の設定にとってネックとなる情報を含んでいるため、必ず手を加える必要がある。例えば IPアドレスは各人違うだろうから、ここから有効な値が取れるようにしておかなくてはならない。 $INET_IP
は、もし持っているのなら、間違いなく有効な IPアドレスでなければならない。 (IPを持っていないというなら、rc.DHCP.firewall.txt に目を通してみるのもいい。しかしとりあえず先へ進もう。このスクリプトを読めば、とにかく興味深い事柄がたくさん分かるはずだ)。また、変数 $INET_IFACE
は、実際にインターネットにつながることになるデバイスを指していなければならない。変数名を与えるのは、eth0 か eth1, ppp0, tr0 といったところ。選択肢はそう多くはないはずだ。
このスクリプトは DHCP や PPPoE などに対応する設定オプションは特に含んでいないため、それらのセクションは空白になっている。他の空白セクションも同様に、他のスクリプトとの違いがよく分かるようにするため、敢えてそのまま残してある。それらの部分が必要になったら、いつでも他のスクリプトと混合もできるし、自分で (頑張って) 一から書いてみるのもいいだろう。
LAN に不可欠な設定オプションは、そのほぼ全部が、ローカルエリアネットワーク (ocal Area Network) セクションに含まれている。例えば、LAN につながっている物理インターフェースの IPアドレス、それにもちろん、LAN で用いる IP範囲、マシンと LAN との接点になっているインターフェースも指定しなければならない。
さらに、ローカルホスト (Localhost) の設定セクションがあるのにも気づいたはずだ。お仕着せながら、 99%の確率で、まず値を変える必要はないだろう。 IPアドレスはほぼ常に 127.0.0.1 であろうし、名前もほぼ lo に違いないからだ。また、ローカルホスト設定のすぐ下には、 iptables に関する簡潔なセクションがある。このセクションの主旨は $IPTABLES
だけで、この変数は iptables アプリケーションそのものずばりの位置を指し示している。これは多少変わるかもしれない。 iptables パッケージを手動でコンパイルしたのなら、その位置は /usr/local/sbin/iptables となる。しかし、アプリケーションの実体を /usr/sbin/iptables など、違った場所に置いているディストリビューションもたくさんある。
最初の部分で、 /sbin/depmod -a コマンドを使って依存関係 (dependency) ファイルを最新状態に更新しているのが分かるだろう。その後に、このスクリプトで必要となるモジュールをロードする。不必要なモジュールはロードしないよう、いつも心がけておくべきだ。また、もう使うはずのなくなったモジュールをロードしたまま放っておくのも極力控えなければいけない。それに対応した追加ルールを書かなくてはならない羽目になり、セキュリティ上好ましくないからだ。では、例を挙げていくとしよう。 LOG、REJECT、MASQUERADE ターゲットを利用可能にしたいとして、それらがカーネルにスタティックにコンパイルされてない場合には、以下のようにしてモジュールをロードする:
/sbin/insmod ipt_LOG /sbin/insmod ipt_REJECT /sbin/insmod ipt_MASQUERADE
ここで扱うスクリプトではモジュールを強制的にロードしているが、これは、ロードの失敗という結果を招くこともある。モジュールロードの失敗には様々な原因が考えられるが、その際にはエラーメッセージが出力されるだろう。ごく基本的なモジュールでロードの失敗が起こる場合、一番ありがちなのは、モジュールや機能がカーネルにスタティックにコンパイルされているケースだ。これに関する詳細なら、付録 よくある問題と質問 の モジュールロードのトラブル を読んでいただきたい。 |
次にあるのが ipt_owner モジュールをロードするためのオプションで、例えば、限られたユーザだけに特定のコネクションを許可するなどの用途に用いる。当例題では使用していないが、基本的な使用法としては、root にだけ redhat.com への FTP 接続と HTTP 接続を許可し、それ以外は DROP する、といった使い方ができる。また、あなた自身のユーザと root 以外、マシンからインターネットへの接続を禁止することもできる。他のユーザはうんざりするだろうが、こうすれば、ハッカーのアタックや、あなたのホストを単なる踏み台に使おうとするアタックを跳ね返し、セキュリティをさらに固められる。 ipt_owner マッチの詳細は、チャプター ルールの作り方 の Ownerマッチ セクションに書いてある。
ここでは、ステート (state) マッチングに関係する追加モジュールもロードしている。ステートマッチとコネクション追跡 (connection tracking) コードを拡張するモジュールはすべて、 ip_conntrack_*, ip_nat_* といった名前が付いている。コネクション追跡ヘルパーは、特殊な接続を間違いなく追跡する方法をカーネルに教えるための特別なモジュールだ。カーネルは、ヘルパーと称するこれらのものなしには、特殊なコネクションを追跡しようとした際に手掛かりの見当が付けられない。これに対して、 NAT ヘルパーは、特殊なパケットのどこを見ればいいのか、コネクションをきちんと機能させるためにはどのような変換 (translation) を行えばいいのかをカーネルに伝えるコネクション追跡拡張ヘルパーだ。例えば、 FTP は規格上複雑なプロトコルで、コネクションに関する情報をパケットのペイロードそのものの中でやりとりする。つまり、あなたのネットワークの中のマシンがインターネット上の FTP サーバに接続する場合を考えると、そのマシンはパケットのペイロードに自分のローカルネットワーク IPアドレスを乗せて送信し、「この IPアドレスに接続するように」と FTP サーバに要求する。ところが、ここで指定されたローカルネットワークアドレスは自分のネットワークを一歩でも出れば無効な種類のものなので、 FTP サーバは手も足も出ず、コネクションは断絶してしまう。 FTP NAT ヘルパーはこれらコネクションに必要な変換をすべて世話してくれるので、 FTP サーバは本当の接続先を知ることが可能となる。これと同様なことは DCC ファイル転送 (送信) とチャットにも言える。この種のコネクションを成立させるには、 IPアドレスとポートを IRC プロトコルの内部で送らなければならない。よって、何らかの変換が必要となるのだ。ヘルパーがないと、 FTP や IRC といった類は、平気で機能する部分もあるし、駄目な部分も出てくる。例えば、 DCC に乗せてファイルを受け取ることはできるとする。ところが送ることはできない。これは DCC がコネクションを開始する際の仕組みに起因している。まず初めに、あなたの側から、ファイルをこれから送る旨と、受信側がどこへ接続すればよいかが、受信者へ通知される。しかしヘルパーがないと、この DCC コネクションは受信者に、受信者のローカルネットワーク上にいる或るホストにつないでくれ、と言っているようにしか見えない。即ちコネクションが成り立たない。しかし、反対方向なら、送信側は (十中八九は) 正しい接続先アドレスを通知してくるので、滞りなく働くのだ。
mIRC DCC でファイヤーウォールを越えられないのだが IRC クライアントでは問題なし、というトラブルに見舞われている場合は、付録 よくある問題と質問 にある mIRC DCCのトラブル を読んでみるといい。 |
執筆の時点では、 FTP および IRC プロトコルのサポートを追加するためのモジュールをロードする手段としては、これ以外の選択肢はない。これらの conntrack および nat モジュールについて詳しい説明が必要なら、付録 よくある問題と質問 を読んでいただきたい。また、 patch-o-matic には、H.323 conntrack ヘルパーをはじめ、他にも様々な NAT ヘルパーがある。それらを利用するには、 patch-o-matic を使用した後、カーネルをコンパイルしなおす必要がある。やり方については、チャプター 準備 で丁寧に説明してある。
FTP と IRC プロトコルに対するネットワークアドレス変換 (Network Address Translation) をどういう形であれ正常に機能させたいのであれば、 ip_nat_irc と ip_nat_ftp をロードしなければならない。また、 NAT モジュールをロードする以前に ip_conntrack_irc と ip_conntrack_ftp モジュールもロードしておく必要がある。使い方は conntrack モジュールと同様だが、これらがそのコンピュータにもたらしてくれるのは、 FTP と IRC プロトコルを NAT する能力だ。 |
この段階で、/proc/sys/net/ipv4/ip_forward に 1 を echo して、IPフォワーディング を有効にする。こんな具合だ:
echo "1" > /proc/sys/net/ipv4/ip_forward
IPフォワーディング をいつ、どのタイミングでオンにするかは、考慮に値する問題だ。このスクリプトおよび、当チュートリアルで取り上げるすべてのスクリプトでは、具体的な IP フィルタ (つまり iptablesルールセット) を作る前にオンにしている。このやり方では、ルールセットの内容とマシンの性能にもよるが、1 ミリ秒から 1分 の間、あらゆる種類のトラフィックをどこへだろうと転送できる瞬間が生じる。これは悪質な連中にファイヤーウォールへ侵入する間隙を与えてしまうかもしれない。つまりは、本来なら、このオプションはすべてのファイヤーウォールルールが構築されてからオンにするべきだ。しかし僕は、全スクリプトで一貫した処理の流れを採るために、敢えて、ルールのロード前にオンにするほうを選択することにした。 |
動的 IP のサポートが必要な場合、例えば SLIP, PPP, DHCP を利用している人は、必要ならば、もうひとつのオプション ip_dynaddr を以下のやり方で有効にする:
echo "1" > /proc/sys/net/ipv4/ip_dynaddr
他にもオンにする必要のあるオプションがある場合は、同様の手順で行う。こういったことのやり方に関しては他にドキュメントがあるし、このドキュメントの主旨からは外れてしまう。proc システムについては、カーネルの付属文書の中にも、やや簡略すぎる嫌いもあるが優れたドキュメントが配布されており、同じものを付録 その他の資料とリンク にも用意しておいた。付録 その他の資料とリンク は、このチュートリアルにない、何か特定のジャンルについて調べたくなった際に、拠り所となる場所だ。
このチュートリアルに含まれるその他すべてのスクリプトもそうだが、 rc.firewall.txt には、必要でないproc設定 (non-requred proc settings) という小さなセクションが含まれている。それら設定は、何かが思った通りに動いてくれない時に、発想のヒントになるだろう。しかし、それらの意味するところをはっきり知らないまま値を変えるのは御法度だ。 |
このセクションでは、このチュートリアルにおいて、ユーザ定義チェーンに関して僕が採用したいくつかの事柄と、 rc.firewall.txt に特有な手法を、簡単に説明する。僕が選択した方針のいくつかは、一部の見方によっては正しくないかもしれない。そうした観点や危惧される問題に関しては、随時、指摘してゆく。このセクションはまた、チャプター テーブルとチェーンの道のり のちょっとした復習にもなっている。これを通じて、実際の生きたサンプルでテーブルとチェーンをパケットがどのように巡ってゆくか、少しでも呑み込んでもらえればと願っている。
僕は各種ユーザチェーンを、できるだけ CPU パワーをセーブしながらも、セキュリティに最重点を置くやり方で配置した。例えば TCP パケットに TCP、UDP、TCP ルールを全部廻らせるのではなく、単純に TCP パケット全部をマッチさせて、それを 1つのユーザ定義チェーンに通すようにした。こうすれば、総体としてのオーバーヘッドを比較的低く抑えることができる。下の図で僕は、入ってきたパケットが Netfilter をどのようにくぐって行くかを説明しているつもりだ。こうした図と、説明文を用いて、当スクリプトの目的を説明し明らかにしていこうと思う。ここではまだ、特定の物事の子細には踏み込まない。それは後のチャプターに取っておこう。この図は、チェーンとテーブルの全行程を詳細に論述したチャプター テーブルとチェーンの道のり にあるものに比べれば、ちゃちななものだ。
この図を見ながら、我々の目的がどういったことなのか、はっきりさせていこう。このスクリプトは終始一貫して、ひとつのローカルネットワーク と、1台のファイヤーウォール 、ファイヤーウォールにつながったひとつのインターネット接続、というシナリオの上に立っている。また、インターネットに対しては 1個の固定 IP を持っている (DHCP, PPP, SLIP などではないということ) という想定だ。ここでは、ファイヤーウォールはインターネット向けにいくつかのサービスを提供するサーバとしても働かせたい。また、ローカルネットワーク は完全に信頼し、よって、ローカルネットワークからのトラフィックは一切ブロックしないようにする。なお且つ、このスクリプトでは、はっきりと許可したいトラフィックだけを通すことを最重点項目に据える。そのために、各チェーンのデフォルトポリシーは DROP にしたい。これによって、こちらのネットワークつまりファイヤーウォールの内側に対して明示的に許可していないパケットやコネクションを、効果的に殺すことができる。
このシナリオでは、ローカルネットワークからのインターネット接続もできるようにしたい。ローカルネットワークは完全に信頼しているので、ローカルネットワーク からインターネットへのトラフィックは、どんな類でも許可するようにしたい。一方、インターネットは信頼の置けるネットワークとはほど遠いものなので、外の連中からローカルネットワークへのアクセスはブロックしたい。これらの想定を踏まえた上で、我々がやるべきこと、やらなくていいこと、そして、やろうと思っていることを考えていこう。
まず第一に、当然ながら、ローカルネットワーク からインターネット に接続できるようにしたい。どのローカルコンピュータも正規の IPアドレスは持っていないので、そのためには、パケットに漏れなく SNAT を掛ける必要がある。これはすべて、 POSTROUTING チェーンで行う。スクリプトで最後に作っているチェーンだ。これに伴って、何も処理をしなければ外界からローカルネットワークにフルアクセスできてしまうので、 FORWARD チェーンで何らかのフィルタリングを行わなければならない。我々はローカルネットワークは完全に信頼し、それ故に、ローカルネットワークからインターネットへのトラフィックは、取り立てて全部許可するようにする。インターネット側からローカルネットワークのコンピュータに触れられては困るので、インターネットからこちらのネットワークへのトラフィックはすべてブロックしたい。ただし、確立済み (established) のコネクションと、関連性 (related) のコネクションは、ローカルネットワークがインターネットから返答を受け取るのに必要なので、許してやる必要がある。
このファイヤーウォールを作るに当たっては、少々予算が厳しいことにしよう。つまり、ほんのいくつかでも、インターネット上の人々へサービスを提供しようと思う。そこで、 HTTP, FTP, SSH, IDENTD へのアクセスをインターネット側から許すことにした。これらのプロトコルはファイヤーウォールそのものの上で提供する。従って、 INPUT チェーンで許可される必要があると同時に、返答のトラフィックを OUTPUT チェーンで許可してやらなくてはならない。またその一方、我々はローカルネットワークについても完全に信頼し、ループバックデバイスとその IPアドレスにも信頼を置いている。そのため、ローカルネットワークおよびループバックネットワークインターフェースからのトラフィックをすべて許可する特別なルールを加えようと思う。また、或る特定の組み合わせによっては許可したくないパケットやパケットヘッダもあるし、インターネット側から侵入を許したくないIP範囲もある。例えば 10.0.0.0/8 というアドレス域は、ローカルネットワーク専用に規定されているものなので、通常、そんなアドレス域からのパケットを許可したいとは思わないはずだ。それらが成りすましパケットであることは 9割がた確実だからだ。だが、この設定を取り入れる前に、心に留めておかなくてはならないことがある。一部のインターネットサービスプロバイダは、こうしたアドレス域をネットワークに利用しているという事実だ。その点についての詳細な論議は、チャプター よくある問題と質問 に書いてある。
我々のサーバでは FTP サーバが動いているので、通るルールを可能な限り少なくするという前提も踏まえて、確立済み (established) および関連性 (related) のトラフィックを許可するルールを INPUT チェーンの一番先頭に組み込む。同様の理由から、ルールはいくつかのサブチェーンに分割しようと思う。こうすることによって、我々の扱うパケットは、可能な限り少数のルールを通るだけですむはずだ。巡るルールが少なければ、ルールセットがパケットひとつひとつに費やさせる時間が短縮でき、ネットワーク上での遅延を小さく抑えることにつながる。
このスクリプトではプロトコルの種別によってパケットを別々のチェーンへ振り分ける方針を決めた。種別とは、具体的には TCP、UDP、ICMP だ。 TCP パケットはどれも、許可したい TCPポートおよびプロトコルを指定したルールから成る tcp_packets という名のチェーンを通る。また、 TCP パケットはさらに追加審査にも掛けたい。そのため、ファイヤーウォールにとって妥当なポートナンバーを使っていると認められたすべてのパケットに対して、もうひとつサブチェーンを作ろうと思う。このチェーンを allowed チェーンと呼ぶことにするが、そこには、パケットの最終受け入れの前に行う少数のチェックを設ける。 ICMP パケットはどうかというと、icmp_packets というチェーンを通る。このチェーンの性質を考えると、 ICMP パケットのタイプとコードさえまともであれば、受け入れ前にそれ以外のチェックを行うこれといった必要性は考えにくい。従って、 ICMP パケットはいきなり受け入れることにする。取り組むべき最後のパケットが、 UDP パケットだ。 UDP パケットは、すべての進入 UDP パケットを扱う udp_packets というチェーンへ送り込む。ファイヤーウォールへやってくる UDP パケットは全部、このチェーンに送られ、許可された種類であれば、それ以上の検査なしにいきなり承認する。
我々が運営しているのは比較的小規模なネットワークなので、このマシンは補助的なワークステーションとして使われることもある。ファイヤーウォールにもう少し仕事をさせるためにも、 speak freely, ICQ といったいくつかの特殊なプロトコルを、当のファイヤーウォールとの間で取り交わせるようにしようと思う。
ファイヤーウォールの最後には OUTPUT チェーンがある。ファイヤーウォールを充分に信頼しているのだから、ファイヤーウォールから出るトラフィックはほぼ手放しで許可する。特定のユーザに対するブロッキングもしないし、プロトコルによるブロックも行わない。ただし、どこかの連中にファイヤーウォールを偽装パケットの発信源にされたくはないので、ファイヤーウォール自体に割り当てられた IPアドレスを発信元とするトラフィックだけを許可するようにしたい。これを実装するとすれば、最も順当な手段は、ファイヤーウォールに振られている IPアドレスのどれかから来たパケットであれば離脱を ACCEPT し、そうでなければ OUTPUT チェーンのデフォルトポリシーで DROP するようなルールを組み込むことだ。
ルールセットの冒頭辺りで、デフォルトポリシーを設定している。各種チェーンでデフォルトポリシーを設定するには、下記のような、極めてシンプルなコマンドを使う:
iptables [-P {chain} {policy}]
デフォルトポリシーは、そのチェーンのどのルールにもマッチしないパケットに、もれなく適用される。例を挙げる。ここに、全ルールセットのルールにどれひとつとしてマッチしないパケットがあるとしよう。こうしたことが起こったら、我々は問題のパケットをどう処置するか決めなくてはならない。そこで登場するのがデフォルトポリシーだ。ルールセットでこれ以外のルールにマッチしないパケットに適用されるのが、デフォルトポリシーなのだ。
他 [訳者註: filter テーブル以外] のテーブルのチェーンにデフォルトポリシーを設定する場合には、注意が必要だ。それらのテーブルはそもそもフィルタリングのために作られたものではないため、奇妙な動作を招いてしまうからだ。 |
ここまでで、我々がこのファイヤーウォールで何を達成したいのか全体像がつかめたはずなので、具体的なルールセットの構築に取りかかることにしよう。我々の作りたい、そして使いたいルールとチェーンを設定し、チェーン内のすべてのルールセットを構築するその時が、いよいよがやってきた。
ここから、 -N コマンドを使って、我々の必要とする各種の特別なチェーンを作成する。新しいチェーンは、何のルールも内包しない状態で作成される。
前述の通り、我々が使用するのは、 icmp_packets,
tcp_packets,
udp_packets,
allowed というチェーンであり、
allowed チェーンは
tcp_packets チェーン経由で使われる。
$INET_IFACE
から進入するパケットのうち、
ICMP タイプのパケットは
icmp_packets チェーンへリダイレクトされる。
TCP タイプのパケットは
tcp_packets チェーンへリダイレクトされ、
$INET_IFACE
からの UDP タイプのパケットは udp_packets チェーンへ行く。これについては、後ろの INPUTチェーン セクションで、より詳細な説明をしている。チェーンの作成は至ってシンプルで、下記のようなチェーンの宣言があるだけだ:
iptables [-N chain]
これからの数セクションで、ここで作られたはずのユーザ定義チェーンそれぞれを、子細に眺めていく。どんな姿をしているのか、どんなルールでできているのか、その中で何を達成しようとしているのか、詳しく見ていくことにしよう。
bad_tcp_packets チェーンは、ヘッダの異常など、問題のある進入パケットの審査を目的としたルールを組み込むための専用チェーンだ。実際のところ、このチェーンには、進入する TCP パケットすべてを検査して、 NEW でありながら SYN ビットが立っていないもの、 NEW でありながら SYN/ACK の立っているものをブロックするパケットフィルタだけを組み込んでいる。このチェーンによって、上記のような異常や、XMAS ポートスキャンなど、想定しうるあらゆる矛盾をチェックすることができる。さらに INVALID ステートを検出するルールを追加してもいいだろう。
NEW でありながら SYN でない、ということについてもっとよく理解したいなら、付録 よくある問題と質問 にある NEWステートでありながらSYNビットの立っていないパケット セクションを読んでみるといい。そこでは、ステートが NEW でありながら SYN でないパケットが他のルールを素通りしてしまうという現象を取り上げている。ある種の特殊な環境では、これらのパケットを許可したほうがいい場合もあるが、99%、通す意味はない。従って、このスクリプトでは、ログを取って DROP している。
なぜ NEW と判定された SYN/ACK パケットを REJECT するかは、理由を考えてみれば非常に単純だ。それについては付録 よくある問題と質問 の SYN/ACKでNEWなパケット セクションで事細かに述べている。この処置は、基本的に、他のホストに対する配慮だ。これによって、他のホストを シーケンスナンバー予測アタック (sequence number prediction attack) から守ることになるからである。
パケットが $INET_IFACE
から入ってきて、そのタイプが TCP ならば、パケットは tcp_packets チェーンを巡る。そこでもし、このコネクションが我々の許可したいポートであったなら、それが本当に望むべきものかどうか最終的なチェックに掛けるようにしたい。この最終チェックを行うのが、allowed チェーンだ。
まず最初に、そのパケットが SYN パケットかどうかを調べる。SYN パケットであるとすれば、それは十中八九、新しいコネクションの最初のパケットだろうから、当然これは許可する。次には、パケットが ESTABLISHED または RELATED なコネクションに属するものかどうかを調べる。そうであれば、当然これもまた許す。ESTABLISHED なコネクションとは、既に双方向にトラフィックがやりとりされたコネクションのことだが、この時点で SYN パケットは既に検出済みであることから、ステート機構の見地から見る限り、そのコネクションは紛れもない ESTABLISHED ステートに違いない。このチェーンで最後のルールは、上記以外のパケットをすべて DROP する。これに当てはまるものとは、つまり、双方向のトラフィックがまだ観察されていないその他すべて、とほぼ同義だ。例えば、SYN パケットに対してこちらが返答しなかった、あるいは SYN パケット以外でコネクションを開始しようとしたやつがいる、などの場合だ。コネクションの開始に SYN パケットを使わないなどということには、まったくもって実用性がない。あるとすれば、ほぼ間違いなく、ポートスキャンをする時だけだ。現在のところ TCP/IP には、SYN 以外のパケットによる TCP コネクション開始に対応した実装など、僕の知る限りひとつもない。よって、99% はポートスキャンに違いないので DROP してしまおう。
このスクリプトでは ESTABLISHED,RELATED パケットに関するルールが二重に定義されていることになり、こちらのルールが実際に使われることはない。しかし、要素を全て網羅するために敢えてここに記述した。使用されるほうのルールは INPUT チェーンの冒頭にあり、そこでも ESTABLISHED,RELATED が定義されている |
tcp_packets チェーンは、インターネット側からファイヤーウォールのどのポートを利用可能にするかを決定する。だが、それだけでのチェックでは心許ない。そこで行うのが、前述した、各々のパケットの allowed チェーンへの送致である。
-A tcp_packets は iptables に対して、新しいルールの追加先チェーンを指示し、そのルールはチェーンの最後尾に追加される。-p TCP は TCP パケットをマッチさせるよう指示し、-s 0/0 は、ネットマスク 0.0.0.0 の送信元アドレス 0.0.0.0、つまり送信元アドレスすべてにマッチさせるよう、iptables に指示を与える。実を言えばこれはデフォルトの動作なのだが、物事をできるだけ明確にするために敢えて記述している。--dport 21 は宛先ポート 21 を表す。別の言い方をすれば、パケットが 21番ポート宛ならばマッチするということだ。すべての評価基準が満たされれば、パケットは allowed チェーン行きとなる。そこでどのルールにも当てはまらなければ、そのパケットは、自身を tcp_packets チェーンへ送り込んだ大元のチェーンへ差し戻される。
これまでのところ、TCP ポート 21 つまり FTP コントロールポート (FTP コネクションを制御するポート) は許可しているわけだが、この先で、RELATED コネクションにも許可を与える。ip_conntrack_ftp モジュールがロードされているはずなので、そうすることによって、PASSIVE、ACTIVE 両方のコネクションが許可できる。FTP をまったく許可しないつもりなら、ip_conntrack_ftp モジュールをアンロードした上で rc.firewall.txt から $IPTABLES -A tcp_packets -p TCP -s 0/0 --dport 21 -j allowed の行を削除すればよい。
ポート 22 は SSH だが、SSH は、外からあなたのマシン上のシェルを使えるようにしたい場合には、23番ポートの telnet を使わせるよりずっとマシなものだ。しかし、肝に銘じてほしい。あなたが今取り組んでいるのはファイヤーウォール である。常識的に、ファイヤーウォールマシンをあなた以外の人が触れるようにするというのは間違った考えだ。ファイヤーウォールには、最小限のものしか持たせない、加えない、というのが鉄則だ。
ポート 80 は HTTP つまり WEBサーバ。ファイヤーウォール上で直に WEBサーバを動かす予定がないのなら、削除していただきたい。
そして最後に許可しているのがポート 113、つまり IDENTD で、IRC など一部のプロトコルが正常に機能するために必要となる。ローカルネットワーク上のたくさんのホストを NAT するつもりなら、是非憶えておきたいのが、oidentd パッケージだ。oidentd には、ローカルネットワーク内で IDENTD リクエストを正しいマシンへ中継できるようにする機能がある。
もし、スクリプトの中で開けておきたいポートが他にもあるというのなら、それもいいだろう。やり方はもう充分に分かっているはずだ。ただ tcp_packets チェーンの行をどれかカット & ペーストして、お好みのポートへと書き換えてやれば終わりだ。
INPUT チェーンで捕捉したのが UDP パケットなら、udp_packets チェーンへ送り込まれる。そこでは、再度 -p udp によってマッチを行い、送信元アドレス 0.0.0.0 ネットマスク 0.0.0.0 で全部をマッチさせる。言い換えれば、すべてを振り出しから検査し直すわけだ。ただし今度は、インターネットに対して開け放ちたい特定の UDP ポートを受け入れる。ステート機構が面倒を見てくれるので、送信してくるホストのポートに合わせてファイヤーウォールに穴を開ける必要はない。ポートを開放する必要があるのは、例えば DNS のように、UDP ポートを使用するサーバを走らせるつもりがある場合だけだ。ファイヤーウォールへ入ってくるパケットのうち、既に (こちらのローカルネットワークによって) 確立済み (established) のコネクションに属するものは、INPUT チェーンの先頭の --state ESTABLISHED,RELATED というルールによって、これ以前に自ずと認可されているのだ。
実際のスクリプトでは、DNS 検索に使用される 53番ポートを送信元とする進入パケットは ACCEPT していない。そのためのルールは書いてあるが、デフォルトではコメントアウトしてある。ファイヤーウォールを DNS サーバとして動作させたい場合には、その行のコメント記号を外してしていだたきたい。
僕の個人的都合で、 123番ポートつまり NTP (network time protocol) にも許可を与えている。このプロトコルは、非常に正確なクロックを備えたタイムサーバを参照して、自分のマシンのクロックを合わせるためのもの。大概の人には必要ないと思ったので、デフォルトでは許可していない。しかし、他の項目と同じで、ルールは一応書いてあり、コメントを外せば働くようになっている。
スクリプトでは現状、許可していないが、 2074番ポートはある種のリアルタイムマルチメディアアプリケーションで使用されるポートだ。その例が speak freely で、スピーカーとマイク、欲を言えばヘッドセットを使って、他の人々とリアルタイムで話すことができる。もし使いたければ、ただコメント記号を取り除くだけですぐに使用できる。
4000番ポートは ICQ プロトコル。 Mirabilis の ICQ と呼ばれるアプリケーションで使用するプロトコルとして広く知られている。 Linux 用にも、少なくとも 2,3種類の ICQ クローンがあり、世界中で最も広く使われているチャットプログラムのひとつとなっている。これについてこれ以上詳しく説明する必要はないのではなかろうか。
ここで、大量のログに悩まされる場合に、それぞれ違った状況に対応するふたつのルールが書かれている。最初のルールは、宛先ポートが 135 から 139 のブロードキャストパケットをブロックする。これらは、マイクロソフトユーザがたいてい装備する NetBIOS つまり SMB が使うポートだ。このルールは、ファイヤーウォール外のマイクロソフトネットワークの活動を一切 iptables にロギングさせない働きを持つ。 2番目のルールも余計なログを抑えるためのものだが、こちらは、外からの DHCP クエリを処置する。これは、あなたの外部ネットワークがノンスイッチングタイプのイーサネットで組まれていて、クライアントが DHCP によって IPアドレスを獲得している場合に特有の現象だ。上記のような環境に置かれている場合には、多量のログに悩まされる可能性がある。
最後のふたつのルールは、もしかするとそうした種類のログに興味がある人もいるかもしれないので、敢えて採用を見合わせている。神経質すぎるログに苛まれているのなら、ここでそれらのパケットを破棄してみてほしい。この種のルールは、INPUT チェーンの、ログに関するルールのすぐ上にもある。 |
ここは、どんな ICMP の型を承認するか判断するところだ。 INPUT チェーンで eth0 から ICMP タイプのパケットが入ってくると、前述したように icmp_packets チェーンへリダイレクトされる。そこでは、どんな ICMPタイプ を承認するかを判定する。今のところ僕が許可することにしているのは、ICMP echo request と、 TTL equals 0 during transmit、 TTL equals 0 during reassembly の 3種類だけだ。その他の種類の ICMP を許可してないのは、それらほとんどの ICMPタイプ が RELATED ステートのルールでカバーできてしまうからである。
或る ICMP パケットが、すでに存在するパケットかストリームへの返答として送られた時には、そのパケットは元のストリームに対して RELATED であると判定される。ステートに関して詳しく知るには、チャプター ステート機構 を読んでいただきたい。 |
これらの ICMP パケットを承認するのには、以下のような理由がある。 Echo Request は echo応答 (echo reply) を要求するために用いられるもので、このことから、主に、或るホストがどこであれネットワーク上で利用可能かどうか調べたい時に、他ホストへの ping に利用される。このルールがないと、どこのネットワークからも、我々のホストが利用可能かどうか確かめることができなくなってしまう。よく、べつにインターネットから存在が見えなくたって構わないという理由で、このルールを削除したがる人もいる。このルールを削除すれば、インターネット側からファイヤーウォールへ打たれる無意味な ping を無効化するには、効果てきめんだ。ファイヤーウォールは、兎にも角にもそれらに返答しなくなるのだから。
Time Exceeded (つまり TTL equals 0 during transit と TTL equals 0 during reassembly) は、どこかのホストを trace-route したい時の備えと、パケットの Time To Live がゼロになった際にその旨の返答を受け取る時のために許可している。実際にどこかへ trace-route する場合を考察してみよう。まず最初は TTL = 1 から始める。すると 1つ目のホップで 0 に減らされるので、 trace-route の最終目的であるホストへの途上にある最初のゲートウェイから、 Time Exceeded が送り返されてくる。次は TTL = 2 にして、2番目のゲートウェイが Time Exceeded を返してくる。これが、最終到達目的のホストから返答を受け取れるまで繰り返される。このようにして、本当に到達したいホストまでの途上にあるすべてのホストから返答を受け取り、経由するホストを知ることができ、どのホストに障害があるのか分かるのだ。
ICMP タイプすべてのリストについては、付録 ICMPタイプ を見ていただきたい。 ICMP タイプとそれらの用途をさらに詳しく知りたい向きには、下記のドキュメントとリポートを読むことをお勧めする。
RFC 792 - Internet Control Message Protocol by J. Postel.
ちなみに、僕がブロックしている ICMP タイプのうちいくつかは、あなたにとっては適当ではないかもしれない。ただ、僕のところに関する限り、ここで不許可にした ICMP タイプをすべてブロックしても、何の問題もなく完璧に動作している。 |
前にも書いたように、 INPUT チェーンはほとんどの重労働を他のチェーンに任せている。こうすることによって、 iptables に余計な負荷を発生させず、そうでなければ高負荷時にパケットを破棄してしまうような遅いマシンでも、ずっとうまく動作するだろう。これを可能にしているのが、多くのパケットで重複するような細かいチェックを共通化して行い、それが終わったパケットを特定のユーザ定義チェーンへ送るという手法だ。これにより、パケットが巡らなければならないルールセットを極力切り詰め、結果として、ファイヤーウォールはパケットフィルタリングによるオーバーヘッドを被らずにすむのだ。
まず初めに、不良パケットでないか、いくつかの検査をする。これは、すべての TCP パケットを bad_tcp_packets チェーンへ送ることによって行う。このチェーンは、造りのおかしいパケットや、受け入れに適さない異常を検査する少々のルールから成っている。詳細に渡る説明は、当チャプターの bad_tcp_packetsチェーン セクションに書いた。
この段階で、前提的に信頼しているネットワークからのトラフィックを探す。信頼するネットワークには、ローカルネットワークアダプタとそこから入ってくるトラフィック、こちらのループバックインターフェースから出入りする全トラフィック、それに、現状で割り当てられているすべての IPアドレス (インターネットIPアドレスも含め、そうしたアドレスの一切) が含まれる。実際のスクリプトでは、LAN からファイヤーウォールへ向けての活動を許可するためのルールを、一番先頭に配置することにした。というのは、ローカルネットワークが発生させるトラフィックのほうが、インターネットコネクションよりも多いからだ。このようにすれば、ルールを一個一個当たっていくのに消費される負荷を、可能な限り抑えることができる。ファイヤーウォールを通るトラフィックにはどんなものが最も多いのか、常に検討を心がけるのは良いことだ。そうやって、なるべく最適に近づけようとルールを並べ替えているうちに、ファイヤーウォールの負荷を下げ、ネットワーク内での混雑を低減できるところへ行き着けるのだ。
インターネットインターフェースからの進入の可否を決める "実効的" なルールに取りかかる前に、オーバーヘッドを抑える目的で RELATED ルールを設定している。このルールは、インターネットIPアドレスへの ESTABLISHED か RELATED なストリームに属するパケットをすべて承認するステートルールだ。同様のルールは allowed チェーンにもあり、このルールと重複する感もあるが、こちらのほうが allowed にあるものより先に評価される。それにも拘わらす allowed チェーンの --state ESTABLISHED,RELATED ルールを捨てないのには、命令句をカット & ペーストしたい際の便など、いくつか理由がある。
INPUT チェーンでは次に、$INET_IFACE
インターフェースから進入した TCP パケットをマッチさせ、前述した tcp_packets チェーンへ送る。それから、同様にして $INET_IFACE
の UDP パケットをマッチさせて udp_packets チェーンへ送り、最後に、ICMP パケットを icmp_packets チェーンに送る。ファイヤーウォールというものは、一般的に、扱う機会が最も多いのが TCP パケットで、次に UDP、最後が ICMP パケットとなる。ただし、これはあくまでも一般的な話であり、あなたのところでは違うかもしれない。先ほどのネットワークベースのルールと同じように、ここでも、まったく同様のことを遵守すべきである。一番トラフィックが多いのはどれだろうか? ルール同士はオーバーヘッドを最も小さくするように組まれているだろうか? これは、データを大量に送り出すようなネットワークでは、避けては通れない考察となる。下手なルールセットを書けば、ペンティアム III 相当のマシンがルール 100個のシンプルなルールセットで音を上げてしまうこともあるし、100M ビットのイーサネットカードがキャパシティいっぱいでフル稼働ということにもなりうる。自分のローカルネットワークのルールセットを書く際には、重要な意味を持つ工程となる。
デフォルトでは無効にしてあるが、ここでもうひとつルールがあり、Linux ファイヤーウォールの外側にマイクロソフトネットワークがある場合に余計なロギングを抑える働きがある。マイクロソフトクライアントには、224.0.0.0/8 のアドレス範囲へマルチキャストパケットをごまんと送信する悪い癖がある。そこで、機会のあるうちにこれらのパケットをブロックして、ログがそれらで溢れないようにするのだ。その他にも、udp_packets で行っているのと同様なふたつのルール (UDPチェーン 参照) がある。
デフォルトポリシーに行き着く手前で、問題やバグが起こった時に発見できるよう、ログを取っている。これらは我々が許したくないパケットのことかもしれないし、誰かが我々に対して何か良くないことをしようとした仕業かもしれないし、あるいは最後の可能性として、許可すべきトラフィックをファイヤーウォールで不許可にしていることから生じた問題かもしれない。いずれにしても、対処の助けとなるよう、知っておく必要がある。ただし、ログがゴミで溢れかえってログパーティションがはちきれるのはご免なので、最大でも 1分間に 3パケットしか記録しない。また、各ログの出所が分かるよう、すべてのログエントリに接頭辞 (prefix) が付くよう設定している。
ここまで来てまだ捕獲されていないものは、INPUT チェーンのデフォルトポリシーによってすべて DROP される。デフォルトポリシーは、かなり以前、当チャプターの デフォルトポリシーの設定 セクションで設定している。
現在のシナリオでは、FORWARD チェーンが持つルールはかなり少しで済んでしまう。まず bad_tcp_packets チェーンへパケットを送る 1行のルールがある。前述の INPUT チェーンでも使っているルールだ。 bad_tcp_packets チェーンは、パケットの種類に関わらず、いろいろなルールから再利用できるように作られているのだ。
この最初の不良パケット検出が終わったところから、FORWARD チェーンの本格的なルールが始まる。最初のルールは、 $LAN_IFACE
から進入するトラフィックを、他のどのインターフェースへでも、まったく規制することなしに承認する。言い換えると、このルールは LAN からインターネットへのすべてのトラフィックを許可していることになる。 2番目のルールは、 ESTABLISHED と RELATED なトラフィックがファイヤーウォールを抜けて帰ってくるのを許可する。これも別の言い方をすると、パケットのうち、内部ネットワークから開始されたコネクションに属するものを、ローカルネットワークへ自由に帰ってこられるようにしている。 FORWARD チェーンのデフォルトポリシーは既に DROP に設定してあるので、ローカルネットワークがインターネットにアクセスするためには、これらのルールが必要なのだ。これはかなり狡猾な働きをしてくれる。つまり、ローカルネットワーク上のホストはインターネット上のホストへ接続できる一方で、インターネット上のホストから我々の内部ネットワークのホストへの接続はブロックしてくれるのだ。
最後に、何らかの理由で FORWARD チェーンで中継を許されなかったパケットを記録するロギングルールも用意している。ここで見られる出来事は、造りのおかしいパケットとその他の問題もろもろ、といった辺りになるだろう。原因はハッカーのアタックということもあるし、パケットが壊れていたという場合もある。これは、ログ接頭辞が "IPT FORWARD packet died: " であることを除けば、INPUT チェーンで使っているルールとまったく同じものだ。ログ接頭辞の主たる活用法はログエントリを区別することで、パケットがどこでログされたかや、ヘッダオプションを調べたい時に、ログエントリの判別基準として活用することができる。
このマシンは現在のところファイヤーウォールとワークステーションの二足のわらじを履いているわけだが、前提として、このマシンを使うのは自分だけに限られているので、送信元アドレスが $LO_IP
か $LAN_IP
または $INET_IP
であれば、ここから出ていくものはほとんど全部許可することにしている。僕のマシンで内輪の誰かがやったのだと疑うことになってしまうが、上記以外は全部、何らかの成りすましをしている可能性が高い。一番最後には、破棄されるパケット全部のログを採る。パケットが破棄される際には、問題への対処がしやすいよう、パケットの情報をログに採っておこうと思うのが当然だ。ここで捕まるのは、厄介なエラーのログか、偽装を受けたいかがわしいパケットのログのどちらかだろう。それらのパケットは、最終的にはデフォルトポリシーによって DROP される。
PREROUTING チェーンは、その名前が示す通り、パケットを filter テーブルの INPUT チェーンへ送るか FORWARD チェーンへ送るかを決定するルーティング判断が下される前の段階で、パケットにネットワークアドレス変換 (network address translation) を施す。このスクリプトに関する限り、なぜ PREROUTING チェーンについて触れるかと言えば、ここではいかなるフィルタリングも行ってはならないという点を改めて確認しておきたい気持ちに迫られたからだ。PREROUTING チェーンはストリームの先頭パケットが通るだけ、つまり、後続のパケットはこのチェーンによる評価をまったく受けることなく素通りしてしまう。このスクリプトにおいては PREROUTING チェーンを一切使用していないが、特定のパケットに DNAT を掛けたいとすれば、ちょうど今頃、我々はこのチェーンに取り組んでいるはずだ。例えば、ローカルネットワークのいずれかのマシンで WEBサーバなどを運営しようとする場合がこれに当たる。 PREROUTING チェーンについての詳しい説明は、チャプター テーブルとチェーンの道のり で読むことができる。
PREROUTING チェーンはいかなるフィルタリングにも使用してはならない。理由はいくつかあるが、中でも重要なのは、このチェーンを通るのはストリームの最初のパケットだけだという点だ。確固たる理由がない限りは、 PREROUTING チェーンは ネットワークアドレス変換 専門に使用するべきである。 |
ここまで来れば、残る仕事はネットワークアドレス変換を稼働させることだけだ。僕はそうだと思うんだが、違うだろうか? まず第一に、インターネットに接続している外部インターフェースから出ていくパケット全部に NAT を掛けるべく、ルールを nat テーブルの POSTROUTING チェーンに追加する。外部インターフェースは、僕の場合は eth0 だ。ただし、これらは固有であり、その設定を自動化するための変数が、すべてのスクリプト例に備え付けてある。 -t オプションは、どのテーブルにルールを挿入するかを iptables に指示するもので、今回の場合は nat テーブルとなる。-A コマンドは POSTROUTING という名の既存チェーンに新たなルールを追加 (Append) することを表し、-o $INET_IFACE は、インターフェース $INET_IFACE
(スクリプトでのデフォルトで言えば eth0) から出ていこうとする全送出パケットをマッチさせることを意味している。そして最後に、パケットを SNAT せよというターゲットを与えている。結果として、このルールにマッチするすべてのパケットは、SNAT され、あたかも我々のインターネット用インターフェースから来たかのように化ける。送出パケットに付ける IPアドレスを --to-source オプションで SNAT ターゲットに与えるのも忘れないでほしい。
当スクリプトでは、MASQUERADE ではなく SNAT ターゲットを使うことにした。これにはふたつの理由があり、ひとつは、このファイヤーウォールが固定IPアドレスを前提としているから。ひとつ目に付随して理由を挙げるとすれば、可能ならば SNAT ターゲットを使ったほうが、動作が速く、効率がよいという点がある。そしてもちろん、実際の生きたサンプルで SNAT がどう働き、どう使われるのかを示すという目的もある。もし、あなたのところが固定IPでないのなら、是非とも MASQUERADE ターゲットの使用を検討してほしい。 MASQUERADE ターゲットは、 NAT を行ってくれるのはもちろんだが、使用すべき IPアドレスを自動的に把握してくれるので、簡単な使い方で単刀直入に便利な機能を提供してくれる。マシンパワーを少々余計に消費するとはいえ、DHCP を利用している場合などには、充分それに見合う価値がある。 MASQUERADE ターゲットがどのようなものかよく見てみたいと思うなら、rc.DHCP.firewall.txt スクリプトを覗いてみるといいだろう。