netfilterとfirewalldとiptablesとnftablesの関係


はじめに

netfilterについて少し調べてみました。
いろいろと認識の相違があったことがわかったので、記録します。

認識の相違

CentOS8(に限らずだけど)では、以下のことがわかった。
iptablesはiptablesではなくnftablesである
firewall-cmdで設定したルールはiptablesでは表示されない
すべてのルール確認はnftを使う

Linuxでのパケットの流れについて

netfilter

netfilterはLinuxでパケットの処理をする基本ルール
インタフェイスで受信したパケットがどんな処理をされていくか、というやつです。

netfilter.orgによると、

netfilterは、カーネルモジュールがネットワークスタックにコールバック関数を登録できるようにするLinuxカーネル内の一連のフックです。登録されたコールバック関数は、ネットワークスタック内の各フックを通過するすべてのパケットに対してコールバックされます。

だそうです。ありがとうgoogle翻訳!

つまり、パケットがネットワークスタックを通り抜けるときに、netfilterで定義した「フック」というものにより、何らかの処理を挟み込めるようにした仕組み。ということでしょうか。

netfilterのchain

ネットワークスタックを通り抜ける一連の処理のことをchainと呼びます。
chainは、以下のものから構成されます。

名称 意味
hook (ネットワークスタックの)どの部分 で
priority どのような順番 で
type どのような処理をする か

基本は以下の図です。

参考:https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

netfilterのchainのhook

「(ネットワークスタックの)どの部分」で、処理が行われるか、を表現したものが、hookです。
つまり、ひっかけるタイミングを表現しています。

上図の中央付近にある、 routing decision の部分で処理が変わっているのがわかると思います。
流入したパケットが「ローカル宛て」か、「ローカル宛てじゃない(=転送する)」かで、使われるhookが変わります。

hookの部分にフォーカスしたものが以下です。

                                             Local
                                            process
                                              ^  |      .-----------.
                   .-----------.              |  |      |  Routing  |
                   |           |-----> input /    \---> |  Decision |----> output \
--> prerouting --->|  Routing  |                        .-----------.              \
                   | Decision  |                                                     --> postrouting
                   |           |                                                    /
                   |           |---------------> forward --------------------------- 
                   .-----------.

参考:https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks

つまり、宛先によって、使われるhookが以下の2通りになります。

ローカルあての場合
prerouting -> input -> output -> postrouting

転送する場合
prerouting -> forward -> postrouting

(このほかにも、ingressというL2へのパケットに対するhookもあるみたいです。)

netfilterのchainのpriority

「どのような順番」で、処理が行われるか、を表現したものが、priorityです。
つまり、各hookの中での処理の順序を表現しています。

priorityは正負の数値で表現されますが、別名が定義されています。
dstnatとscrnatはそれぞれ、preroutingとpostroutingのhookでのみ利用可能です。

ですが、実体は飽くまで数値です。

通称 定義 priority 利用可能なhook
- NF_IP_PRI_RAW_BEFORE_DEFRAG -450 すべて
- NF_IP_PRI_CONNTRACK_DEFRAG -400 すべて
raw NF_IP_PRI_RAW -300 すべて
- NF_IP_PRI_SELINUX_FIRST -225 すべて
- NF_IP_PRI_CONNTRACK -200 すべて
mangle NF_IP_PRI_MANGLE -150 すべて
dstnat NF_IP_PRI_NAT_DST -100 prerouting
filter NF_IP_PRI_FILTER 0 すべて
security NF_IP_PRI_SECURITY 50 すべて
srcnat NF_IP_PRI_NAT_SRC 100 postrouting
- NF_IP_PRI_SELINUX_LAST 225 すべて
- NF_IP_PRI_CONNTRACK_HELPER 300 すべて

参考:http://git.netfilter.org/nftables/tree/include/linux/netfilter_ipv4.h
参考:https://manpages.debian.org/testing/nftables/nft.8.en.html

いわゆる「テーブル」ってやつに似てますね。

netfilterのchainのtype

「どのような処理をする」のか、を表現したものが、typeです。
つまり、具体的にパケットをどうするのか、ということを表現しています。

type 利用可能なhook
filter すべて
nat prerouting,postrouting,input,output
route output

あれ?こっちも「テーブル」ってやつに似てますね。
だから紛らわしいんだよ!

参考:https://www.netfilter.org/projects/nftables/manpage.html

firewalldとiptablesとnftables

netfilterを構成するものについては、上で解説しましたが、じゃあnetfilterをどう操作するの?
ということで、それらを操作するソフトウェアの話です。

firewalld ってなんだ?

firewalld が最上位にいて、バックエンドでiptablesもしくはnftablesが動作している。
バックエンドで動作するiptablesもしくはnftablesがnetfilterの操作をしている。

古いバージョンの firewalld ではバックエンドに iptables が、
新しいバージョンの firewalld ではバックエンドに nftables が、採用されている。

とても分かりやすい。
てか、本稿のタイトルはもはやこれ1枚でいいんではなかろうか。

参考:https://firewalld.org/documentation/concepts.html

iptables ってなんだ?

iptablesはnetfilterを操作できるツールだ。
firewalldが導入される前の古いCentOSでは、iptablesをiptables-serviceというものでデーモン化(サービス化?)していた。
つまり、iptablesコマンドでiptablesのルールを直接変更したり、特定のファイルを読み込ませたりすることで、フィルタリングなりNATなりをしていたんだ。

CentOS8にもiptablesはあるが、実体はnftablesで動作している。

# iptables --version
iptables v1.8.2 (nf_tables)

とはいえ、すでに iptables は iptables ではない。

# ll /usr/sbin/iptables
lrwxrwxrwx. 1 root root 17 11月  9 03:40 /usr/sbin/iptables -> xtables-nft-multi

# man xtables-nft
NAME
       xtables-nft ― iptables using nftables kernel api

DESCRIPTION
       xtables-nft  are versions of iptables that use the nftables
       API.  This is a set of tools to help the system administra‐
       tor  migrate  the  ruleset  from iptables(8), ip6tables(8),
       arptables(8), and ebtables(8) to nftables(8).

iptablesを叩くと、それっぽい書式でルールが見える。
けど、iptablesからは、後述するnftablesで追加したテーブルが見えない。
だから、iptables -L -nv -t nat|filter で表示されるルールと実際の動作が噛み合わないことがある。

例えば、Dockerホストからbridge接続されているコンテナへポート転送をするために firewall-cmd --add-masquerade としたとき。nftでchainを確認すると、以下の動作が見える。

# nft list chain ip firewalld nat_POST_public_allow
table ip firewalld {
        chain nat_POST_public_allow {
                oifname != "lo" masquerade
        }
}

けど、iptablesから firewalld というテーブルは見れない。

# iptables -L -t firewalld
iptables v1.8.2 (nf_tables): table 'firewalld' does not exist
Perhaps iptables or your kernel needs to be upgraded.

man iptablesでTABLESの部分をみてみると、iptablesでは、決められたキーワードのテーブルしか確認ができない。

TABLES
       There  are  currently  five  independent tables (which tables are present at any time depends on the kernel configuration options and which modules are
       present).

       -t, --table table
              The tables are as follows:
              filter:
              nat:
              mangle:
              raw:
              security:

つまり、firewalldのバックでnftablesが動作している場合、 iptablesで表示したルールは正しくない!
ので、ルール確認は、 iptablesでなくnftを使う!

nftables ってなんだ?

nftablesもnetfilterを操作できる、iptabesに置き換わるツールだ。
前述のiptablesと同じような感じで、nftablesもサービス化できるみたいだけど、、、

# systemctl status nftables
● nftables.service - Netfilter Tables
   Loaded: loaded (/usr/lib/systemd/system/nftables.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:nft(8)

CentOS8ではfirewalldが動作していて、nftablesが裏で動作しているので、nftablesとしてのデーモンは休んでいるみたい。

その中身は、以下のように、 nft コマンドでルールのflush(=除去)と設定ファイルの読み込みをしている。
iptablesをデーモン化したときと似ているね。

# cat /usr/lib/systemd/system/nftables.service  | grep nft
Documentation=man:nft(8)
ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf
ExecReload=/sbin/nft 'flush ruleset; include "/etc/sysconfig/nftables.conf";'
ExecStop=/sbin/nft flush ruleset

debian/arch/ubuntu/fedora では、nftablesのデーモン動作がデフォルトになっているようだ。

参考:https://wiki.nftables.org/wiki-nftables/index.php/Nftables_from_distributions

基本的なnftablesの使い方

nft

nftablesは nft というコマンドで提供されます。
nftで設定するルールは、処理そのものであるchainと、chainをひとまとめにしたtableとして表現されます。

chainは特定のhookで特定のtypeで表現される処理をまとめたものであり、「(ネットワークスタックの)どの部分」で「どのような順番」で「どのような処理をする」かは、ひとつのchainでまとめられます。
tableは複数のchainをまとめた仮想的なグループとしてとらえます。

設定の確認は nft list というコマンドを使います。

nft list table(s)

設定されているすべてのtableの名称のみ表示します。

# nft list tables
table ip filter
table ip6 filter
(とか、たくさん)

指定したtableの設定されているすべてのchainを表示します。

# nft list table ip nat
table ip nat {
        chain PREROUTING {
                type nat hook prerouting priority -100; policy accept;
                fib daddr type local counter packets 0 bytes 0 jump DOCKER
        }
(table ip natに含まれるchainがすべて表示されます)

nft list chain(s)

設定されているすべてのchainに対して、各chainがどのtableに含まれているか、発動条件である「(ネットワークスタックの)どの部分」で「どのような順番」の処理を行うのか、が表示されます。
(具体的な処理内容は表示されません)

# nft list chains
table ip filter {
        chain INPUT {
                type filter hook input priority 0; policy accept;
        }
        chain FORWARD {
                type filter hook forward priority 0; policy drop;
        }
(たくさん表示されます)

指定したchainを表示します

# nft list chain ip filter FORWARD
table ip filter {
        chain FORWARD {
                type filter hook forward priority 0; policy drop;
                counter packets 0 bytes 0 jump DOCKER-USER
(たくさん表示されます)

すべてのルールの表示

設定されているすべてのルールが表示されます。

# nft list ruleset
table ip filter {
        chain INPUT {
                type filter hook input priority 0; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority 0; policy drop;
                counter packets 0 bytes 0 jump DOCKER-USER

(すごくたくさん表示されます)

さいごに

netfilterはじめ、パケットの取り扱いは奥が深い。
これでもまちがった理解をしているかもしれないし、テキトーに設定してもなんとか動いちゃうこともある。
なるべく正しい知識を取り入れたい。