iptables の使い方

# はじめに

iptables は Linux カーネルに組み込まれているファイアウォールです。そのホストの通信を制限したり、転送したりする目的で使用します。なお、ここでは IPv4 を前提にしていますが、IPv6 にもほぼ同じ使い方の ip6tables があります。

# 設定例

細かい説明は後回しにして iptables の設定ファイルの例を示します。

```conf:/etc/iptables/iptables.rules
# 原則として入力・転送は全て禁止
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# ループバックからの接続は全て許可
-A INPUT -i lo -j ACCEPT

# PING の受信を許可するが頻度はクライアント辺り 1 秒に 1 回程度に制限
-A INPUT -p icmp --icmp-type echo-request -m hashlimit --hashlimit-name t_echo --hashlimit-mode srcip --hashlimit-upto 1/s --hashlimit-burst 5 --hashlimit-htable-expire 10000 -j ACCEPT

# 既に確立した通信はポート番号に関係なく許可
-A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT

# SSH 接続を許可するが頻度はクライアント辺り 1 分間に 1 回程度に制限
-A INPUT -m state --state NEW -m tcp -p tcp --syn --dport 22 -m hashlimit --hashlimit-name t_sshd --hashlimit-mode srcip --hashlimit-upto 1/m --hashlimit-burst 5 --hashlimit-htable-expire 600000 -j ACCEPT

# DNS サーバーからのレスポンス受信を許可
-A INPUT -p udp --sport 53 -j ACCEPT

COMMIT
```

# 解説

## ポリシー

```conf
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
```

原則としてパケットの受信と転送は全て禁止し、送信は許可するように設定します。もちろん、全くパケットを受信できないのは困るので、このポリシーの例外をこの後ろに設定していきます。

## ループバック

```conf
# ループバックからの接続は全て許可
-A INPUT -i lo -j ACCEPT
```

Linux では他のホストとの通信だけではなく、プロセス間の通信に socket が使用されることがあります。このようなホストに閉じた通信のことをループバックと呼びます。ループバックの通信を制限してしまうといろいろなプログラムが正常に動作しなくなる可能性が高いので、全面的に許可しています。

## Ping

```conf
# PING の受信を許可するが頻度はクライアント辺り 1 秒に 1 回程度に制限
-A INPUT -p icmp --icmp-type echo-request -m hashlimit --hashlimit-name t_echo --hashlimit-mode srcip --hashlimit-upto 1/s --hashlimit-burst 5 --hashlimit-htable-expire 10000 -j ACCEPT
```

ホストが Ping に応答できるようにしています。ただし、大量の Ping を送信することでシステムに負荷を加える ICMP Flood (Ping Flood) への対策として、その頻度を制限しています。パケットを受信（あるいは転送や送信）する頻度を制限には hashlimit モジュールを使用します。

`--hashlimit-name` はハッシュテーブルの名前です。このモジュールを使用するためにはパケットの受信履歴などを記録する必要があるため、その保存先のファイル名を他のルールと重複しないように設定します。

`--hashlimit-mode` はどのような単位でパケットを受信する頻度を制限するかを指定します。ここでは `srcip` を設定しているため送信元の IP アドレスごとにパケットの受信を制限することになります。他に `dstip` `srcport` `dstport` などのフラグがあります。複数のフラグの組み合わせも可能です。このオプションを省略した場合は送信元や受信先には関係なく、システム全体としてパケットを受信する頻度を制限することになります。

`--hashlimit-upto` と `--hashlimit-burst` には具体的にどのような頻度までパケットの受信を許可するかを設定します。この 2 つの設定値の意味を理解するには、パケットの受信が許可される頻度をバケツの容量に例えるとわかりやすいと思います。1 回のパケットを受信するためにはバケツから 1 リットルの水を取り出す必要があると考えてください。システムはバケツが満水の状態で起動しますが、水が不足するとパケットを受信できなくなります。`--hashlimit-upto 1/s` は 1 秒間に 1 リットルの水をバケツに注いでいることを意味します。ただし、`--hashlimit-burst 5` はバケツの容量が 5 リットルしか無いことを意味します。つまり、どんなに短い周期でパケットが到着しても 5 回までは問題なく受信できますが、それ以上連続してパケットを受信するためには再びバケツに水が貯まるまで待たなければならないということです。

`--hashlimit-htable-expire` にはハッシュテーブルのレコードの期限をミリ秒単位で指定します。$\frac{hashlimit-burst}{hashlimit-upto}$ よりも大きな値を設定しなければ受信頻度が期待通りに制限されません。ここでは余裕をもって計算結果 (5 秒) の 2 倍に相当する 10000 ミリ秒に設定しています。

## TCP

```conf
# 既に確立した通信はポート番号に関係なく許可
-A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
```

TCP で 1 度接続を確立した後は、そのポートで任意のパケットを受信することを許可します。後述する SSH サーバーの設定はあくまでもサーバーへの接続を許可するものですが、この設定により接続に成功した後の通信もファイアウォールを通過します。

また、自分がクライアントとしてサーバーに接続することは前述の `:OUTPUT ACCEPT [0:0]` で許可されていますが、接続に成功した後にパケットを受信できるのはこの設定のおかげです。

## SSH サーバー

```conf
# SSH 接続を許可するが頻度はクライアント辺り 1 分間に 1 回程度に制限
-A INPUT -m state --state NEW -m tcp -p tcp --syn --dport 22 -m hashlimit --hashlimit-name t_sshd --hashlimit-mode srcip --hashlimit-upto 1/m --hashlimit-burst 5 --hashlimit-htable-expire 600000 -j ACCEPT
```

TCP の 22 番ポートへの接続を許可します。Ping と同じように hashlimit モジュールを使用して接続の頻度を制限しています。`--dport 22` を他のポート番号に変更すれば他の用途にも応用できます。例えば HTTP なら 80 番を、HTTPS なら 443 番ポートの通信を許可してください。ただし、HTTP や HTTPS ではもう少し hashlimit の制限を緩く設定するべきです。

## DNS クライアント

```conf
# DNS サーバーからのレスポンス受信を許可
-A INPUT -p udp --sport 53 -j ACCEPT
```

DNS は TCP/UDP のどちらかあるいは両方を使用します。TCP については `:OUTPUT ACCEPT [0:0]` `-A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT` の設定で既に許可されています。UDP については接続の概念が存在しないので、明示的に 53 番ポートから送信されたパケットを受信できるように許可する必要があります。

# 設定の反映

iptables を再起動してください。Arch Linux など systemd を使用している場合は以下の通りです。

```console
# systemctl restart iptables
```