CUBE SUGAR CONTAINER

技術系のこと書きます。

systemd で nftables の設定を永続化する

今回は nftables のスクリプトを systemd から読み込むことで設定を永続化する方法について。 結論から述べると systemctl cat nftables で読み込んでいるファイルの場所を確認したら、そこにルールを書けば良い。

使った環境は次のとおり。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04.2 LTS
Release:    24.04
Codename:   noble
$ uname -srm
Linux 6.8.0-59-generic x86_64
$ nft --version
nftables v1.0.9 (Old Doc Yak #3)

もくじ

下準備

まずは nftables をインストールする。 一般的な環境であれば最初から入っているはず。

$ sudo apt-get -y install nftables

UFW (Uncomplicated Firewall) は、nftables と同時に利用すると競合しやすい。 そのため、もし使っている場合には無効にする。

$ sudo systemctl stop ufw
$ sudo systemctl disable ufw

そして、systemd で nftables のサービスを動かす。

$ sudo systemctl start nftables
$ sudo systemctl enable nftables

設定ファイルの場所を確認する

まずは nftables のサービスが、どこの設定ファイルを読むのか確認する。 systemctl cat でユニットファイルの内容を見るのが手っ取り早い。

$ systemctl cat nftables | grep -i ^exec
ExecStart=/usr/sbin/nft -f /etc/nftables.conf
ExecReload=/usr/sbin/nft -f /etc/nftables.conf
ExecStop=/usr/sbin/nft flush ruleset

上記から /etc/nftables.conf を読んでいることが確認できる。

デフォルトの設定を確認する

先ほど確認した設定ファイルの内容を見てみよう。 すると input, forward, output hook に base チェインが設定されている。 単なる入れ物が用意されているだけで、すべての通信が accept される状態になっている。

$ cat /etc/nftables.conf 
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter;
    }
    chain forward {
        type filter hook forward priority filter;
    }
    chain output {
        type filter hook output priority filter;
    }
}

上記の設定ファイルの内容がシステムに反映されているか確認してみよう。

nft list ruleset コマンドを実行すると、先ほどの設定ファイルと同じ内容が確認できる。

$ sudo nft list ruleset
table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;
    }

    chain forward {
        type filter hook forward priority filter; policy accept;
    }

    chain output {
        type filter hook output priority filter; policy accept;
    }
}

これは、先ほど systemd の nftables サービスを開始したため設定が読み込まれている。

nftables の設定ファイルを編集してみる

続いては systemd のサービスが読んでいる nftables の設定ファイルを編集してみよう。

以下では /etc/nftables にディレクトリを作って、そこに nftables のスクリプトを用意している。 内容は基本的な設定を入れたファイアウォールになっている。

$ sudo mkdir -p /etc/nftables
$ cat << 'EOF' | sudo tee /etc/nftables/simple-firewall.nft
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
   chain input {
      type filter hook input priority 0; policy drop;
      # 関連・確立済みのコネクションは通す
      ct state established,related accept
      # 不正なコネクションは落とす
      ct state invalid drop
      # ループバックは通す
      iif lo accept
      # ICMPv4 の特定タイプは通す
      ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
      # ICMPv6 の特定タイプは通す
      ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
      # SSH (TCP/22) はレートリミットつきで通す
      tcp dport ssh limit rate 10/minute accept
   }

   chain forward {
      type filter hook forward priority 0; policy drop;
   }

   chain output {
      type filter hook output priority 0; policy accept;
   }

}
EOF

上記の設定を /etc/nftables.conf から include する。

$ cat << 'EOF' | sudo tee /etc/nftables.conf >/dev/null
#!/usr/sbin/nft -f

flush ruleset

include "/etc/nftables/simple-firewall.nft"
EOF

この状態で systemd の nftables サービスをリロードする。

$ sudo systemctl reload nftables

すると、先ほどのファイルに書いた内容が動作に反映される。

$ sudo nft list ruleset
table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;
        ct state established,related accept
        ct state invalid drop
        iif "lo" accept
        ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
        ip6 nexthdr ipv6-icmp icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
        tcp dport 22 limit rate 10/minute burst 5 packets accept
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
    }

    chain output {
        type filter hook output priority filter; policy accept;
    }
}

再起動して設定が永続化されていることを確認する

念の為、システムを再起動しても設定が反映され直すことを確認しておこう。

$ sudo shutdown -r now

再起動が終わったら、もう一度ログインして nftables の設定を確認する。

$ sudo nft list ruleset
table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;
        ct state established,related accept
        ct state invalid drop
        iif "lo" accept
        ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
        ip6 nexthdr ipv6-icmp icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
        tcp dport 22 limit rate 10/minute burst 5 packets accept
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
    }

    chain output {
        type filter hook output priority filter; policy accept;
    }
}

ちゃんと、先ほどと同じ設定が読み込まれていることが確認できる。

めでたしめでたし。