- 自宅の DMZ を構成するために nftables でステートフルファイアウォールを作った
- 長年、iptables を使ってきたが Debian 11 リリースに備え、移行することにした
- 単純なファイアウォールを構成するための最低限の設定をまとめておく
iptables から nftables への移行
Debian 11 (bullseye) の Freeze がはじまったので、自宅サーバ移行検証をしていたところ、ちょっとファイアウォールが気になった。
Debian では、10 (buster) で既に nftables がデフォルトになっており、iptables は nftables の互換レイヤーとして機能している。
今のところ、bullseye でも iptables が無くなるという話はないので、もう1回引っ張っても問題は無いのだが、nftables がデフォルトになった次のバージョンで移行というのはタイミングとしても良いので、ここで移行することにした。
検証環境
想定する構成
目指すはシンプルな2ファイアウォール型のDMZを構成すること。抽象化したモデルとしては以下のような形になる。
なお、我が家はいわゆる「逸般の誤家庭」ではないので、Y社 や C社 のエンタープライズ向けルーターなんてものはない*1。 そのため、実態としては以下のような形になる。
この図の「Linux server」が今回のターゲットになる。
簡単のため、以下 DMZ 側の NIC を eth-dmz
、Trust Zone 側の NIC を eth-trust
と記載する。目指すルールは以下。
- Trust Zone から DMZ への通信は全て許可
- DMZ から Trust Zone へのセッション確立済みの通信を許可
- DMZ から Trust Zone への特定のマシンから、特定のマシンの特定のポートへの通信を許可
nftables で作るステートフルファイアウォール
IP forward の有効化
まずは、eth-dmz
と eth-trust
間のパケット転送を有効にする。ここは nftables に限った話じゃないので詳細は触れない。
$ sudo vim /etc/sysctl.conf #net.ipv4.ip_forward=1 ↓ net.ipv4.ip_forward=1
適用。
$ sudo sysctl -p
nftables の導入
Debian buster および bullseye では、iptables と nftables の両方が提供されているため、明示的に nftables を導入する。
なお、パッケージは nftables のみでよい。iptables と iptables-persistent のように、管理インタフェースと永続化のための仕組みで分かれていたりはしない。
$ sudo apt install nftables
nftables のルールを書く
nftables は、全ての操作が nft
コマンドで完結する。
iptables と同様、テーブル、チェイン、ルールの概念がある。ただし、デフォルトのテーブルやチェインは無い。そのため「nftables はなにもしない」状態から設定を作り込んでいく*2。
大まかな流れは以下のようになる。
なお、<>
はパラメータ、[]
は省略可能な引数を指す。
1. 設定済みのルールをクリア
$ sudo nft flush ruleset
2. テーブルを作成
$ sudo nft add table [<アドレスファミリ>] <テーブル名>
アドレスファミリには以下が設定できる。
IP層でのステートフルファイアウォールでは inet
を使えば事足りる。具体例は以下。
$ sudo nft add table inet FIREWALL
3. テーブルにベースチェインを登録
$ sudo nft add chain [アドレスファミリ] <テーブル名> <ベースチェイン名> \ { type <タイプ> hook <フック> [device <デバイス>] priority <優先度>; [policy <ポリシ> ;] }
ベースチェインは、初めに呼び出されるチェイン。Cでいう main()
みたいなもの*3。
「タイプ」は、チェインの用途を指定する。
タイプ | 用途とアドレスファミリ | 利用できるフック |
---|---|---|
filter |
汎用 (全ファミリ) | 全て |
nat |
NAT (ip , ip6 ) |
prerouting , input , output , postrouting |
route |
ルーティング (ip , ip6 ) |
output |
「フック」は、チェインの実行タイミングを指定する。
フック | タイミング | 利用できるアドレスファミリ |
---|---|---|
ingress |
デバイスがパケットを受信した際 | netdev |
prerouting |
パケット受信後、ルーティング判定の前 | ip , ip6 , inet , bridge |
input |
ルーティング判定後、宛先が自分自身と判断した際 | ip , ip6 , inet , bridge , arp |
forward |
ルーティング判定後、宛先が他ホストと判断した際 | ip , ip6 , inet , bridge |
output |
自分自身が他ホスト宛のパケットを生成した際 | ip , ip6 , inet , bridge |
postrouting |
他ホストへパケットを送信する直前 | ip , ip6 , inet , bridge |
「プライオリティ」は、同じフックが指定されたベースチェインが複数ある場合の優先度を指定する。値が小さいほど、先に実行される。
「ポリシ」は、チェインの中で取り扱いが決まらなかった際の取り扱いを accept
か drop
で指定する。未指定だと accept
になる。
具体例は以下の通り。
$ sudo nft add chain inet FIREWALL FILTER { type filter hook prerouting priority 0 \; }
type filter
(汎用) でhook prerouting
(ルーティング判定前) にpriority 0
(最優先) で処理するベースチェインで、- 合致するルールが無い場合は受け入れる
policy を accept
もしくは省略した場合は、確実に不要なパケットを落とすルールを入れることを忘れてはいけない。
4. テーブルにレギュラーチェインを登録
$ sudo nft add chain [アドレスファミリ] <テーブル名> <レギュラーチェイン名>
レギュラーチェインは、他のチェインから明示的に呼び出されるチェイン。呼び出し元のチェインの設定を引き継ぐため、チェイン名の登録のみ。具体例は割愛。
5. チェインにルールを登録
$ sudo nft add rule [<アドレスファミリ>] <テーブル名> <チェイン名> <ステートメント>
当該チェインが呼び出された際に、適用するルールを実行順に登録する。
「ステートメント」は、具体的な制御内容を記載するが、網羅すると膨大な数になるため参考ページを記載し、ここでは具体例の記述にとどめる。
「確立済みの通信を許可する」ための設定例は以下になる。
$ sudo nft add rule inet FIREWALL FILTER \ ct state related,established accept
ct
(コネクション) のstate
(状態) がrelated
かestablished
なものをaccept
(許可) する。
「特定のマシンから、特定のマシンの特定のポートへのアクセスを許可する」ための設定例は以下になる。
$ sudo nft add rule inet FIREWALL FILTER \ ip saddr 10.0.0.100 ip daddr 172.16.0.100 tcp dport 443 accept
ip
(IPv4ヘッダ) のsaddr
(送信元IPアドレス) が10.0.0.100
であってip
(IPv4ヘッダ) のdaddr
(送信先IPアドレス) が172.16.0.100
であってtcp
(TCPヘッダ) のdport
(接続先ポート番号) が443
のものをaccept
(許可) する。
「(先のステートメントから漏れたパケットで、)特定のNICから入力された全てのパケットを破棄する」ための設定例は以下になる。
$ sudo nft add rule inet FIREWALL FILTER \ meta iifname eth-dmz drop
meta
(その他のメタデータ) のiifname
(入力インターフェース名) がeth-dmz
のものをdrop
(破棄) する
設定済みのルールを確認
$ sudo nft list ruleset
上記のコマンドにて、現在の設定を出力できる。出力結果の具体例は以下になる。
table inet FIREWALL { chain FILTER { type filter hook prerouting priority 0; policy accept; ct state established,related accept ip saddr 10.0.0.100 ip daddr 172.16.0.100 tcp dport https accept iifname "eth-dmz" drop } }
ルールの永続化
list ruleset
の出力は、そのまま nftables の設定ファイルとして用いることができる。
Debian の場合、nftables
サービスが起動する際に /etc/nftables.conf
があればそれをロードする。そのため、以下のようにすることで設定を永続化できる。
$ sudo nft list ruleset | sudo tee /etc/nftables.conf $ sudo systemctl enable nftables $ sudo systemctl restart nftables
なお、手動で設定ファイルをロードしたい場合は以下のようにする。
$ sudo nft -f <ファイル名>
まとめ
最後に、上記のような設定を一発で行うためのスクリプトを作ったので公開しておく。
ライセンスは、MITライセンスで。