netfilterとfirewalldとiptablesとnftablesの関係

# はじめに

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


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


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

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

[netfilter.org](https://netfilter.org/index.html)によると、

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

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

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


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

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


基本は以下の図です。

[![Screenshot from Gyazo](https://gyazo.com/bffd295f4b35fd99ecca52b61932610c/raw)](https://gyazo.com/bffd295f4b35fd99ecca52b61932610c)

参考：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が以下の２通りになります。

ローカルあての場合
*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 が、採用されている。

[![Screenshot from Gyazo](https://gyazo.com/9e8e077e1d81049918bfbf70e2cc982e/raw)](https://gyazo.com/9e8e077e1d81049918bfbf70e2cc982e)

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

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