nftables

提供: ArchWiki
2022年9月26日 (月) 20:02時点におけるAshMyzk (トーク | 投稿記録)による版 (→‎トラブルシューティング: 追加)
ナビゲーションに移動 検索に移動

関連記事

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 パッケージも存在します。

ヒント: ほとんどの iptables フロントエンド はnftables の直接的または間接的なサポートを備えていませんが、導入することは可能です。[1] nftables と iptables の両方をサポートするグラフィカルなフロントエンドとしては、firewalld があります。[2]

使用方法

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
ノート: nft list は変数の定義を出力しません。/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 以上が必要で、ipip6 ファミリーを統一してルールを簡単に定義できます。

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

アドレスフファミリーの完全な定義は 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 \; }

typefilter, route, nat のどれかから選ぶことができます。

IPv4/IPv6/Inet アドレスファミリーでは hookprerouting, input, forward, output, postrouting のどれかになります。他のファミリーのフックについては nft(8) を参照してください。

priority には整数値を指定します。低い値のチェインが先に処理され、負の値を指定することもできます [3]

例えば、input パケットをフィルタリングするベースチェインを追加するには:

# nft add chain inet filter input { type filter hook input priority 0\; }

addcreate に置き換えると、チェインが既に存在するときにエラーが返ってくるようになります。

ルールの表示

以下のコマンドでチェインの全てのルールを表示できます:

# 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

ルール

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

ルールの追加

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

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

# 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 と共に使う

ノート:
  • With the following setup, you will not be able to use protocols like AF_BLUETOOTH inside containers even with --net host --privileged.
  • Rootless Docker containers already run in a separate network namespace. You may not need to do anything.

Using nftables can interfere with Docker networking (and probably other container runtimes as well). You can find various workarounds on the internet which either involve patching iptables rules and ensuring a defined service start order or disabling dockers iptables management completely which makes using docker very restrictive (think port forwarding or docker-compose).

A reliable method is letting docker run in a separate network namespace where it can do whatever it wants. It is probably best to not use iptables-nft to prevent docker from mixing nftables and iptables rules.

Use the following docker service drop-in file:

/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

Adjust the 10.0.0.* IP addresses if they are not appropriate for your setup.

Enable IP forwarding and set-up NAT for docker0 with the following postrouting rule:

iifname docker0 oifname eth0 masquerade

Then, ensure that kernel IP forwarding is enabled.

Now you can setup a firewall and port forwarding for the docker0 interface using nftables without any interference.

参照