nftablesによるIPv6フィルタ設定
nftables を使って IPv6 の filter を設定する例。
IPv6 を使っている場合は、IPv4 と IPv6 のデュアルスタックであることが多いと思う。この場合、nftables の inet テーブルを使うことで IPv4 と IPv6 の設定を共通化できる。 inet テーブルを使った IPv4/IPv6 の filter 設定例を以下に示す。
想定ケースとしては単純なWebサービスとメールサービスを提供しているものとする。
inet table の設定例
table inet filter { # (1) inet でテーブルを作成
# (2) setはIPv4/IPv6でそれぞれ作る
set safeip_v4 {
type ipv4_addr
flags interval
elements = {
192.0.2.0/24,
203.0.113.0/24
}
}
set safeip_v6 {
type ipv6_addr
flags interval
elements = {
# (3) IPv6はアドレスが定期的に変わるので/64 prefixで指定
2001:db8:2345:8c00::/64
}
}
set blacklist_v4 {
type ipv4_addr
flags interval
elements = {
xxx.xxx.xxx.0/24
}
}
set blacklist_v6 {
type ipv6_addr
flags interval
elements = {
2001:db8:6d68:bf80::/64
}
}
chain INPUT {
type filter hook input priority filter; policy accept;
# blacklistの設定
# (4) setを参照するルールもip,ip6でそれぞれ指定する必要がある
ip saddr @blacklist_v4 counter packets 0 bytes 0 accept
ip6 saddr @blacklist_v6 counter packets 0 bytes 0 accept
ct state related,established counter packets 0 bytes 0 accept
# ICMPv4/ICMPv6
# (5) ip6では"protocol"はない。"nexthdr"を使う。
ip protocol icmp counter packets 0 bytes 0 accept
ip6 nexthdr icmpv6 counter packets 0 bytes 0 accept
iifname "lo" counter packets 0 bytes 0 accept
# ssh (safeip_xxからのみ接続許可)
ip saddr @safeip_v4 tcp dport 22 ct state new counter packets 0 bytes 0 accept
ip6 saddr @safeip_v6 tcp dport 22 ct state new counter packets 0 bytes 0 accept
# web(IPv4/IPv6)
tcp dport { http, https } ct state new accept
# メール関連の設定例
# smtp
tcp dport { 25 } ct state new counter packets 0 bytes 0 accept
ip saddr @safeip_v4 tcp dport 110 ct state new counter packets 0 bytes 0 accept
ip6 saddr @safeip_v6 tcp dport 110 ct state new counter packets 0 bytes 0 accept
ip saddr @safeip_v4 tcp dport 587 ct state new counter packets 0 bytes 0 accept
ip6 saddr @safeip_v6 tcp dport 587 ct state new counter packets 0 bytes 0 accept
# (6) 最後のrejectルールもIPv4,IPv6でそれぞれ指定する必要がある。
# これを忘れるとIPv6からは全てacceptになってしまうので注意。
counter packets 0 bytes 0 reject with icmp type host-prohibited
counter packets 0 bytes 0 reject with icmpv6 type admin-prohibited
}
chain FORWARD {
type filter hook forward priority filter; policy accept;
counter packets 0 bytes 0 reject
}
chain OUTPUT {
type filter hook output priority filter; policy accept;
}
}
要点は以下のとおり。
(1) table は inet で作成する。
(2) set を使う場合は IPv4/IPv6 でそれぞれ set とルールを作成する必要がある。
この例では会社や家など特定のIPアドレスからのアクセスを許可することを想定した safeip_v4 / safeip_v6 setとDoS攻撃対策などで特定のアクセス元を遮断することを想定した blacklist_v4 / blacklist_v6 set を作成している
(3) IPv6 はアドレスが定期的に変わることがほとんどだと思われるので /64 とかの prefix で指定することになると思われる。場合によっては、set による指定はやめて tailscale 等の VPN ソフトを使うことも検討した方がよいかもしれない。tailscale については後報。
(4) set を参照するルールも IPv4/IPv6 でそれぞれ指定する必要がある。
(5) ICMP は IPv4/IPv6 で設定方法が異なる。IPv4 では "protocol" で指定するが、IPv6 には "protocol" は存在せず "nexthdr" を使って指定する必要がある。
(6) chain のデフォルトポリシーを accept にして最後のルールで reject する方式で filter を記述している場合は、最後の reject ルールも IPv4/IPv6 でそれぞれ指定する必要がある。
counter packets 0 bytes 0 reject with icmpv6 type admin-prohibited を忘れず設定すること。これを忘れると、IPv6 の通信が全て accept になってしまうので注意が必要だ。過去に受けたお問い合わせのなかで、これを忘れて IPv6 通信が全て accept 状態になっている事例が実際にあった。
inet テーブルを使えば IPv4/IPv6 の区別なく filter 設定できるかと思いきや、それなりに個別の設定が必要となることがわかるだろう。個人的にはどれが IPv4/IPv6 共通で記述できて、どれが個別に記述する必要があるのか判断する必要があり却ってわかり辛くなっているように思える。
IPv6 の filter を設定しようとする際、既に IPv4 の filter 設定はされていることがほとんどだと思う。この場合、無理に inet テーブルで filter を再作成しようとせず ip6 テーブルを追加する形で設定するのがいいだろう。 ip, ip6 テーブルを両方管理する必要があるが、filter ルールは一度作成してしまえば後は set のメンテナンスくらいで済むことが多いので、それならばテーブルが分かれていてもたいした手間にはならない。
他にも ip, ip6 テーブルで個別に設定した方がよい理由がある。 firewall(nftables) を使うアプリケーションが inet テーブルに対応していないケースがあるためだ。
tailscale などがこれにあたる。
tailscale はサービス起動時に nftables の既存のルールに優先して tailscale のルールを追加しようとする。ただし、tailscale は現状 inet テーブルは認識せず、ip, ip6 テーブルにしかルールを追加できないようだ。
inet テーブルで filter 設定していた場合、tailscale は既存の filter 設定が存在しないとみなして ip テーブルを新規作成してそこにルールを追加する。 このため、本来既存 filter ルールに優先して処理させたい tailscale の filter ルールが、既存ルールより後に処理されるため、tailscale が正常に動作できない(tailnet からの新規接続を受け付けられない)。
せっかく inet テーブルというものがあるのでこれを使ってスマートに filter ルールを記載してみたいと考えるかもしれないが、このことからも現状は ip, ip6 テーブルでそれぞれ filter 設定するのがよいと考える。
最後に ip6 テーブルによる filter 設定の例を示す。
ip6 table の設定例
table ip filter {
...
ip filterの方は省略
}
table ip6 filter {
set safeip_v6 {
type ipv6_addr
flags interval
elements = {
2001:db8:2345:8c00::/64
}
}
set blacklist_v6 {
type ipv6_addr
flags interval
elements = {
2001:db8:6d68:bf80::/64
}
}
chain INPUT {
type filter hook input priority filter; policy accept;
# blacklistの設定
ip6 saddr @blacklist_v6 counter packets 0 bytes 0 accept
ct state related,established counter packets 0 bytes 0 accept
# ICMPv4/ICMPv6
ip6 nexthdr icmpv6 counter packets 0 bytes 0 accept
iifname "lo" counter packets 0 bytes 0 accept
# ssh (safeip_xxからのみ接続許可)
ip6 saddr @safeip_v6 tcp dport 22 ct state new counter packets 0 bytes 0 accept
# web(IPv4/IPv6)
tcp dport { http, https } ct state new accept
# メール関連の設定例
# smtp
tcp dport { 25 } ct state new counter packets 0 bytes 0 accept
ip6 saddr @safeip_v6 tcp dport 110 ct state new counter packets 0 bytes 0 accept
ip6 saddr @safeip_v6 tcp dport 587 ct state new counter packets 0 bytes 0 accept
counter packets 0 bytes 0 reject with icmpv6 type admin-prohibited
}
chain FORWARD {
type filter hook forward priority filter; policy accept;
counter packets 0 bytes 0 reject
}
chain OUTPUT {
type filter hook output priority filter; policy accept;
}
}
まとめ
- inet テーブルによる filter 設定は思ったより面倒くさい
- firewall を使うアプリケーションが inet テーブルに対応していないケースがある
ということで、ip, ip6 テーブルでそれぞれ filter 設定を書く方が無難。
投稿日:2026/09/22 23:37