コンテンツにスキップ

nftables

提供: ArchWiki
2026年5月27日 (水) 13:11時点におけるKusanaginoturugi (トーク | 投稿記録)による版 (サンプル: 記事を更新)

関連記事

nftables は既存の ip-, ip6-, arp-, ebtables フレームワークを置き換える Netfilter のプロジェクトです。新しいパケットフィルタリングフレームワーク、新しいユーザースペースユーティリティ (nft)、そして ip- と ip6tables の互換レイヤーを提供します。現行のフック、接続追跡システム、ユーザースペースのキューイングコンポーネント、そして netfilter のログサブシステムを使っています。

nftables は3つのメインコンポーネントから構成されています: カーネルの実装、libnl netlink communication そして nftables ユーザースペースフロントエンド。カーネルは netlink の設定インターフェイスだけでなく、小さなクラス言語インタプリタを使用するランタイムのルールセットの評価も提供します。libnl にはカーネルと通信するためのローレベルな関数が含まれています。nftables フロントエンドはユーザーが対話するものです。

nftables の公式 wiki には詳しい情報が載っています。

インストール

ユーザースペースユーティリティパッケージ nftablesインストールしてください。

iptables-legacy がインストールされている場合は、iptables をインストールしてください。これにより iptables-legacy が自動的にアンインストールされ、nftables との競合を防ぎます。

ノート iptables パッケージは、実際には nftables ルールを作成し操作する iptables コマンドの実装を提供します。ただし、古い iptables-legacy ツールで作成されたルールは別のオブジェクトであり、それらが存在する場合、iptables は警告を表示します。

フロントエンド

ヒント ほとんどの iptables フロントエンドは nftables を直接または間接的にサポートしていませんが、将来的に対応する可能性があります。[1] nftables と iptables の両方をサポートするグラフィカルフロントエンドの 1 つは firewalld です。[2] ufw は互換レイヤー iptables-nft を介してサポートされています。[3]
  • firewalld (firewall-cmd) — ネットワークとファイアウォールゾーンの設定、およびファイアウォールルールの設定と構成を行うデーモンおよびコンソールインターフェイス。
https://firewalld.org/ || firewalld
  • nft-blackhole — 国別およびブラックリストによって nftables で IP をブロックするスクリプト / デーモン。
https://github.com/tomasz-c/nft-blackhole || nft-blackholeAUR
  • ufw — Ufw は Uncomplicated Firewall の略で、netfilter ファイアウォールを管理するためのプログラムです。
https://help.ubuntu.com/community/UFW || ufw
  • reaction — プログラムの出力から繰り返し出現するパターンをスキャンし、アクションを実行するデーモン。fail2ban の軽量な代替です。
https://framagit.org/ppom/reaction || reactionAUR

使用方法

ヒント すでに iptables ルールがある場合は、その iptables ルールを nftables ルールに変換できます。詳しくは [4] を参照してください。

nftables は、コマンドラインで作成された一時的なルールと、ファイルから読み込まれた、またはファイルに保存された永続的なルールを区別しません

すべてのルールは nft コマンドラインユーティリティを使って作成または読み込む必要があります。

使用方法については #設定 セクションを参照してください。

現在の ruleset は次のコマンドで表示できます:

# nft list ruleset

すべての ruleset を削除し、システムにファイアウォールがない状態にします:

# nft flush ruleset

nftables.service再起動することで、/etc/nftables.conf から ruleset を読み込みます。

シンプルなファイアウォール

nftables には、/etc/nftables.conf ファイルに保存されたシンプルで安全なファイアウォール設定が付属しています。

nftables.service は、起動/有効化されたときに、そのファイルからルールを読み込みます。

設定

nftables ユーザースペースユーティリティ nft は、ruleset をカーネルに渡す前に、ルールセット評価の大部分を実行します。ルールはチェインに格納され、チェインはテーブルに格納されます。以下のセクションでは、これらの構造を作成および変更する方法を示します。

ファイルから入力を読み込むには、-f/--file オプションを使用します:

# nft --file filename

既に読み込まれているルールは自動的にはフラッシュされないことに注意してください。

すべてのコマンドの完全な一覧については nft(8) を参照してください。

テーブル

テーブルはチェインを保持します。iptables のテーブルと違って、nftables には初めから組み込まれているテーブルはありません。テーブルの数や名前はユーザーが自由に決めることができますが、各テーブルにはアドレスファミリーをひとつしか保持することができません。5つのファミリーのうち指定したファミリーのパケットにだけ適用されます:

nftables ファミリー iptables ユーティリティ
ip iptables
ip6 ip6tables
inet iptables と ip6tables
arp arptables
bridge ebtables

ip (IPv4) がデフォルトのファミリーです。特に指定がなければ ip が使われます。

IPv4 と IPv6 の両方に適用されるルールを作成するには inet を使います。inet を使うには Linux 3.15 以上が必要で、ipip6 ファミリーを統一してルールを簡単に定義できます。

ノート inetnat タイプのチェインでは使えず、filter タイプのチェインを使う必要があります (ソース)。

アドレスフファミリーの完全な定義は nft(8)ADDRESS FAMILIES セクションを参照してください。

以下で例示しているコマンドの family は全て任意であり、指定しなかった場合は ip が使われます。

テーブルの作成

以下のコマンドで新しいテーブルが追加されます:

# nft add table family table

テーブルの一覧表示

全てのテーブルを表示するには:

# nft list tables

テーブル内のチェインとルールの表示

指定したテーブルの全てのチェインとルールを表示するには:

# nft list table family_type table_name

例えば、inet family の my_table テーブルのすべてのルールを一覧表示するには:

# nft list table inet my_table

テーブルの削除

テーブルを削除するには:

# nft delete table family_type table_name

これにより、テーブル内のすべてのチェインが破棄されます。

テーブルのクリア

テーブルから全てのルールを消去するには:

# nft flush table family_type table_name

チェイン

チェインの用途はルールを保持することです。iptables のチェインと違って、nftables には初めから組み込まれているチェインはありません。そのためチェインが netfilter フレームワークにあるタイプやフックをどれも使わない場合、iptables とは異なりチェインを通り抜けるパケットは nftables の影響を受けません。

チェインには2つのタイプがあります。base チェインはネットワークスタックからのパケットのエントリポイントとなります。フックの値を指定することができます。regular チェインはジャンプターゲットとして使用することができます。

以下のコマンドで使っている family_type は全て任意であり、指定しなかった場合は ip が使われます。

チェインの作成

Base chain

base chain を追加するには、type、hook、priority の値を指定する必要があります:

# nft add chain family_type table_name chain_name '{ type chain_type hook hook_type priority priority_value ; policy policy ;}'

chain_type には filterroutenat を指定できます。

IPv4/IPv6/Inet address family では、hook_typepreroutinginputforwardoutputpostrouting を指定できます。サポートされる family_typechain_typehook_type の組み合わせ一覧については nft(8) § CHAINS を参照してください。

priority_value には priority 名または整数値を指定できます。標準 priority 名と値の一覧については nft(8) § CHAINS を参照してください。数値が小さいチェインほど先に処理され、負の値も使用できます。[5]

任意で、base chain には policy (drop またはデフォルトの accept) を指定できます。これにより、チェイン内のルールで明示的に accept または refuse されなかったパケットに何が起こるかを定義します。

例えば、入力パケットをフィルタリングする base chain を追加するには:

# nft add chain inet my_table my_chain '{ type filter hook input priority 0; }'

上記のいずれでも addcreate に置き換えると、新しいチェインを追加しますが、チェインが既に存在する場合はエラーを返します。

Regular chain

次のコマンドは、table_name という名前のテーブルに chain_name という名前の regular chain を追加します:

# nft add chain family_type table_name chain_name

例えば、inet address family の my_table テーブルに my_tcp_chain という regular chain を追加するには:

# nft add chain inet my_table my_tcp_chain

チェインの一覧表示

次のコマンドは、family_type のすべてのチェインを、ルールなしで一覧表示します (#ルールの一覧表示 を参照):

# nft list chains family_type

例えば、次のコマンドは IPv6 のチェインを一覧表示します:

# nft list chains ip6

family_type を省略した場合、すべてのチェインが表示されます。

チェインの編集

チェインを編集したいときは、チェインの名前を指定して変更したいルールを定義します:

# nft chain family_type table_name chain_name '{ [ type chain_type hook hook_type device device_name priority priority_value ; policy policy_type ; ] }'

例えば、デフォルトテーブル内の my_input チェインの policy を accept から drop に変更するには:

# nft chain inet my_table my_input '{ policy drop ; }'

チェインの削除

チェインを削除するには:

# nft delete chain family_type table_name chain_name

削除するチェインにはルールやジャンプターゲットが含まれていてはいけません。

チェインのルールを消去

チェインからルールを消去するには:

# nft flush chain family_type table_name chain_name

ルール

ルールは表現または宣言から構成され、チェインの中に格納されます。

ルールの追加

ヒント iptables-translate ユーティリティを使うことで iptables のルールを nftables フォーマットに変換できます。

チェインにルールを追加するには:

# nft add rule family_type table_name chain_name handle handle_value statement

ルールは handle_value の位置に追加されます。これは任意です。指定しない場合、ルールはチェインの末尾に追加されます。

ルール handle を確認するには、任意の有効な list コマンドに --handle スイッチを追加する必要があります。このスイッチにより、nft は出力に handle を表示します。--numeric 引数は、未解決の IP アドレスなど、一部の数値出力を確認するのに便利です。

# nft --handle --numeric list chain inet my_table my_input
table inet my_table {
     chain input {
          type filter hook input priority 0;
          ip saddr 127.0.0.1 accept # handle 10
     }
}

指定位置の前にルールを挿入するには:

# nft insert rule family_type table_name chain_name handle handle_value statement

handle_value が指定されていない場合、ルールはチェインの先頭に挿入されます。

表現

statement にはマッチする表現と判断宣言が入ります。判断宣言には accept, drop, queue, continue, return, jump chain, goto chain などが存在します。判断宣言以外の宣言も指定できます。詳しくは nft(8) を参照してください。

nftables では様々な表現を使うことができ、ほとんどは、iptables と対応するようになっています。一番大きな違いは汎用的なマッチと暗黙的なマッチが存在しないことです。汎用的なマッチとは、--protocol--source のように、いつでも使うことができるマッチで、暗黙的なマッチとは、--sport のように、特定のプロトコルでしか使えないマッチのことです。

以下は利用できるマッチの一部です:

  • meta (メタプロパティ。例: インターフェイス)
  • icmp (ICMP プロトコル)
  • icmpv6 (ICMPv6 プロトコル)
  • ip (IP プロトコル)
  • ip6 (IPv6 プロトコル)
  • tcp (TCP プロトコル)
  • udp (UDP プロトコル)
  • sctp (SCTP プロトコル)
  • ct (接続のトラッキング)

以下はマッチ引数の一部です (完全なリストは nft(8) を見て下さい):

meta:
  oif <output interface INDEX>
  iif <input interface INDEX>
  oifname <output interface NAME>
  iifname <input interface NAME>

  (oif and iif accept string arguments and are converted to interface indexes)
  (oifname and iifname are more dynamic, but slower because of string matching)

icmp:
  type <icmp type>

icmpv6:
  type <icmpv6 type>

ip:
  protocol <protocol>
  daddr <destination address>
  saddr <source address>

ip6:
  daddr <destination address>
  saddr <source address>

tcp:
  dport <destination port>
  sport <source port>

udp:
  dport <destination port>
  sport <source port>

sctp:
  dport <destination port>
  sport <source port>

ct:
  state <new | established | related | invalid>

ある意味では、iif と oif に対する iifname と oifname の違いは、static と dynamic の違いに似ています。または、プログラミング概念でいう definition と declaration、あるいは early binding と delayed binding に似ています。使用例と追加説明へのリンクについては [6] を参照してください。

ルールの一覧表示

次のコマンドはチェイン内のすべてのルールを一覧表示します:

# nft list chain family_type table_name chain_name

例えば、my_table という inet テーブル内の my_output というチェインのルールを一覧表示するには:

# nft list chain inet my_table my_output

削除

個々のルールはハンドルを使わないと削除することができません。nft --handle list コマンドを使うことでルールのハンドルを確認できます。例えば次のような場合:

# nft --handle --numeric list chain inet my_table my_input
table inet my_table {
     chain input {
          type filter hook input priority 0;
          ip saddr 127.0.0.1 accept # handle 10
     }
}

次のコマンドで削除できます:

# nft delete rule inet my_table my_input handle 10

テーブル内のすべてのチェインは nft flush table コマンドでフラッシュできます。個別のチェインは nft flush chain または nft delete rule コマンドでフラッシュできます。

# nft flush table table_name
# nft flush chain family_type table_name chain_name
# nft delete rule family_type table_name chain_name

最初のコマンドは、ip table_name テーブル内のすべてのチェインをフラッシュします。2 番目のコマンドは、family_type table_name テーブル内の chain_name チェインをフラッシュします。3 番目のコマンドは、family_type table_name テーブル内の chain_name チェインのすべてのルールを削除します。

セット

セットには名前付きセットと匿名セットがあります。セットは 1 つ以上の要素で構成され、要素はカンマで区切られ、波括弧で囲まれます。匿名セットはルールに埋め込まれ、更新できません。ルールを削除して再追加する必要があります。例えば、次の dports セットから "http" だけを削除することはできません:

# nft add rule ip6 filter input tcp dport {telnet, http, https} accept

名前付きセットは更新でき、型付けやフラグ付けが可能です。sshguard は、ブロックされたホストの IP アドレスに名前付きセットを使用します。

table ip sshguard {
       set attackers {
               type ipv4_addr
               flags interval
               elements = { 1.2.3.4 }
       }

セットに要素を追加または削除するには、次のようにします:

# nft add element ip sshguard attackers { 5.6.7.8/32 }
# nft delete element ip sshguard attackers { 1.2.3.4/32 }

ipv4_addr 型には CIDR netmask を含められることに注意してください (ここでの /32 は必須ではありませんが、完全性のために含めています)。また、ここで TABLE ip sshguard { SET attackers } によって定義されたセットは、ip sshguard attackers として参照されることにも注意してください。

ヒント systemd-networkd 接続は、事前定義された名前付きセットにホスト IP アドレス、ネットワークプレフィックス、インターフェイスインデックスを投入するように設定できます。詳しくは systemd.network(5) § [ADDRESS SECTION OPTIONS]NFTSet= の説明と #systemd-networkd を使った動的名前付きセット の例を参照してください。

アトミックリロード

現在のルールセットをフラッシュする:

# echo "flush ruleset" > /tmp/nftables

現在のルールセットをダンプする:

# nft list ruleset >> /tmp/nftables

/tmp/nftables を編集して次のコマンドで変更を適用:

# nft -f /tmp/nftables

サンプル

ワークステーション

/etc/nftables.conf
flush ruleset

table inet my_table {
	set LANv4 {
		type ipv4_addr
		flags interval

		elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
	}
	set LANv6 {
		type ipv6_addr
		flags interval

		elements = { fd00::/8, fe80::/10 }
	}

	chain my_input_lan {
		udp sport 1900 udp dport >= 1024 meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"

		udp sport netbios-ns udp dport >= 1024 meta pkttype unicast accept comment "Accept Samba Workgroup browsing replies"

	}

	chain my_input {
		type filter hook input priority filter; policy drop;

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"
		fib daddr . iif type != { local, broadcast, multicast } drop comment "Drop packets if the destination IP address is not configured on the incoming interface (strong host model)"
		ct state { established, related } accept comment "Accept traffic originated from us"

		meta l4proto { icmp, ipv6-icmp } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
		udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

		ip6 saddr @LANv6 jump my_input_lan comment "Connections from private IP address ranges"
		ip saddr @LANv4 jump my_input_lan comment "Connections from private IP address ranges"

		counter comment "Count any other traffic"
	}

	chain my_forward {
		type filter hook forward priority filter; policy drop;
		# Drop everything forwarded to us. We do not forward. That is routers job.
	}

	chain my_output {
		type filter hook output priority filter; policy accept;
		# Accept every outbound connection
	}

}
ヒント systemd-networkd を使用していてローカルネットワークに接続している場合、systemd.network(5) オプション NFTSet= を使って接続のネットワークプレフィックスを取得することで、ネットワークサブネットのハードコーディングを避けられます。#systemd-networkd を使った動的名前付きセット を参照してください。

サーバー

/etc/nftables.conf
flush ruleset

table inet my_table {
	set LANv4 {
		type ipv4_addr
		flags interval

		elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
	}
	set LANv6 {
		type ipv6_addr
		flags interval

		elements = { fd00::/8, fe80::/10 }
	}

	chain my_input_lan {
		meta l4proto { tcp, udp } th dport 2049 accept comment "Accept NFS"

		udp dport netbios-ns accept comment "Accept NetBIOS Name Service (nmbd)"
		udp dport netbios-dgm accept comment "Accept NetBIOS Datagram Service (nmbd)"
		tcp dport netbios-ssn accept comment "Accept NetBIOS Session Service (smbd)"
		tcp dport microsoft-ds accept comment "Accept Microsoft Directory Service (smbd)"

		udp sport { bootpc, 4011 } udp dport { bootps, 4011 } accept comment "Accept PXE"
		udp dport tftp accept comment "Accept TFTP"
	}

	chain my_input {
		type filter hook input priority filter; policy drop;

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"
		fib daddr . iif type != { local, broadcast, multicast } drop comment "Drop packets if the destination IP address is not configured on the incoming interface (strong host model)"
		ct state { established, related } accept comment "Accept traffic originated from us"

		meta l4proto { icmp, ipv6-icmp } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
		udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

		ip6 saddr @LANv6 jump my_input_lan comment "Connections from private IP address ranges"
		ip saddr @LANv4 jump my_input_lan comment "Connections from private IP address ranges"

		tcp dport ssh accept comment "Accept SSH on port 22"

		tcp dport ipp accept comment "Accept IPP/IPPS on port 631"

		meta l4proto { tcp, udp } th dport { http, https, 8008, 8080 } accept comment "Accept HTTP (ports 80, 443, 8008, 8080)"

		udp sport bootpc udp dport bootps ip saddr 0.0.0.0 ip daddr 255.255.255.255 accept comment "Accept DHCPDISCOVER (for DHCP-Proxy)"
	}

	chain my_forward {
		type filter hook forward priority filter; policy drop;
		# Drop everything forwarded to us. We do not forward. That is routers job.
	}

	chain my_output {
		type filter hook output priority filter; policy accept;
		# Accept every outbound connection
	}

}

レート制限

table inet my_table {
	chain my_input {
		type filter hook input priority filter; policy drop;

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"
		fib daddr . iif type != { local, broadcast, multicast } drop comment "Drop packets if the destination IP address is not configured on the incoming interface (strong host model)"

		meta l4proto icmp icmp type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"
		meta l4proto ipv6-icmp icmpv6 type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"

		ct state { established, related } accept comment "Accept traffic originated from us"

		meta l4proto { icmp, ipv6-icmp } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		tcp dport ssh ct state new limit rate 15/minute accept comment "Avoid brute force on SSH"

	}

}

ジャンプ

設定ファイルでジャンプを使うときは、先にターゲットチェインを定義する必要があります。そうしないと Error: Could not process rule: No such file or directory というエラーが発生します。

table inet my_table {
    chain web {
        tcp dport http accept
        tcp dport 8080 accept
    }
    chain my_input {
        type filter hook input priority filter;
        ip saddr 10.0.2.0/24 jump web
        drop
    }
}

インターフェイスによってルールを変える

複数のネットワークインターフェイスが存在する場合、それぞれのインターフェイスごとに別々のフィルターチェインを設定したい場合があるかもしれません。例えば、ホームルーターを構築するとき、LAN 上でアクセスできるウェブサーバーを実行しつつ (nsp3s0 インターフェイス)、インターネットからはアクセスできないようにしたい場合 (enp2s0 インターフェイス) などは以下のように設定します:

table inet my_table {
  chain my_input { # this chain serves as a dispatcher
    type filter hook input priority filter; policy drop;

    iif lo accept comment "always accept loopback"
    iifname enp2s0 jump my_input_public
    iifname enp3s0 jump my_input_private
  }
  chain my_input_public { # rules applicable to public interface interface
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
  }
  chain my_input_private {
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
    tcp port http accept
    tcp port https accept
    reject with icmpx port-unreachable comment "all other traffic"
  }
  chain my_output { # we let everything out
    type filter hook output priority filter;
    accept
  }
}

あるいは、単一の upstream interface に対する iifname statement だけを選び、それ以外のすべてのインターフェイスに対するデフォルトルールを 1 か所に置くこともできます。各インターフェイスごとに dispatch する必要はありません。

マスカレード

nftables には特殊なキーワード masquerade が存在し、送信元アドレスが自動的に出力インターフェイスのアドレスに設定されます (ソース)。ルーターのインターフェイスが多数の ISP に接続されているときなど、インターフェイスの IP アドレスが一定でない場合に有用です。通常は、インターフェイスの IP アドレスが変わるたびにネットワークアドレス変換 (NAT) のルールを更新する必要があります。

masquerade を使用するには:


  • masquerading がカーネルで有効になっていることを確認してください (デフォルトカーネルを使用している場合は true です)。そうでない場合は、カーネル設定で CONFIG_NFT_MASQ=m を設定します。
  • masquerade キーワードは nat type のチェインでのみ使用できます。
  • masquerading は source NAT の一種であるため、output path でのみ動作します。

2 つのインターフェイスを持つマシンの例です: LAN は enp3s0 に接続され、public internet は enp2s0 に接続されています:

table inet my_nat {
  chain my_masquerade {
    type nat hook postrouting priority srcnat;
    oifname "enp2s0" masquerade
  }
}

テーブル type が inet なので、IPv4 と IPv6 の両方のパケットが masquerade されます。IPv6 は追加の address space により NAT が不要なため、IPv4 パケットだけを masquerade したい場合は、meta nfproto ipv4 expression を oifname "enp2s0" の前に使用するか、テーブル type を ip に変更します。

NAT とポートフォワーディング

この例では、eth0 という WAN インターフェイスを通って出ていくトラフィックを masquerade し、ポート 22 と 80 を 10.0.0.2 に転送します。sysctl によって net.ipv4.ip_forward1 に設定する必要があります。

table nat {
    chain prerouting {
        type nat hook prerouting priority dstnat;
        iif eth0 tcp dport {22, 80} dnat to 10.0.0.2
    }
    chain postrouting {
        type nat hook postrouting priority srcnat;
        oif eth0 masquerade
    }
}

IP ごとの新規接続数をカウント

HTTPS 接続をカウントするには、次のスニペットを使用します:

/etc/nftables.conf
table inet filter {
    set https {
        type ipv4_addr;
        flags dynamic;
        size 65536;
        timeout 60m;
    }

    chain input {
        type filter hook input priority filter;
        ct state new meta l4proto { tcp, udp } th dport 443 update @https { ip saddr counter }
    }
}

カウンターを表示するには、nft list set inet filter https を実行してください。

動的 blackhole

このスニペットは、10/second の制限を超えた source IP (または /64 IPv6 range) からのすべての HTTPS 接続を 1 分間 drop します。

/etc/nftables.conf
table inet dev {
    set blackhole_ipv4 {
        type ipv4_addr;
        flags dynamic, timeout;
        size 65536;
    }
    set blackhole_ipv6 {
        type ipv6_addr;
        flags dynamic, timeout;
        size 65536;
    }

    chain input {
        type filter hook input priority filter; policy accept;
        ct state new meta l4proto { tcp, udp } th dport 443 \
                meter flood_ipv4 size 128000 { ip saddr timeout 10s limit rate over 10/second } \
                add @blackhole_ipv4 { ip saddr timeout 1m }
        ct state new meta l4proto { tcp, udp } th dport 443 \
                meter flood_ipv6 size 128000 { ip6 saddr and ffff:ffff:ffff:ffff:: timeout 10s limit rate over 10/second } \
                add @blackhole_ipv6 { ip6 saddr and ffff:ffff:ffff:ffff:: timeout 1m }

        ip saddr @blackhole_ipv4 counter drop
        ip6 saddr and ffff:ffff:ffff:ffff:: @blackhole_ipv6 counter drop
    }
}

blackhole された IP を表示するには、nft list set inet dev blackhole_ipvX を実行してください。

ヒントとテクニック

シンプルなステートフルファイアウォール

シンプルなステートフルファイアウォールの記事も参照してください。

シングルマシン

現在のルールセットを消去:

# nft flush ruleset

テーブルを追加:

# nft add table inet filter

input, forward, output ベースチェインを追加。input と forward のポリシーは破棄にして、output のポリシーは許可にする:

# nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
# nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
# nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }

レギュラーチェインを追加して tcp と udp に関連付ける:

# nft add chain inet filter TCP
# nft add chain inet filter UDP

関連・確立済みトラフィックは許可する:

# nft add rule inet filter input ct state related,established accept

ループバックインターフェイスのトラフィックは全て許可する:

# nft add rule inet filter input iif lo accept

不正なトラフィックは全て破棄する:

# nft add rule inet filter input ct state invalid drop

新しいエコー要求 (ping) は許可する:

# nft add rule inet filter input ip protocol icmp icmp type echo-request ct state new accept

新しい udp トラフィックは UDP チェインにジャンプする:

# nft add rule inet filter input ip protocol udp ct state new jump UDP

新しい tcp トラフィックは TCP チェインにジャンプする:

# nft add rule inet filter input ip protocol tcp tcp flags \& \(fin\|syn\|rst\|ack\) == syn ct state new jump TCP

他のルールによって処理されなかったトラフィックは全て拒否する:

# nft add rule inet filter input ip protocol udp reject
# nft add rule inet filter input ip protocol tcp reject with tcp reset
# nft add rule inet filter input counter reject with icmp type prot-unreachable

ここから TCP と UDP チェインで処理する接続で開きたいポートを決めます。例えばウェブサーバーの接続を開くには:

# nft add rule inet filter TCP tcp dport 80 accept

ポート 443 からのウェブサーバーの HTTPS 接続を許可するには:

# nft add rule inet filter TCP tcp dport 443 accept

ポート 22 の SSH 接続を許可するには:

# nft add rule inet filter TCP tcp dport 22 accept

DNS リクエストを許可するには:

# nft add rule inet filter TCP tcp dport 53 accept
# nft add rule inet filter UDP tcp dport 53 accept

設定に満足したら変更を保存して永続化させてください。

ブルートフォース攻撃の対策

Sshguard はブルートフォース攻撃を検出して一時的に IP アドレスに基づきブロックするようにファイアウォールを編集します。Sshguard で nftables を使うように設定する方法は Sshguard#nftables を見てください。

トラブルシューティング

Docker と共に使う

ノート
  • 次のセットアップでは、--net host --privileged を使用してもコンテナ内で AF_BLUETOOTH などのプロトコルを利用できなくなります。
  • ルートレス Dockerコンテナはすでに別のネットワーク名前空間で実行されています。何もする必要がないかもしれません。

nftables を使用すると、Docker のネットワーク (おそらく他のコンテナランタイムも同様) に干渉する可能性があります。 iptables ルールにパッチを適用して定義されたサービス開始順序を確保するか、docker の使用が非常に制限される dockerのiptablesの管理を完全に無効にするなど、さまざまな回避策がインターネット上で見つかります。 (ポートフォワーディングや docker-compose を考えてください)

信頼できる方法は、docker を別のネットワーク名前空間で実行させ、そこで任意の処理を実行できるようにすることです。 Docker が nftables と iptables ルールを混在させないように、iptables-nft使用しない方が良いでしょう。

以下の docker サービス ドロップインファイル を使用してください:

/etc/systemd/system/docker.service.d/netns.conf
[Service]
PrivateNetwork=yes
PrivateMounts=No

# cleanup
ExecStartPre=-nsenter -t 1 -n -- ip link delete docker0

# add veth
ExecStartPre=nsenter -t 1 -n -- ip link add docker0 type veth peer name docker0_ns
ExecStartPre=sh -c 'nsenter -t 1 -n -- ip link set docker0_ns netns "$$BASHPID" && true'
ExecStartPre=ip link set docker0_ns name eth0

# bring host online
ExecStartPre=nsenter -t 1 -n -- ip addr add 10.0.0.1/24 dev docker0
ExecStartPre=nsenter -t 1 -n -- ip link set docker0 up

# bring ns online
ExecStartPre=ip addr add 10.0.0.100/24 dev eth0
ExecStartPre=ip link set eth0 up
ExecStartPre=ip route add default via 10.0.0.1 dev eth0

セットアップにおいてIPアドレス 10.0.0.* が適切でない場合は、調整してください。

以下のポストルーティングルールで、docker0 のIPフォワーディングを有効にし、NATを設定します:

iifname docker0 oifname eth0 masquerade

次に、kernel IP forwarding が有効になっていることを確認します。


これで、nftables を使用して docker0 インターフェイスのファイアウォールとポートフォワーディングを干渉することなくセットアップできるようになります。

参照