nftables
nftables は既存の ip-, ip6-, arp-, ebtables フレームワークを置き換える netfilter のプロジェクトです。新しいパケットフィルタリングフレームワーク、新しいユーザースペースユーティリティ (nft)、そして ip- と ip6tables の互換レイヤーを提供します。現行のフック、接続追跡システム、ユーザースペースのキューイングコンポーネント、そして netfilter のログサブシステムを使っています。
nftables は3つのメインコンポーネントから構成されています: カーネルの実装、libnl netlink communication そして nftables ユーザースペースフロントエンド。カーネルは netlink の設定インターフェイスだけでなく、小さなクラス言語インタプリタを使用するランタイムのルールセットの評価も提供します。libnl にはカーネルと通信するためのローレベルな関数が含まれています。nftables フロントエンドはユーザーが対話するものです。
nftables の公式 wiki には詳しい情報が載っています。
インストール
nftables のユーザーランドユーティリティは nftables パッケージで利用できます。開発版の nftables-gitAUR パッケージも存在します。
使用方法
nftables ではコマンドラインで作成される一時的なルールと、ファイルに保存して読み込まれる永続的なルールを区別していません。デフォルトファイルの /etc/nftables.conf
には "inet filter" という名前のシンプルな ipv4/ipv6 ファイアウォールテーブルが既に記述されています。
nftables.service
を起動・有効化してください。
以下のコマンドでルールセットを確認できます:
# nft list ruleset
シンプルなファイアウォール
nftables には、/etc/nftables.conf
ファイルに保存されたシンプルで安全なファイアウォール設定が付属しています。
nftables.service
は、起動または有効化時に、このファイルからルールを読み込みます。
設定
nftables のユーザースペースユーティリティ nft
は現在カーネルのためにルールセットを処理する前にほとんどのルールセットの評価を行います。ルールはチェインに保存され、チェインはテーブルに保存されます。下のセクションではルールを作成・編集する方法を説明します。
下のセクションで行った変更は全て一時的なものになります。変更を永続化するにはルールセットを /etc/nftables.conf
に保存してください (ルールセットは nftables.service
によって読み込まれます):
# nft list ruleset > /etc/nftables.conf
ファイルから入力するには -f
フラグを使用します:
# nft -f 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 table
例えば inet
ファミリーの filter
テーブルのルールを全て表示するには:
# nft list table inet filter
テーブルの削除
テーブルを削除するには:
# nft delete table family table
テーブルはチェインが全く存在しない場合にのみ削除できます。
テーブルのクリア
テーブルから全てのルールを消去するには:
# nft flush table family table
チェイン
チェインの用途はルールを保持することです。iptables のチェインと違って、nftables には初めから組み込まれているチェインはありません。そのためチェインが netfilter フレームワークにあるタイプやフックをどれも使わない場合、iptables とは異なりチェインを通り抜けるパケットは nftables の影響を受けません。
チェインには2つのタイプがあります。base チェインはネットワークスタックからのパケットのエントリポイントとなります。フックの値を指定することができます。regular チェインはジャンプターゲットとして使用することができます。
以下のコマンドで使っている family
は全て任意であり、指定しなかった場合は ip
が使われます。
チェインの作成
Regular チェイン
以下のコマンドは table
という名前のテーブルに chain
という名前のレギュラーチェインを追加します:
# nft add chain family table chain
例えば、inet
アドレスファミリーの filter
テーブルに tcpchain
という名前のレギュラーチェインを追加するには:
# nft add chain inet filter tcpchain
Base チェイン
ベースチェインを追加するにはフックとプライオリティの値を指定します:
# nft add chain family table chain { type type hook hook priority priority \; }
type
は filter
, route
, nat
のどれかから選ぶことができます。
IPv4/IPv6/Inet アドレスファミリーでは hook
は prerouting
, input
, forward
, output
, postrouting
のどれかになります。他のファミリーのフックについては nft(8) を参照してください。
priority
には整数値を指定します。低い値のチェインが先に処理され、負の値を指定することもできます [3]。
例えば、input パケットをフィルタリングするベースチェインを追加するには:
# nft add chain inet filter input { type filter hook input priority 0\; }
add
を create
に置き換えると、チェインが既に存在するときにエラーが返ってくるようになります。
ルールの表示
以下のコマンドでチェインの全てのルールを表示できます:
# nft list chain family table chain
例えば、filter
という名前の inet
テーブルに存在する output
という名前のチェインのルールを表示するには:
# nft list chain inet filter output
チェインの編集
チェインを編集したいときは、チェインの名前を指定して変更したいルールを定義します:
# nft chain <table> <family> <chain> { [ type <type> hook <hook> device <device> priority <priority> \; policy <policy> \; ] }
例えば、デフォルトテーブルの input チェインポリシーを "accept" から "drop" に変更したい場合:
# nft chain inet filter input { policy drop \; }
チェインの削除
チェインを削除するには:
# nft delete chain family table chain
削除するチェインにはルールやジャンプターゲットが含まれていてはいけません。
チェインのルールを消去
チェインからルールを消去するには:
# nft flush chain family table chain
ルール
ルールは表現または宣言から構成され、チェインの中に格納されます。
ルールの追加
チェインにルールを追加するには:
# nft add rule family table chain position statement
ルールは position
に追加されます。位置は指定しなくてもかまいません。指定しなかった場合、ルールはチェインの末尾に追加されます。
特定の位置の前にルールを追加するには:
# nft insert rule family table chain position statement
position
を指定しなかった場合、ルールはチェインの一番前に追加されます。
表現
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>
削除
個々のルールはハンドルを使わないと削除することができません。nft --handle list
コマンドを使うことでルールのハンドルを確認できます。--handle
スイッチを付けると、nft
はハンドルを出力するようになります。
以下ではルールのハンドルを確認してルールを削除しています。未解決の IP アドレスのような、数字の出力を表示するときは --number
引数を使うと良いでしょう。
# nft --handle --numeric list chain filter input
table ip fltrTable { chain input { type filter hook input priority 0; ip saddr 127.0.0.1 accept # handle 10 } }
# nft delete rule fltrTable input handle 10
nft flush table
コマンドを使うことでテーブルの全てのチェインをフラッシュできます。個別のチェインをフラッシュするときは nft flush chain
または nft delete rule
コマンドを使います。
# nft flush table foo # nft flush chain foo bar # nft delete rule ip6 foo bar
最初のコマンドでは ip foo
テーブルのチェイン全てをフラッシュします。2番目のコマンドは ip foo
テーブルの bar
チェインをフラッシュします。3番目のコマンドは ip6 foo
テーブルの bar
チェインの全てのルールを削除します。
アトミックリロード
現在のルールセットをフラッシュする:
# echo "flush ruleset" > /tmp/nftables
現在のルールセットをダンプする:
# nft list ruleset >> /tmp/nftables
/tmp/nftables
を編集して次のコマンドで変更を適用:
# nft -f /tmp/nftables
サンプル
ワークステーション
/etc/nftables.conf
flush ruleset table inet filter { chain input { type filter hook input priority 0; # accept any localhost traffic iif lo accept # accept traffic originated from us ct state established,related accept # activate the following line to accept common local services #tcp dport { 22, 80, 443 } ct state new accept # accept neighbour discovery otherwise IPv6 connectivity breaks. ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept # count and drop any other traffic counter drop } }
シンプルな IPv4/IPv6 ファイアウォール
firewall.rules
# A simple firewall flush ruleset table inet filter { chain input { type filter hook input priority 0; policy drop; # established/related connections ct state established,related accept # invalid connections ct state invalid drop # loopback interface iif lo accept # ICMP # routers may also want: mld-listener-query, nd-router-solicit ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept # SSH (port 22) tcp dport ssh accept # HTTP (ports 80 & 443) tcp dport { http, https } accept } chain forward { type filter hook forward priority 0; policy drop; } chain output { type filter hook output priority 0; policy accept; } }
レート制限 IPv4/IPv6 ファイアウォール
firewall.2.rules
table inet filter { chain input { type filter hook input priority 0; policy drop; # no ping floods: ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate 10/second accept ip protocol icmp icmp type echo-request limit rate 10/second accept ct state established,related accept ct state invalid drop iif lo accept # avoid brute force on ssh: tcp dport ssh limit rate 15/minute accept } chain forward { type filter hook forward priority 0; policy drop; } chain output { type filter hook output priority 0; policy accept; } }
ジャンプ
設定ファイルでジャンプを使うときは、先にターゲットチェインを定義する必要があります。そうしないと Error: Could not process rule: No such file or directory
というエラーが発生します。
jump.rules
table inet filter { chain web { tcp dport http accept tcp dport 8080 accept } chain input { type filter hook input priority 0; ip saddr 10.0.2.0/24 jump web drop } }
インターフェイスによってルールを変える
複数のネットワークインターフェイスが存在する場合、それぞれのインターフェイスごとに別々のフィルターチェインを設定したい場合があるかもしれません。例えば、ホームルーターを構築するとき、LAN 上でアクセスできるウェブサーバーを実行しつつ (nsp3s0
インターフェイス)、インターネットからはアクセスできないようにしたい場合 (enp2s0
インターフェイス) などは以下のように設定します:
table inet filter { chain input { # this chain serves as a dispatcher type filter hook input priority 0; iif lo accept # always accept loopback iifname enp2s0 jump input_enp2s0 iifname enp3s0 jump input_enp3s0 reject with icmp type port-unreachable # refuse traffic from all other interfaces } chain input_enp2s0 { # rules applicable to public interface interface ct state {established,related} accept ct state invalid drop udp dport bootpc accept tcp dport bootpc accept reject with icmp type port-unreachable # all other traffic } chain input_enp3s0 { 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 icmp type port-unreachable # all other traffic } chain ouput { # we let everything out type filter hook output priority 0; accept } }
もしくは iifname
ステートメントを特定のインターフェイスで使用して、他のインターフェイスについてはデフォルトルールを設定するという方法もあります。
マスカレード
nftables には特殊なキーワード masquerade
が存在し、送信元アドレスが自動的に出力インターフェイスのアドレスに設定されます (ソース)。ルーターのインターフェイスが多数の ISP に接続されているときなど、インターフェイスの IP アドレスが一定でない場合に有用です。通常は、インターフェイスの IP アドレスが変わるたびにネットワークアドレス変換 (NAT) のルールを更新する必要があります。
masquerade
を使用するには:
- カーネルコンフィグで以下のマスカレード設定が有効になっている必要があります。
CONFIG_NFT_MASQ=m
masquerade
キーワードはnat
タイプのチェインでのみ使うことができ、inet
ファミリーのテーブルでは利用できません。ip
ファミリーやip6
ファミリーのテーブルを使ってください。- マスカレードは一種のソース NAT であり、出力パスでのみ機能します。
2つのインターフェイスが存在し nsp3s0
が LAN に接続され、enp2s0
がインターネットに接続されているマシンでの設定例:
table ip nat { chain prerouting { type nat hook prerouting priority 0; } chain postrouting { type nat hook postrouting priority 0; oifname "enp0s2" masquerade } }
ヒントとテクニック
シンプルなステートフルファイアウォール
シンプルなステートフルファイアウォールの記事も参照してください。
シングルマシン
現在のルールセットを消去:
# 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 と共に使う
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 # 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
インターフェイスのファイアウォールとポートフォワーディングを干渉することなくセットアップできるようになります。