OXY NOTES

コピペから脱出!iptablesの仕組みを理解して環境に合わせた設定をしよう

Linuxのファイアウォール「iptables」について入門から実践まで解説

数回に分けてLinuxのファイアウォール「iptables(アイピーテーブルズ)」について解説します。

ネット上に有益な設定が溢れているので、あまり理解しないままコピーペーストで運用している方も多いはず。
しかしそれでは実際に攻撃された際に対処できません。

そこでこのページでは、初めてファイアーウォールについて学ぶ方でも理解できるように、全体像と細かな設定の意味について解説します。


目次

ファイアーウォールの種類
NATについて
パケットフィルタリングの概要と書式
テーブルについて
チェインについて
オプションについて
パラメータについて
拡張パラメータについて
iptablesの記述順序とルールの適用順について
ポリシーについて


ファイアーウォールの種類

ファイアウォールと聞いて、まず何を思い浮かべるでしょうか?
ほとんどの場合、通信の許可・不許可を設定する、パケットフィルタリングを思い浮かべる方が多いのではないでしょうか?

実はファイアーウォールにはパケットフィルタリングだけでなく、さまざまな機能があります。

ファイアーウォールは大別して「パケットフィルタリング型」と「プロキシ型」があります。パケットフィルタリングは名前の通り、アクセスを制御します。今回解説するiptablesはパケットフィルタリング型です。
そのためプロキシ型については今回は掘り下げません。簡単にいえば、プロキシ型は「Webサーバやメールサーバの代理として応答する」といった用途に利用します。

iptablesはLinuxのKernel 2.4移行で標準インストールされています。そのためLinux標準のファイアーウォールといえます。
それではiptablesの各機能について解説していきます。


NATについて

NAT(Network Address Translation)とはパケットフィルタリング型のファイアーウォールに搭載されている「ネットワークアドレス変換機能」のことです。
内部ネットワークの構成を知られたくない場合や、1つのグローバルIPを複数の内部サーバに割り振る際に利用します。
つまり、ファイアーウォールで通信の制御をするルーターの役割を担うという仕組みです。

今回はNATについては詳しく解説しませんが「こちらのページ」で詳しく解説されています。興味のある方は調べてみてください。


パケットフィルタリングの概要と書式

それでは本題のパケットフィルタリングについて解説します。まずは基本的な書式を確認していきましょう。
それぞれのパラメータについては後ほど解説します。まずは概要だけ理解しておいてください。

設定ファイルは「/etc/sysconfig/iptables」に保存されています。この設定ファイルを直接編集することも可能ですが、コマンドやシェルスクリプトを利用した編集が一般的です。このページではシェルスクリプトを利用して編集する方法を解説します。

iptablesの書式

iptables [-t テーブル] コマンド [マッチ] [ターゲット/ジャンプ]

例)tcp接続の受信データを破棄するというルールを追加する

iptables -A INPUT -p tcp -j DROP

テーブルについて

iptablesではパケットを、4つのテーブルに分けて、それぞれのタイミングで制御します。テーブルには「filterテーブル」「natテーブル」「mangleテーブル」「rawテーブル」があります。

filterテーブル

filterテーブルではパケットの通過や遮断といった制御をします。通常ほとんどの設定はこのテーブルに記述します。

下の例にあるように、設定ファイルを直接編集する際は、行頭に「*(アスタリスク)」を付けて、最後は「COMMIT」で指定。その間に書かれた設定がfilterテーブルのルールとなります。

例)設定ファイル(/etc/sysconfig/iptables)にあるfilterテーブルの例

*filter
 #ここにfilter関係の記述
COMMIT

例)コマンドでfilterテーブルにポート80のパケットを破棄するルールを追加する

iptables -t filter -p tcp --dport 80 -j -DROP

iptablesでは、ほとんどの設定をこのテーブルで行うため、特にテーブルの指定をしないと、自動でfilterテーブルにルールが追加されます。

例)上と同じルールの追加

iptables -p tcp --dport 80 -j -DROP

natテーブル

上で解説した、NAT(ネットワークアドレス変換機能)を担当します。送信先や送信元といったパケットの中身を書き換える際に利用します。各通信をローカルネットワーク上のサーバへ振り分けるルーターとして機能させることができます。

例)natテーブルの例

*nat
 #ここにnat関係の記述
COMMIT

例)IP マスカレードの指定

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE

例)インターフェイスeth0から受信したポート80のTCPパケットをローカルのApacheサーバ(172.31.0.1:80)へフォワーディング

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to 172.31.0.1:80

mangleテーブル

TOS(Type Of Service)フィールド等の値を書き換えます。TOSはパケット処理の優先度付けを行い、通信品質を制御する際に利用されます。
また特定の通信マークを置き換える事もできます。

例)mangleテーブルの例

*mangle
 #ここにmangle関係の記述
COMMIT

例)ポート80の通信のTOSを高スループットのものへ書き換え

iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TOS --set-tos Maximize-Throughput

例)ポート443の通信マークをポート80のものと合わせ、マークマッチを使って通信を破棄

iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 80
iptables -t mangle -A PREROUTING -p tcp --dport 443 -j MARK --set-mark 80
iptables -t mangle -A PREROUTING -m mark --mark 80 -j DROP

Rawテーブル

あまり解説されることはありませんが、特殊なテーブルとしてRawテーブルというものも存在します。用途はmangleテーブルのように特定のパケットにマークを付けることですが、Rawテーブルでは追跡を除外するようマークを付けます。つまり、特定の通信をファイアウォールで処理せずに他の機材へ通したりといった経路制御する場合に利用します。

例えば通信が1秒間に数千といった高負荷の場合、全ての通信を1台のサーバで扱うことが難しくなります。

高負荷の原因がDNSサーバへの問い合わせだったと仮定します。
負荷分散のために「DNSのパケットに関しては、別サーバで処理させたい」そのような際に、ポート53の通信にNOTRACKマークを付けます。

するとiptablesはNOTRACKマークの付いたパケットを、記録したり処理をせずに、別のサーバへ通します
そうすることで、DNSの問い合わせを行うサーバと、それ以外の通信を処理するサーバに分けることができます。

*raw
 #ここにraw関係の記述
COMMIT

例)ポート53のUDPを通ったパケットの受信時と送信時にNOTRACKマークを付ける

iptables -t raw -I PREROUTING -p udp --dport 53 -j NOTRACK
iptables -t raw -I OUTPUT -p udp --dport 53 -j NOTRACK
この他にもSELinuxで利用するSecurityテーブルというものもあります。
特殊なテーブルのため、このページでは詳しく解説しません。

チェインについて

それぞれのテーブルの中で、どのタイミングでフィルタリングするかを決めるのがチェインです。例えば受信時についてはINPUT、送信時についてはOUTPUTといった具合です。

チェインの種類

チェインには以下の種類があります

INPUT 入力(受信)に対するチェイン
OUTPUT 出力(送信)に対するチェイン
FORWARD フォアード(転送)に対するチェイン
PREROUTING 受信時に宛先アドレスを変換するチェイン。タイミングとしてはfilterで適用されるルールより手前
POSTROUTING 送信時に送信元アドレスを変換するチェイン。これもfilterの後でパケットが送信される直前

上記のチェインを各テーブルに記述してパケットを制御しますが、それぞれのテーブルで使えるチェインは以下のように決まっています。

filterテーブル INPUT、OUTPUT、FORWARD
natテーブル POSTROUTING、PREROUTING、OUTPUT
mangleテーブル POSTROUTING、PREROUTING、INPUT、OUTPUT、FORWARD
Rawテーブル PREROUTING、OUTPUT

ユーザー定義チェイン

ユーザーが自分でチェインを定義することもできます。
よく使う処理などをあらかじめ登録しておくことで、設定の見通しが良くなります。

定義する際には「-N userchain」と記述します。そのチェインを使う際には標準で用意されたチェインと同じように、「-j userchain」と記述する。

例)通信を破棄するdropchainを作成。ポート80のパケットを作成したdropchainにジャンプさせる。(結果としてポート80のパケットは破棄)

iptables -N dropchain
iptables -A dropchain -j DROP

iptables -A INPUT --dport 80 -j dropchain

各テーブルとチェインの関係を図にすると以下のようになります。思わぬ失敗を避けるため、実際に設定する際はどこにルールを追加しているか意識しながら作業を行うようにしてください。


オプションについて

オプションで設定できるのは、「コマンド」と「パラメータ」です。「コマンド」でチェインの扱いを決めて、「パラメータ」で細かな指定をするという流れになります。

コマンドについて

コマンドはチェインをどのように扱うかを指定します。下の一覧にあるように「-A INPUT」とすれば新しい受信時のルールを追加。「-D INPUT」とすれば受信時のルールを削除という指定ができます。

よく使うコマンドの一覧

指定方法 内容
-A(--append) 指定チェインに1つ以上の新しいルールを追加
-D(--delete) 指定チェインから1つ以上のルールを削除
-P(--policy) 指定チェインのポリシーを指定したターゲットに設定
-N(--new-chain) 新しいユーザー定義チェインを作成
-X(--delete-chain) 指定ユーザー定義チェインを削除
-I(--insert) 指定したチェーンにルール番号を指定してルールを挿入する。(ルール番号を省略した際にはルール番号は1に設定され、チェーンの先頭に挿入される。)

例)filterテーブルのINPUTに登録されたルール番号3番のルールを削除

iptables -t filter -D INPUT 3

パラメータについて

受信や送信はチェインで指定できますが、特定のIPだけ遮断、プロトコルによって遮断といった細かい制御をしたい場合はパラメータで指定します。

よく使うパラメータの一覧

指定方法 内容
-s (--source) パケットの送信元を指定。特定のIP(192.168.0.1)や範囲(192.168.0.0/24)を指定する
-d (--destination) パケットの宛先を指定。指定方法は-sと同じ。
-p (--protocol) チェックされるパケットのプロトコル。 指定できるプロトコルは、 tcp、udp、icmp、allのいずれか1つか、数値。
-i (--in-interface) パケットを受信することになるインターフェース名。eth0、eth1など
-o (--out-interface) 送信先インターフェース名を指定
-j (--jump) ターゲット(ACCEPT、DROP、REJECT)を指定

例)224.0.0.1宛の受信パケットを破棄

iptables -A INPUT -d 224.0.0.1 -j DROP

「-j」で指定するターゲットについて

書式のtargetに当たる部分です。ターゲットは指定したパケットをどのように扱うかを指定します。ACCEPTであれば許可、DROPであれば破棄といった具合です。この他にユーザー定義チェインも指定することができます。

ターゲットの一覧

指定方法 内容
ACCEPT パケットの通過を許可
DROP パケットを破棄。応答を返さない。
REJECT パケットを拒否し、ICMPメッセージを返信
REDIRECT 特定ポートにリダイレクト
LOG マッチしたパケットのログを記録

拡張パラメータについて

上記のパラメータに加え、一致する条件を更に細かく指定をする際に利用します。パケットマッチングモジュールとも呼ばれます。
拡張であることを明示することなく利用できる「-p」の後に追記するタイプと、「-m」で拡張を明示してから追記するタイプがあります。

「-p」オプションによって(暗示的に)呼び出されるタイプ

指定方法 内容
--sport Source Port。送信側(クライアント側)のポート番号を指定。-p tcp か -p udp の後に指定します。
--dport Destination Port。受信側(サーバ側)のポート番号を指定-p tcp か -p udp の後に指定します。
--tcp-flags TCP のときだけ指定することができる。第1引数に評価されるTCPフラグを指定し、第2引数に設定されていなければならないフラグを指定します。指定可能なフラグは、SYN、ACK、FIN、RST、URG、PSH、ALL、NONE です。複数指定する場合はカンマで区切り、引数の間は半角スペースで区切ります。(--tcp-flags SYN,RST,ACK SYN)
--syn TCP のときだけ指定することができる。SYNビットがセットされた、いわゆるTCPの接続確立時に、正規のスリーハンドシェイク手順を踏んだ通信を指定します。上記のSYN,RST,ACK SYNの省略形。
--icmp-type ICMP のタイプを指定。pingに限定した指定をする際などに指定します。

例)ポート80で受信したTCPの受信パケットを破棄

iptables -A INPUT -p tcp --dport 80 -j DROP

例)ポート80で受信したTCPかつ、SYN、RST、ACKのうちSYNフラグだけを持った受信パケットを許可

iptables -A INPUT -p --tcp-flags SYN,RST,ACK SYN --dport 80 -j ACCEPT

「-m」オプションを付けることで(明示的に)呼び出されるタイプ

リミット拡張モジュール

limit リミット拡張モジュールを読み込み。
--limit 時間あたりに何パケットまで許可するかを指定する。second(秒)、minute(分)、hour(時)、day(日)を指定することができます。--limit の初期値は 3/hour
--limit-burst 上記のリミットが有効になる通信の回数を指定。「○回受信したら1時間に1回に制限」という際の「○回」の部分を指定する。初期値は5。

例)TCPのポート80宛の受信パケットを、ハッシュリミットで5回まで無条件で受け入れ、それ以上のパケットを1分間に1度だけ許可する

バーストしても1分経つごとに1アクセスだけ受け入れるパケットが増える。つまり5分経てば再び5パケットまで受け入れる

iptables -A INPUT -p tcp --dport 80 -m hashlimit --hashlimit-burst 5 --hashlimit 1/m --hashlimit-mode srcip -j ACCEPT

length拡張モジュール

length length拡張モジュールを読み込み。パケットのサイズをフラグとしてマッチさせることができる。
--length 「○:○」という書式で「前半の○から、後半の○サイズまで」と指定する。後半の指定を外せば○以上、前半の指定を外せば○以下という指定も可能。

例)85から65535バイトまでに一致。

iptables -A INPUT -p tcp --dport 80 -m length --length 85:65535

例)85バイト以下に一致。

iptables -A INPUT -p tcp --dport 80 -m length --length :85

state拡張モジュール

state state拡張モジュールを読み込み。パケットを追跡することでステートフル・パケットインスペクションが可能になります。詳細は後述
--state 追跡しているパケットの状態を指定。新規か、既に許可をしたものか、などを指定する。

state拡張で指定可能なパラメータ

NEW 全くの新参の接続
ESTABLISHED 既に許可された接続
RELATED 新参ではあるが許可された接続
INVALID パラメータの不明な無効な接続

例)TCPの受信パケットの内、新規の接続に関しては破棄

iptables -A INPUT -p tcp -m state --state NEW -j DROP

この他にも多くの拡張モジュールが存在します。
こちらのページ」で詳しく解説しているので興味のある方は調べてみてください。


iptablesの記述順序とルールの適用順について

指定方法がわかったので早速設定といきたいところですが、iptablesの設定を理解する上でもう1つ重要な要素があります。それが記述順序です。

基本として、設定ファイルの先頭からルールが適用されます。条件にマッチしてパケットの扱いを設定した場合、そのパケットについての設定はそこで終了します。

よくある間違いの例です。

iptables -A INPUT -p tcp -m --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -m --dport 80 -j DROP

まずポート80の通信を「許可」しています。その設定の下に何度ポート80を「拒否」する設定を書いても「許可」されます。このように適用後に進行が停止するルールの代表がACCEPTやDROPです。

継続するルール

上の例ではパケットに関するルールが停止しましたが、継続してルールが適用されるものもあります。それがLOGやTOSなどです。
ポート80の通信をログファイルに書き出してから破棄する場合は以下のように記述します。

iptables -A INPUT -p tcp -m --dport 80 -j LOG
iptables -A INPUT -p tcp -m --dport 80 -j DROP

注意点としては、先に破棄(DROP)を記述してしまうと、DROP移行はルールが停止するため、ログファイルには何も書き出されません。
上の例からわかるように、適用後に進行が停止するルールと、適用後も進行するルールを意識して使用する必要があります。

順序を間違えることでよくある間違い

TCP通信を全て許可してから、ポート80のTCP通信を破棄という設定です。すでにTCP通信全体を許可しているので、それ移行TCP通信についてのルールは適用されません。

iptables -A INPUT -p tcp -j ACCEPT
iptables -A INPUT -p tcp -m --dport 80 -j DROP

ポート80のTCP通信を破棄して、それ以外のTCP通信を全て許可したい場合は、以下の順序で記述してください。

iptables -A INPUT -p tcp -m --dport 80 -j DROP
iptables -A INPUT -p tcp -j ACCEPT

はじめにポート80のTCP通信を破棄しているので、それより後にTCP通信全体を許可しても、ポート80のTCP通信は破棄されます。

このようにルールの記述順で全く違った設定になってしまいます。特に設定の例をコピー・ペーストして利用していると、間違えやすいので注意してください。


ポリシーについて

ポリシーとはチェイン全体に適用されるルールのことです。ポリシーの設定は「-P」オプションを使って記述します。

ポリシーでは2つのパターンが考えられます。

ほとんどの場合、INPUTチェインに関しては安全性を考えて1番の「拒否してから許可」で運用します。

例)filterテーブルの受信はすべて破棄。送信はすべて許可

iptables -t filter -P INPUT DROP
iptables -t filter -P OUTPUT ACCEPT

上で設定後にパケットの扱いが停止するルールと、停止しないルールについて解説しましたが、ポリシーは停止しません。そのため、後に記述するルールで上書きすることができます

例)ポリシーでINPUT全体を破棄した後に、ポート80を許可する

iptables -P INPUT DROP
iptables -A INPUT -m --dport 80 -j ACCEPT

以上でiptablesの仕組みと設定方法についての基本は終了です。

初めてiptablesについて学んだ方は「規制が多くて敷居が高そう」と感じてるのではないでしょうか。しかし安心してください、実際に設定してみると意外に簡単で、それらの規制が逆に便利に感じられます。

次は実践編として、シェルスクリプトを利用して設定ファイルを編集します。
iptablesの設定ファイルをシェルスクリプトを利用して動的に作成