「Nftables」の版間の差分
Kusanaginoturugi (トーク | 投稿記録) →サンプル: 記事を更新 |
Kusanaginoturugi (トーク | 投稿記録) →ヒントとテクニック: 記事を更新 |
||
| 684行目: | 684行目: | ||
== ヒントとテクニック == |
== ヒントとテクニック == |
||
=== 現在の rule set の保存 === |
|||
{{ic|nft list ruleset}} コマンドの出力は、そのまま有効な入力ファイルとしても使用できます。現在の rule set をファイルに保存し、後で読み戻すことができます。 |
|||
# nft -s list ruleset | tee ''filename'' |
|||
{{Note|元のファイルで変数定義を使っていた場合でも、{{ic|nft list}} は変数定義を出力しません。変数は失われます。ルール内で使われていた変数は、その値に置き換えられます。}} |
|||
=== シンプルなステートフルファイアウォール === |
=== シンプルなステートフルファイアウォール === |
||
| 762行目: | 770行目: | ||
[[Sshguard]] はブルートフォース攻撃を検出して一時的に IP アドレスに基づきブロックするようにファイアウォールを編集します。Sshguard で nftables を使うように設定する方法は [[Sshguard#nftables]] を見てください。 |
[[Sshguard]] はブルートフォース攻撃を検出して一時的に IP アドレスに基づきブロックするようにファイアウォールを編集します。Sshguard で nftables を使うように設定する方法は [[Sshguard#nftables]] を見てください。 |
||
=== トラフィックのログ記録 === |
|||
{{ic|log}} action を使ってパケットをログに記録できます。すべての受信 traffic をログに記録する最も単純なルールは次のとおりです: |
|||
# nft add rule inet filter input log |
|||
詳しくは [https://wiki.nftables.org/wiki-nftables/index.php/Logging_traffic nftables wiki] を参照してください。 |
|||
=== Monitor === |
|||
すべてのイベントを監視し、native nft format で報告します。 |
|||
# nft monitor |
|||
{{man|8|nft|MONITOR}} を参照してください。 |
|||
==== ruleset debugging trace temporary ==== |
|||
'''meta nftrace set 1''' は ruleset packet tracing を on/off します。trace を見るには [[#Monitor|monitor trace]] コマンドを使用します。 |
|||
別の shell で、interactive shell 内にファイルを "include" します: |
|||
# nft -i |
|||
nft> include "/root/nftables.trace" |
|||
必要に応じて調整した例です: |
|||
{{hc|/root/nftables.trace| |
|||
add table ip temp-trace {comment "Temporary table!!"; flags owner;} |
|||
add chain ip temp-trace icmp-prerouting { type filter hook prerouting priority raw - 1 ; } |
|||
add rule ip temp-trace icmp-prerouting ''ip protocol icmp'' '''meta nftrace set 1''' |
|||
}} |
|||
このファイルは一時テーブル (''flags owner'') を追加します。そのため、呼び出し元の interactive nft プロセスが閉じられると自動的に削除されます。Base Chain は用途に応じて調整する必要があります。複数のチェインや複数の "meta nftrace set 1" ルールを作成できます。"ip protocol icmp" は単なる例であり、必須ではありません。同様の効果を得る方法は多数あります。この方法の利点は、interactive shell を閉じることで以前の状態が自動的に復元されることと、ファイル内にエラーがある場合に何も実行されないことです。 |
|||
詳しくは [https://wiki.nftables.org/wiki-nftables/index.php/Ruleset_debug/tracing nftables wiki] と、その処理を自動化して色付けする [https://github.com/aborrero/nftables-tracer python tool] を参照してください。 |
|||
=== iptables-nft の使用 === |
|||
{{Accuracy|nftables は legacy iptables object が読み込まれている場合に警告するため、legacy iptables と nftables を同時に使うことが「完全に問題なく動作する」とは言えません。}} |
|||
古い iptables 言語は Linux ドキュメントでは依然としてかなり支配的であり、Docker のネットワークなど、iptables に依存して動作するものも少なくありません。legacy iptables と nftables を同時に使うことも可能ではありますが、iptables-nft の変換を使うことが推奨されます。理由は次のとおりです: |
|||
* すべてを新しく、より効率的で、ロックを必要としないフレームワークの同じ場所に配置します。 |
|||
* 競合をチェックします。 |
|||
nftables で古い iptables 言語を使用する方法は 2 つあります: |
|||
* {{ic|iptables-translate}} と {{ic|iptables-restore-translate}} ({{ic|ip6tables}}、{{ic|ebtables}} なども同様) は、iptables 言語を受け取り nft 言語を出力します。実行中の nft 設定は変更しません。{{man|8|xtables-translate}} を参照してください。 |
|||
: 後で保守したい設定については、{{ic|-translate}} ツールを使い、その結果のコードを既存のルールに統合するのがよいでしょう。例えば、[[シンプルなステートフルファイアウォール]]やインターネット上で便利なものを見つけた場合、それらを nft 設定に入れられるように変換できます。 |
|||
* {{ic|iptables}} と {{ic|iptables-restore}} ({{ic|ip6tables}} なども同様) は上記の変換を使い、さらに実行中の nft 設定に反映します。通常の iptables と同じように統計情報も提供します。{{man|8|xtables-nft}} を参照してください。 |
|||
: これらのコマンドは、やるべきことを考えれば十分にうまく動作します。単純な使い方では「そのまま動く」はずですが、ときどき手動でのデバッグが必要になることがあります。 |
|||
{{Note|translator は iptables 言語の大部分をカバーしていますが、すべてではありません。一部の iptables ルールは組み合わせて動作し、translator が正しく変換するには文脈が必要です。そのため、変換結果が空になる行があっても慌てないでください。}} |
|||
=== systemd-networkd を使った動的名前付きセット === |
|||
[[systemd-networkd]] の接続は、{{ic|NFTSet{{=}}}} オプションを使用して、事前定義された名前付きセットにホスト IP アドレス、ネットワークプレフィックス、インターフェイスインデックスを投入できます。これにより、{{ic|/etc/nftables.conf}} にそれらをハードコーディングすることを避けられます。{{ic|NFTSet{{=}}}} オプションは、{{ic|[Address]}}、{{ic|[DHCPv4]}}、{{ic|[DHCPv6]}}、{{ic|[IPv6AcceptRA]}} セクションでサポートされています。{{man|5|systemd.network|[ADDRESS] SECTION OPTIONS}} を参照してください。 |
|||
例えば、ローカルネットワークからの接続 (IP アドレスが DHCP または SLAAC で割り当てられる場合) を別の {{ic|my_input_lan}} チェインで処理するには: |
|||
{{hc|/etc/nftables.conf|2= |
|||
... |
|||
table inet my_table { |
|||
set eth_ipv4_prefix { |
|||
type ipv4_addr |
|||
flags interval |
|||
comment "Populated by systemd-networkd" |
|||
} |
|||
set eth_ipv6_prefix { |
|||
type ipv6_addr |
|||
flags interval |
|||
comment "Populated by systemd-networkd" |
|||
elements = { fe80::/10 } |
|||
} |
|||
set eth_ifindex { |
|||
type iface_index |
|||
comment "Populated by systemd-networkd" |
|||
} |
|||
... |
|||
chain my_input { |
|||
type filter hook input priority filter; policy drop; |
|||
iif @eth_ifindex ip6 saddr @eth_ipv6_prefix jump my_input_lan comment "Connections from LAN" |
|||
iif @eth_ifindex ip saddr @eth_ipv4_prefix jump my_input_lan comment "Connections from LAN" |
|||
} |
|||
... |
|||
} |
|||
}} |
|||
{{hc|/etc/systemd/network/my-network.network|2= |
|||
... |
|||
[DHCPv4] |
|||
NFTSet=prefix:inet:my_table:eth_ipv4_prefix |
|||
NFTSet=ifindex:inet:my_table:eth_ifindex |
|||
[DHCPv6] |
|||
NFTSet=prefix:inet:my_table:eth_ipv6_prefix |
|||
NFTSet=ifindex:inet:my_table:eth_ifindex |
|||
[IPv6AcceptRA] |
|||
NFTSet=prefix:inet:my_table:eth_ipv6_prefix |
|||
NFTSet=ifindex:inet:my_table:eth_ifindex |
|||
... |
|||
}} |
|||
== トラブルシューティング == |
== トラブルシューティング == |
||
2026年5月27日 (水) 13:14時点における版
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 コマンドの実装を提供します。ただし、古い iptables-legacy ツールで作成されたルールは別のオブジェクトであり、それらが存在する場合、iptables は警告を表示します。フロントエンド
- firewalld (firewall-cmd) — ネットワークとファイアウォールゾーンの設定、およびファイアウォールルールの設定と構成を行うデーモンおよびコンソールインターフェイス。
- nft-blackhole — 国別およびブラックリストによって nftables で IP をブロックするスクリプト / デーモン。
- ufw — Ufw は Uncomplicated Firewall の略で、netfilter ファイアウォールを管理するためのプログラムです。
- reaction — プログラムの出力から繰り返し出現するパターンをスキャンし、アクションを実行するデーモン。fail2ban の軽量な代替です。
使用方法
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 以上が必要で、ip と ip6 ファミリーを統一してルールを簡単に定義できます。
アドレスフファミリーの完全な定義は 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 には filter、route、nat を指定できます。
IPv4/IPv6/Inet address family では、hook_type に prerouting、input、forward、output、postrouting を指定できます。サポートされる family_type、chain_type、hook_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; }'
上記のいずれでも add を create に置き換えると、新しいチェインを追加しますが、チェインが既に存在する場合はエラーを返します。
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
ルール
ルールは表現または宣言から構成され、チェインの中に格納されます。
ルールの追加
チェインにルールを追加するには:
# 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 として参照されることにも注意してください。
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
}
}
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キーワードはnattype のチェインでのみ使用できます。- 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_forward を 1 に設定する必要があります。
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 を実行してください。
ヒントとテクニック
現在の rule set の保存
nft list ruleset コマンドの出力は、そのまま有効な入力ファイルとしても使用できます。現在の rule set をファイルに保存し、後で読み戻すことができます。
# nft -s list ruleset | tee filename
nft list は変数定義を出力しません。変数は失われます。ルール内で使われていた変数は、その値に置き換えられます。シンプルなステートフルファイアウォール
シンプルなステートフルファイアウォールの記事も参照してください。
シングルマシン
現在のルールセットを消去:
# 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 を見てください。
トラフィックのログ記録
log action を使ってパケットをログに記録できます。すべての受信 traffic をログに記録する最も単純なルールは次のとおりです:
# nft add rule inet filter input log
詳しくは nftables wiki を参照してください。
Monitor
すべてのイベントを監視し、native nft format で報告します。
# nft monitor
nft(8) § MONITOR を参照してください。
ruleset debugging trace temporary
meta nftrace set 1 は ruleset packet tracing を on/off します。trace を見るには monitor trace コマンドを使用します。
別の shell で、interactive shell 内にファイルを "include" します:
# nft -i nft> include "/root/nftables.trace"
必要に応じて調整した例です:
/root/nftables.trace
add table ip temp-trace {comment "Temporary table!!"; flags owner;}
add chain ip temp-trace icmp-prerouting { type filter hook prerouting priority raw - 1 ; }
add rule ip temp-trace icmp-prerouting ip protocol icmp meta nftrace set 1
このファイルは一時テーブル (flags owner) を追加します。そのため、呼び出し元の interactive nft プロセスが閉じられると自動的に削除されます。Base Chain は用途に応じて調整する必要があります。複数のチェインや複数の "meta nftrace set 1" ルールを作成できます。"ip protocol icmp" は単なる例であり、必須ではありません。同様の効果を得る方法は多数あります。この方法の利点は、interactive shell を閉じることで以前の状態が自動的に復元されることと、ファイル内にエラーがある場合に何も実行されないことです。
詳しくは nftables wiki と、その処理を自動化して色付けする python tool を参照してください。
iptables-nft の使用
古い iptables 言語は Linux ドキュメントでは依然としてかなり支配的であり、Docker のネットワークなど、iptables に依存して動作するものも少なくありません。legacy iptables と nftables を同時に使うことも可能ではありますが、iptables-nft の変換を使うことが推奨されます。理由は次のとおりです:
- すべてを新しく、より効率的で、ロックを必要としないフレームワークの同じ場所に配置します。
- 競合をチェックします。
nftables で古い iptables 言語を使用する方法は 2 つあります:
iptables-translateとiptables-restore-translate(ip6tables、ebtablesなども同様) は、iptables 言語を受け取り nft 言語を出力します。実行中の nft 設定は変更しません。xtables-translate(8) を参照してください。
- 後で保守したい設定については、
-translateツールを使い、その結果のコードを既存のルールに統合するのがよいでしょう。例えば、シンプルなステートフルファイアウォールやインターネット上で便利なものを見つけた場合、それらを nft 設定に入れられるように変換できます。
iptablesとiptables-restore(ip6tablesなども同様) は上記の変換を使い、さらに実行中の nft 設定に反映します。通常の iptables と同じように統計情報も提供します。xtables-nft(8) を参照してください。
- これらのコマンドは、やるべきことを考えれば十分にうまく動作します。単純な使い方では「そのまま動く」はずですが、ときどき手動でのデバッグが必要になることがあります。
systemd-networkd を使った動的名前付きセット
systemd-networkd の接続は、NFTSet= オプションを使用して、事前定義された名前付きセットにホスト IP アドレス、ネットワークプレフィックス、インターフェイスインデックスを投入できます。これにより、/etc/nftables.conf にそれらをハードコーディングすることを避けられます。NFTSet= オプションは、[Address]、[DHCPv4]、[DHCPv6]、[IPv6AcceptRA] セクションでサポートされています。systemd.network(5) § [ADDRESS SECTION OPTIONS] を参照してください。
例えば、ローカルネットワークからの接続 (IP アドレスが DHCP または SLAAC で割り当てられる場合) を別の my_input_lan チェインで処理するには:
/etc/nftables.conf
...
table inet my_table {
set eth_ipv4_prefix {
type ipv4_addr
flags interval
comment "Populated by systemd-networkd"
}
set eth_ipv6_prefix {
type ipv6_addr
flags interval
comment "Populated by systemd-networkd"
elements = { fe80::/10 }
}
set eth_ifindex {
type iface_index
comment "Populated by systemd-networkd"
}
...
chain my_input {
type filter hook input priority filter; policy drop;
iif @eth_ifindex ip6 saddr @eth_ipv6_prefix jump my_input_lan comment "Connections from LAN"
iif @eth_ifindex ip saddr @eth_ipv4_prefix jump my_input_lan comment "Connections from LAN"
}
...
}
/etc/systemd/network/my-network.network
... [DHCPv4] NFTSet=prefix:inet:my_table:eth_ipv4_prefix NFTSet=ifindex:inet:my_table:eth_ifindex [DHCPv6] NFTSet=prefix:inet:my_table:eth_ipv6_prefix NFTSet=ifindex:inet:my_table:eth_ifindex [IPv6AcceptRA] NFTSet=prefix:inet:my_table:eth_ipv6_prefix NFTSet=ifindex:inet:my_table:eth_ifindex ...
トラブルシューティング
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 インターフェイスのファイアウォールとポートフォワーディングを干渉することなくセットアップできるようになります。