nftablesクイックHowto

本エントリーはÉric Leblond氏のNftables quick howtoを翻訳し、執筆時点でのnftablesの情報を追加したものです。

はじめに

このドキュメントはダーティなハウツーとチートシートの中間に位置するものです。nftablesの面白い機能のいくつかについての短い説明は、Why you will love nftables和訳)で読むことが出来ます。

nftablesの背景にある設計やアイデアについては、announce of the first release of nftablesを読んでください。より一般的な情報については、Kernel Recipes:で私が話したEric Leblond, OISF – Nftablesを見ることも出来ます。

nftablesを作る

訳注:執筆時点で、Fedora rawhideにはnftablesを含め、nftablesに必要とされるRPMパッケージが用意されています。最新版を試すのでなければ、このセクションは読み飛ばして構いません。以下の手順でrawhideレポジトリを有効化し、yumコマンドでnftablesパッケージをインストールすればnftablesを使えます。

# yum install fedora-release-rawhide
# yum-config-manager --disable fedora updates updates-testing
# yum-config-manager --enable rawhide
# yum update yum
# yum install nftables

ライブラリ

以下のライブラリが必要です。

ディストリビューションにlibmnlが既に含まれているかもしれませんが、普通にビルドしても、両方とも簡単です:

# ./autogen.sh
# ./configure
# make
# make install
# ldconfig

nftables

最初に依存するものをインストールします:

# aptitude install libgmp-dev libreadline-dev

ドキュメンテーションをビルドしたい場合:

# aptitude install docbook2x docbook-utils
# git clone git://git.netfilter.org/nftables
# cd nftables
# ./autogen.sh
# ./configure
# make
# make install

kernel

もしLinuxのgitツリーがまだ手元に無い場合は実行:

# git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

もしLinuxのgitツリーが手元にある場合は最新のソースに更新するだけ:

# cd linux
# git pull --rebase

これでソースが用意出来たので、nftablesのオプションを選択できます:

$ make oldconfig

Netfilter Xtables support (required for ip_tables) (NETFILTER_XTABLES) [M/y/?] m
Netfilter nf_tables support (NF_TABLES) [N/m] (NEW) m
  Netfilter nf_tables payload module (NFT_PAYLOAD) [N/m] (NEW) m
  Netfilter nf_tables IPv6 exthdr module (NFT_EXTHDR) [N/m] (NEW) m
  Netfilter nf_tables meta module (NFT_META) [N/m] (NEW) m
  Netfilter nf_tables conntrack module (NFT_CT) [N/m] (NEW) m
  Netfilter nf_tables rbtree set module (NFT_RBTREE) [N/m] (NEW) m
  Netfilter nf_tables hash set module (NFT_HASH) [N/m] (NEW) m
  Netfilter nf_tables counter module (NFT_COUNTER) [N/m] (NEW) m
  Netfilter nf_tables log module (NFT_LOG) [N/m] (NEW) m
  Netfilter nf_tables limit module (NFT_LIMIT) [N/m] (NEW) m
  Netfilter nf_tables nat module (NFT_NAT) [N/m] (NEW) m
  Netfilter x_tables over nf_tables module (NFT_COMPAT) [N/m/?] (NEW) m

IPv4 nf_tables support (NF_TABLES_IPV4) [N/m] (NEW) m
  nf_tables IPv4 reject support (NFT_REJECT_IPV4) [N/m] (NEW) m
  IPv4 nf_tables route chain support (NFT_CHAIN_ROUTE_IPV4) [N/m] (NEW) m
  IPv4 nf_tables nat chain support (NFT_CHAIN_NAT_IPV4) [N/m] (NEW) m

IPv6 nf_tables support (NF_TABLES_IPV6) [M/n] m
  IPv6 nf_tables route chain support (NFT_CHAIN_ROUTE_IPV6) [M/n] m
  IPv6 nf_tables nat chain support (NFT_CHAIN_NAT_IPV6) [M/n] m

Ethernet Bridge nf_tables support (NF_TABLES_BRIDGE) [N/m/y] (NEW) m

さて、いつものコマンドでカーネルがビルドできます。Debianでデュアルコアのマシンの場合は以下で可能:

# make -j 2 deb-pkg

もしくは、代わりに以下の古い方法でも:

CONCURRENCY_LEVEL=2 make-kpkg --revision 0.1 --rootcmd fakeroot  --initrd   --append-to-version nftables kernel_image kernel_headers

Debianユーザはgitソースからカーネルをビルドすることも可能:

他の関連するパッケージはこのディレクトリから利用できます。

実行する

初期設定

iptablesのようなチェインの設定にするには、ソースで提供されているipv4-filterファイルを使います。

# nft -f files/nftables/ipv4-filter

訳注:Fedora rawhideでインストールした場合、/etc/nftables/ipv4-filter。実行権限とSELinuxのラベルが設定されているので、パスを指定して実行することが可能です。後述するIPv6のフィルタも同様。

そしてその結果のチェインを表示できます:

# nft list table filter

フィルターはoutputやinputがチェインやテーブルの名前として使われている点に注意。他のどんな文字列でも使えます。

基本的なルールの扱い

ある宛先アドレスを持つoutputパケットをドロップするには

# nft add rule ip filter output ip daddr 1.2.3.4 drop

ルールのカウンタ(訳注:パケットのバイト数をカウントするオプション)はnftablesではオプションで、有効にするにはcounterキーワードが必要です:

# nft add rule ip filter output ip daddr 1.2.3.4 counter drop

ルールをネットワークに追加するには、以下をそのまま使います:

# nft add rule ip filter output ip daddr 192.168.1.0/24 counter

TCP/80番ポートへのパケットをドロップするには構文は以下です:

# nft add rule ip filter input tcp dport 80 drop

ICMP echoリクエストを受け取るには:

# nft add rule filter input icmp type echo-request accept

フィルタリングを組み合わせるには、ipの構文を複数回指定すれば良いだけです:

# nft add rule ip filter output ip protocol icmp ip daddr 1.2.3.4 counter drop

チェインのルールを全て削除するには:

# nft delete rule filter output

特定のルールを削除するには、-aフラッグをnftコマンドで使ってハンドルナンバーを知る必要があります:

# nft list table filter -a
table filter {
        chain output {
                 ip protocol icmp ip daddr 1.2.3.4 counter packets 5 bytes 420 drop # handle 10
...

そうすればルール10番を削除できます:

# nft delete rule filter output handle 10

フィルタテーブルをフラッシュすることもできます:

# nft flush table filter

ルールを挿入することも:

# nft insert rule filter input tcp dport 80 counter accept

ルールを特定の位置に挿入するあるいは追加することが可能です。そのためには、新しいルールを挿入あるいは追加したいルールのハンドル知る必要があります。リスト操作で-aフラッグを使えばできます:

# nft list table filter -n  -a
table filter {
        chain output {
                 type filter hook output priority 0;
                 ip protocol tcp counter packets 82 bytes 9680 # handle 8
                 ip saddr 127.0.0.1 ip daddr 127.0.0.6 drop # handle 7
        }
}
# nft add rule filter output position 8 ip daddr 127.0.0.8 drop
# nft list table filter -n -a
table filter {
        chain output {
                 type filter hook output priority 0;
                 ip protocol tcp counter packets 190 bytes 21908 # handle 8
                 ip daddr 127.0.0.8 drop # handle 10
                 ip saddr 127.0.0.1 ip daddr 127.0.0.6 drop # handle 7
        }
}

確かに、ハンドル8番のルールの後にルールが追加されました。指定のハンドル番号のルールの前に追加するのは、以下で可能:

# nft insert rule filter output position 8 ip daddr 127.0.0.12 drop

単にプロトコルにマッチさせたいだけなら、次のようにすればできます:

# nft insert rule filter output ip protocol tcp counter

IPv6

IPv4同様、チェインを作成する必要があります。以下で可能:

# nft -f files/nftables/ipv6-filter

そうすればルールを追加できます:

# nft add rule ip6 filter output ip6 daddr home.regit.org counter

ルールの一覧は以下で得られます:

# nft list table ip6 filter

動的なIPv6設定と近隣探索(Neighbor Discovery)を受け入れるには、以下で可能:

# nft add rule ip6 filter input icmpv6 type nd-neighbor-solicit accept
# nft add rule ip6 filter input icmpv6 type nd-router-advert accept

コネクションのトラッキング

コネクションが確立した内向きパケットを受け入れるには:

# nft insert rule filter input ct state established accept

インターフェースでのフィルタ

ループバックインターフェース上の外向きパケットを受け入れるには:

# nft insert rule filter output oif lo accept

eth2上の内向きパケットの場合:

# nft insert rule filter input iif eth2 accept

oifは実際のところ、カーネル内部のインターフェースのインデックスである整数でマッチすることに注意してください。ユーザスペースはnftのルールが評価され(カーネルに送られる前に)指定された名前をインターフェースのインデックスに変換します。結果として、もしインターフェースが存在しない場合にはルールを追加することは出来ません。さらに、インターフェースが削除されたり再作成された場合も同様で、カーネル内部に追加されたインターフェースのインデックスは単調に増加するので、やはりマッチしません。従って、oifは高速なフィルタですが、動的なインターフェースを使う時には問題を引き起こす可能性があります。インターフェースの名前でフィルタすることは可能ですが、整数でのマッチの代わりに文字列でのマッチを行うため、パフォーマンスコストとなります。インターフェースの名前でフィルタにするにはoifnameを使います:

# nft insert rule filter input oifname ppp0 accept

ロギング

ログをとるにはlogキーワードを使います。典型的なログと受け入れるルールは以下のようになります:

# nft add rule filter input tcp dport 22 ct state new log prefix \"SSH for ever\" group 2 accept

nftablesでは、iptablesだと2つになるような(NFLOGとACCEPT)ルールを1つのルールで書けます。もしprefixが単なる標準的な前置きのオプションであれば、groupオプションはnfnetlink_logグループを含み、このモードはロギングのフレームワークとして用いられます。

事実として、nftablesでのログはNetfilterのロギングのフレームワークを利用しています。つまり、ロギングはロードされたカーネルモジュールに依存するということです。利用可能なカーネルモジュールは:

  • xt_LOG:printkベースのログで、全てをsyslogに出力する(iptablesのLOGターゲットで用いられるものと同じモジュール)
  • nfnetlink_log:netlinkベースのログで、イベント取得のためにulogd2を設定する必要がある(iptablesのNFLOGターゲットで用いられるものと同じモジュール)

2つのモジュールのどちらかを使うには、modprobeでロードします。

各プロトコルを基準にログの設定が出来ます。設定は/procで可能:

# cat /proc/net/netfilter/nf_log 
 0 NONE (nfnetlink_log)
 1 NONE (nfnetlink_log)
 2 nfnetlink_log (nfnetlink_log,ipt_LOG)
 3 NONE (nfnetlink_log)
 4 NONE (nfnetlink_log)
 5 NONE (nfnetlink_log)
 6 NONE (nfnetlink_log)
 7 nfnetlink_log (nfnetlink_log)
 8 NONE (nfnetlink_log)
 9 NONE (nfnetlink_log)
10 nfnetlink_log (nfnetlink_log,ip6t_LOG)
11 NONE (nfnetlink_log)
12 NONE (nfnetlink_log)

これでnfnetlink_logが最初にロードされulogdが起動します。例として、IPv4(リスト中の2番)にipt_LOGを使いたい場合は、以下で可能です:

# echo "ipt_LOG" >/proc/sys/net/netfilter/nf_log/2

これでIPv4にipt_LOGによるロギングが有効になります:

# cat /proc/net/netfilter/nf_log 
 0 NONE (nfnetlink_log)
 1 NONE (nfnetlink_log)
 2 ipt_LOG (nfnetlink_log,ipt_LOG)
 3 NONE (nfnetlink_log)
 4 NONE (nfnetlink_log)
 5 NONE (nfnetlink_log)
 6 NONE (nfnetlink_log)
 7 nfnetlink_log (nfnetlink_log)
 8 NONE (nfnetlink_log)
 9 NONE (nfnetlink_log)
10 nfnetlink_log (nfnetlink_log,ip6t_LOG)
11 NONE (nfnetlink_log)
12 NONE (nfnetlink_log)

もしちょっとしたテストのために使いたいだけなら、nfnetlink_logの前にxt_LOGを単にロードすれば、IPv4とIPv6プロトコルに対してロギングが可能になります。

単一のチェインを使う

チェインはユーザが定義し、色々な方法でアレンジできます。例えば、1台の箱なら、入力用に単一のチェインを使うこともあり得ます。onechainというファイルを以下の内容で作成します:

#! nft -f

table global {
        chain one { 
                type filter hook input priority   0;
        }
}

それから実行します。

# nft -f onchain

そして以下のようにルールを追加することが可能です:

# nft add rule ip global one ip daddr 192.168.0.0/24

この設定の良いところは箱にパケットが届いた時にだけNetfilterのフィルタリングが有効になることです。

セット

名前無しセットは以下の構文で使えます:

# nft add rule ip filter output ip daddr {192.168.1.1, 192.168.1.4} drop

名前付きセットはファイルに記述出来ます。例えば、simpleというファイルを作成できます:

define ip_set = {192.168.1.2, 192.168.2.3}
add rule filter output ip daddr $ip_set counter

それから実行します。

# nft -f simple

名前付きセットを使うことも出来ます。IPv4アドレスを含むセットを1つ定義します:

# nft add set filter ipv4_ad { type ipv4_addr\;}

セットに要素を追加するには:

# nft add element filter ipv4_ad { 192.168.3.4 }
# nft add element filter ipv4_ad { 192.168.1.4, 192.168.1.5 }

セットの一覧は:

# nft list set filter ipv4_ad

そして、ルール中でセットを使えます:

# nft add rule ip filter input ip saddr @ipv4_ad drop

既存のセットから要素を取り除けます:

# nft delete element filter ipv4_ad { 192.168.1.5 }

セットを削除するには:

# nft delete set filter ipv4_ad

マッピング

マッピングは辞書のように振る舞う特定のタイプのセットです。例えばIPv4のアドレスをあう判定にマップすることが出来ます:

# nft -i
nft> add map filter verdict_map { type ipv4_address : verdict; }
nft> add element filter verdict_map { 1.2.3.5 : drop}
nft> add element filter verdict_map { 1.2.3.4 : accept}

nft> add rule filter output ip daddr vmap @verdict_map

マッピングの要素を1つ削除するには、セットの操作と同じ構文で可能です:

nft> delete element filter verdict_map 1.2.3.5

セットを削除するには:

# nft delete set filter verdict_map

マッピングは名前を付けない方法でも使えます:

# nft add rule filter output ip daddr vmap {192.168.0.0/24 : drop, 192.168.0.1 : accept}

特定のマッピングを一覧するには:

# nft list set filter nat_map -n

NAT

何より最初にNATモジュールが必要です:

# modprobe nft_nat

次にカーネルがプロトコルに(ここではIPv4)NATを使うことを通知しないといけません:

# modprobe nft_chain_nat_ipv4

これでNAT専用のチェインを作成できます:

# nft add table nat
# nft add chain nat post { type nat hook postrouting priority 0 \; }
# nft add chain nat pre { type nat hook prerouting priority 0 \; }

NATのルールを追加できます:

# nft add rule nat post ip saddr 192.168.56.0/24 oif wlan0 snat 192.168.1.137
# nft add rule nat pre udp dport 53 ip saddr 192.168.56.0/24 dnat 8.8.8.8:53

1つ目は192.168.56.0/24からのwlan0インターフェースへの外向きトラフィックを全てIPアドレス192.168.1.137にNATします。2つ目は192.168.56.0/24からの全てのDNSトラフィックを8.8.8.8サーバにリダイレクトします。アドレスレンジへのNATも可能です:

# nft add rule nat post ip saddr 192.168.56.0/24 oif wlan0 snat 192.168.1.137-192.168.1.140

IPv6のNATも可能です。最初にIPv6用のNAT機能を定義するモジュールをロードする必要があります:

# modprobe nft_chain_nat_ipv6

終われば、以下のようにルールを追加できます:

table ip6 nat {
    chain postrouting {
        type nat hook postrouting priority -150; 
        ip6 saddr 2::/64 snat 1::3;
    }
}

基本のルールセットを作り上げる

以下のルールセットはIPv4とIPv6であるラップトップを保護するための典型的なルールセットです:

# IPv4 filtering
table Filter {
        chain Input {
                 type filter hook input priority 0;
                 ct state established accept
                 ct state related accept
                 iif lo accept
                 tcp dport ssh counter accept
                 counter log drop
        }

        chain Output {
                 type filter hook output priority 0;
                 ct state established accept
                 ct state related accept
                 oif lo accept
                 ct state new counter accept
        }
}
#IPv6 filtering
table ip6 Filter {
        chain Input {
                 type filter hook input priority 0;
                 ct state established accept
                 ct state related accept
                 iif lo accept
                 tcp dport ssh counter accept
                 icmpv6 type { nd-neighbor-solicit, echo-request, nd-router-advert, nd-neighbor-advert } accept
                 counter log drop
        }

        chain Output {
                 type filter hook output priority 0;
                 ct state established accept
                 ct state related accept
                 oif lo accept
                 ct state new counter accept
        }

}