こんにちは、ぐっちゃんです。
前回は、firewall-cmdコマンドで設定したら、その内容が nftables のルールに反映されました。
iptables の場合、パケットを受信する際は nat テーブルの PREROUTING チェーン → filterテーブルの INPUT チェーンの順番でしたが、RHEL8以降のnftables だとどうなのだろう?
そこで今回は nftables のパケット処理順について調べてみました!
環境
OS:MIRACLE LINUX 9.0
カーネル:5.14.0-70.13.1.el9_0.x86_64
nftablesのバージョン:nftables-0.9.8-12.el9.x86_64
まとめ
- PREROUTING では、mangle → nat → filter の順で処理される
- INPUT では、ゾーンに設定するルールよりもダイレクトルールが先に処理される
検証方法
以下のようにポート80番にアクセスがあったらログを出すように設定し、他ホストからcurlコマンドを叩いて出力されたメッセージから処理の流れを特定していきます。
tcp dport 80 log prefix “direct INPUT ” level info
PREROUTINGの場合
受信したパケットのルーティング決定前に適用される PREROUTING の流れを見ていきます。
[root@localhost ~]# nft list ruleset > nftrule
■ログ出力ルールを記載する
[root@localhost ~]# vi nftrule
[root@localhost ~]# less nftrule
flush ruleset
table ip filter {
:
chain INPUT {
type filter hook input priority filter; policy accept;
tcp dport 80 log prefix “direct INPUT ” level info
}
}
table inet firewalld {
chain mangle_PREROUTING {
type filter hook prerouting priority mangle + 10; policy accept;
tcp dport 80 log prefix “mangle_PREROUTING ” level info ★
jump mangle_PREROUTING_ZONES
}
:
chain mangle_PREROUTING_ZONES {
tcp dport 80 log prefix “mangle_PREROUTING_ZONES ” level info ★
iifname “enp0s3” goto mangle_PRE_public
goto mangle_PRE_public
}
:
chain nat_PREROUTING {
tcp dport 80 log prefix “nat_PREROUTING ” level info ★
type nat hook prerouting priority dstnat + 10; policy accept;
jump nat_PREROUTING_ZONES
}
:
chain filter_PREROUTING {
type filter hook prerouting priority filter + 10; policy accept;
tcp dport 80 log prefix “filter_PREROUTING ” level info ★
icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
meta nfproto ipv6 fib saddr . mark . iif oif missing drop
}
}
■ルールを反映する
[root@localhost ~]# nft -f nftrule
mangle や nat、filter にログ出力ルールをセットし、以下のようにクライアントから curlで接続してみます。
■クラアントから接続
C:\Users\Test>curl http://192.168.56.108
I am 192.168.78.56.108
■サーバのmessagesに出力されたログを見てみる
[root@localhost ~]# tail -f /var/log/messages
:
Sep 18 13:31:39 localhost kernel: mangle_PREROUTING IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: mangle_PREROUTING_ZONES IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: nat_PREROUTING IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: filter_PREROUTING IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: direct INPUT IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: filter_INPUT IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: filter_INPUT_ZONES IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: filter_IN_public IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: filter_IN_public_allow IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … SYN …
Sep 18 13:31:39 localhost kernel: mangle_PREROUTING IN=enp0s3 … PROTO=TCP SPT=55868 DPT=80 … ACK …
出力されたログの中で SYN というフラッグの付いたログに着目すると、mangle → nat → filter のPREROUTINGで処理されてから INPUT へパケットが流れることがわかりました。
INPUTの場合
PREROUTING の時と同様に、INPUT に関係しそうなチェーンに、ログを出力させるルールを追加してみます。
[root@localhost ~]# vi nftrule
[root@localhost ~]# less nftrule
flush ruleset ★
table ip filter {
:
chain INPUT {
type filter hook input priority filter; policy accept;
tcp dport 80 log prefix “direct chain ” level info ★
}
}
table inet firewalld {
:
chain filter_INPUT {
type filter hook input priority filter + 10; policy accept;
tcp dport 80 log prefix “filter_INPUT ” level info ★
ct state { established, related } accept
ct status dnat accept
iifname “lo” accept
jump filter_INPUT_ZONES
:
}
chain filter_INPUT_ZONES {
tcp dport 80 log prefix “filter_INPUT_ZONES ” level info ★
iifname “enp0s3” goto filter_IN_public
goto filter_IN_public
}
chain filter_IN_public {
tcp dport 80 log prefix “filter_IN_public ” level info ★
:
jump filter_IN_public_deny
jump filter_IN_public_allow
:
}
chain filter_IN_public_allow {
tcp dport 80 log prefix “filter_IN_public_allow ” level info ★
tcp dport 22 ct state { new, untracked } accept
ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept
tcp dport 9090 ct state { new, untracked } accept
tcp dport 80 ct state { new, untracked } accept
}
:
}
では、INPUT のチェーンの処理順を見てみましょう。
■クラアントから接続
C:\Users\Test>curl http://192.168.56.108
I am 192.168.78.56.108
■サーバのmessagesに出力されたログを見てみる
[root@localhost ~]# tail -f /var/log/messages
:
Sep 18 09:27:13 localhost kernel: direct chain IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … SYN …
Sep 18 09:27:13 localhost kernel: filter_INPUT IN=enp0s3 OUT= PROTO=TCP SPT=53301 DPT=80 … SYN …
Sep 18 09:27:13 localhost kernel: filter_INPUT_ZONES IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … SYN …
Sep 18 09:27:13 localhost kernel: filter_IN_public IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … SYN …
Sep 18 09:27:13 localhost kernel: filter_IN_public_allow IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … SYN …
Sep 18 09:27:13 localhost kernel: direct chane IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … ACK …
Sep 18 09:27:13 localhost kernel: filter_INPUT IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … ACK …
Sep 18 09:27:13 localhost kernel: direct chane IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … ACK PSH …
Sep 18 09:27:13 localhost kernel: filter_INPUT IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … ACK PSH …
Sep 18 09:27:13 localhost kernel: direct chane IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … ACK FIN …
Sep 18 09:27:13 localhost kernel: filter_INPUT IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … ACK FIN …
Sep 18 09:27:13 localhost kernel: direct chane IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … ACK …
Sep 18 09:27:13 localhost kernel: filter_INPUT IN=enp0s3 OUT=… PROTO=TCP SPT=53301 DPT=80 … ACK …
ログからは、最初は”direct chain”に入り、その後 firewalld テーブル内の “filter_INPUT”チェーンに行き、”filter_INPUT_ZONES”、”filter_IN_public”…と順に処理されることがわかります。
ということは、ゾーンに設定するルールよりもダイレクトルールが先に処理されるということがわかりました。
コメント