サーバーのパケットフィルタリングも、そろそろ古式ゆかしい iptables から新しい(とはいっても、Linux Kernel 3.13 から使えるようにはなっていたんで、登場して既に8年経ってるのですが) nftables に置き換えるか、ということで試行錯誤を繰り返して何とか設定に成功。とはいえ、先行事例のコピペみたいなものなので、ちょっとは勉強しないとなあと反省した次第。

Debian では、buster からデフォルトになってたんだけど、iptables で別に困ってないからいいや、と放っておいたんです。導入事例が溜まるのを待ってからでも遅くないし、と少々小狡いことも考えてたりした。

一応、謳い文句では IPv4 と IPv6 では別々に設定しなくてはならなかったルールが nftables では統一的に扱えるとの触れ込みだったし、実際 man ページにもそう書かれてるんだけど、試しに設定してみたらなぜか IPv6 のフィルタリングだけ想定したように動かない。というか、全パケット破棄されてしまう。

table inet filter {

        chain input {
                type filter hook input priority filter; policy accept;
                counter packets 0 bytes 0 jump BASE-FILTER
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
                counter packets 0 bytes 0 jump BASE-FILTER
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }

        chain BASE-FILTER {
                ## local interface
                iif "lo" accept

                ## Home
                ip  addr   118.118.118.118 counter accept
                ip6 saddr [2406:0::]/64 counter accept

                 ## For Ping
                meta l4proto icmp counter accept
                meta l4proto ipv6-icmp counter accept

                ## Drop syn packet without new state
                meta l4proto tcp tcp flags & (fin | syn | rst | ack) != syn ct state new counter drop

                ## Accept established connection
                ct state established,related counter accept

                ## HTTP
                ct state new tcp dport 80 counter accept

                ## HTTPS
                ct state new tcp dport 443 counter accept
                meta l4proto udp udp dport 443 counter accept

                ## Drop others
                counter packets 0 bytes 0 drop
        }
}

上記のようなルールセットを書いて読み込ませてみたら、IPv6 で接続していた ssh セッションが見事フリーズ、というか遮断されてにっちもさっちもいかない。あれこれ試しても、IPv4 は疎通するけど、IPv6 が通らない。結局、仕方ないので、ip フィルターと ip6 フィルターで分けたら無事意図した通りに動いた。

table ip filter {

        chain input {
                type filter hook input priority filter; policy accept;
                counter packets 0 bytes 0 jump BASE-FILTER
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
                counter packets 0 bytes 0 jump BASE-FILTER
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }

        chain BASE-FILTER {
                ## local interface
                iif "lo" accept

                ## Home
                ip  addr   118.118.118.118 counter accept

                ## For Ping
                meta l4proto icmp counter accept

                ## Drop syn packet without new state
                meta l4proto tcp tcp flags & (fin | syn | rst | ack) != syn ct state new counter drop

                ## Accept established connection
                ct state established,related counter accept

                ## HTTP
                ct state new tcp dport 80 counter accept

                ## HTTPS
                ct state new tcp dport 443 counter accept
                meta l4proto udp udp dport 443 counter accept
 
                ## Drop others
                counter drop
        }
}

table ip6 filter {

        chain input {
                type filter hook input priority filter; policy accept;
                counter packets 0 bytes 0 jump BASE-FILTER
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
                counter packets 0 bytes 0 jump BASE-FILTER
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }

        chain BASE-FILTER {
                ## local interface
                iif "lo" accept

                ## Home
                ip6 saddr [2406:0::]/64 counter accept

                ## For Ping
                meta l4proto ipv6-icmp counter accept

                ## Drop syn packet without new state
                meta l4proto tcp tcp flags & (fin | syn | rst | ack) != syn ct state new counter drop

                ## Accept established connection
                ct state established,related counter accept

                ## HTTP
                ct state new tcp dport 80 counter accept

                ## HTTPS
                ct state new tcp dport 443 counter accept
                meta l4proto udp udp dport 443 counter accept

                ## Drop others
                counter drop
        }
}

何か仕様を誤解してるんだと思うんだけど、登場から8年も経ってるのに、致命的なくらい導入事例がネットにない。みんな、StackOverflow とか、誰かのブログの焼き直し設定事例ばかりでなんと、IPv6 関連の設定例が見つからないときた。これなら、iptables とあんまし変わらないなあとしばし絶望。

まあ、動くことは動くので、切り替えちゃったんですけどね。