2026年5月28日

2026年5月28日

nftablesでLinuxサーバーのファイアウォールを設定する方法(設定ファイル・SSH締め出し対策つき)

nftables は Linux カーネルのパケットフィルター(netfilter)を操作する仕組みで、iptables の後継です。Debian 10 以降、Ubuntu 20.10 以降、RHEL 8 以降(AlmaLinux・Rocky Linux を含む)では、iptables コマンドや firewalld も内部では nftables を使っています。

この記事では、WordPress を動かす1台の Web サーバーを例に、nft コマンドと設定ファイルで「外から入ってくる通信は原則拒否し、SSH・HTTP・HTTPS だけ許可する」ファイアウォールを作ります。

作業の前に確認すること

  • ファイアウォールの管理ツールは1つにする。 ufw や firewalld が動いている状態で nftables のルールを足すと、パケットは両方のルールで判定され、どちらで止まったのか分かりにくくなります。nftables を直接使う場合は、先に止めます。
# Ubuntu で ufw を使っている場合
sudo ufw status
sudo ufw disable

# RHEL 系で firewalld を使っている場合
sudo systemctl disable --now firewalld
  • Docker を使っているサーバーでは、この記事の設定をそのまま使わない。 Docker は独自のルールを追加し、コンテナへの通信は forward(転送)として処理されます。後で作る forward チェーンを既定で拒否にするとコンテナに届かなくなり、flush ruleset は Docker が作ったルールも消します。
  • コンソールで入れる手段を用意する。 VPS ならコントロールパネルのコンソール(VNC・シリアルコンソール)が使えることを確かめておきます。SSH のルールを間違えると、SSH では戻れなくなります。
  • 今のルールを保存しておく。
sudo nft list ruleset | sudo tee /root/nft-before.nft

nftables をインストールして有効にする

# Debian / Ubuntu
sudo apt update
sudo apt install nftables

# RHEL / AlmaLinux / Rocky Linux
sudo dnf install nftables

起動時にルールを読み込む設定ファイルは、ディストリビューションで場所が違います。

ディストリビューション 起動時に読み込まれるファイル
Debian / Ubuntu /etc/nftables.conf
RHEL / AlmaLinux / Rocky Linux /etc/sysconfig/nftables.conf(ここから /etc/nftables/ 内のファイルを include する)

以下では Debian / Ubuntu の /etc/nftables.conf を例にします。RHEL 系では同じ内容を /etc/nftables/server.nft などに書き、/etc/sysconfig/nftables.conf に include "/etc/nftables/server.nft" を追加してください。

nftables の基本の考え方

nftables のルールは「テーブル → チェーン → ルール」の3段で書きます。

要素 役割 この記事での値
テーブル ルールをまとめる入れ物。アドレスファミリーを指定する inet filter(inet は IPv4 と IPv6 の両方を扱う)
チェーン ルールの並び。hook を付けるとカーネルの処理位置に結び付く input(自分宛て)、forward(転送)、output(自分から外へ)
ポリシー どのルールにも一致しなかったパケットの扱い input と forward は drop、output は accept
ルール 条件と動作。上から順に評価される tcp dport 22 accept など

iptables と違い、nftables は最初はテーブルもチェーンも空です。nft add table を実行しただけではパケットは止まらず、hook を持つチェーン(ベースチェーン)を作って初めてフィルターが働きます。

設定ファイルを書く

/etc/nftables.conf を次の内容にします(既存の内容は上で保存済みです)。

#!/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

        # ping と IPv6 の近隣探索(ICMPv6 を止めると IPv6 が使えなくなる)
        ip protocol icmp accept
        meta l4proto ipv6-icmp accept

        # SSH(新規接続を1分あたり15回までに制限)
        tcp dport 22 ct state new limit rate 15/minute accept

        # Web
        tcp dport { 80, 443 } accept

        # 拒否したパケットの一部をカーネルログに記録
        limit rate 5/minute log prefix "nft-input-drop: "
    }

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

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

ポイントは次のとおりです。

  • ct state established,related accept を先頭近くに置く。 サーバーから外へ出た通信(DNS の問い合わせ、apt の更新、外部 API への接続など)の応答は、この行で受け入れられます。DNS サーバーを自分で動かしていない限り、受信側の 53 番ポートを開ける必要はありません。
  • ルールは上から順に評価される。 一致して accept か drop になった時点で、そのチェーンの評価は終わります。
  • 最後の log 行は通過したパケットを止めない。 ログに書いた後、ポリシーの drop で破棄されます。
  • SSH のポートを変更している場合は 22 をそのポート番号に直します。

管理用の通信を特定の IP アドレスに限る

MySQL(3306)や FTP(21)をインターネット全体に開けるのは避けます。データベースに別のサーバーから接続する必要がある場合は、接続元を指定します。

        # 例: 203.0.113.10 からだけ MySQL を許可
        ip saddr 203.0.113.10 tcp dport 3306 accept

SSH を会社や自宅の固定 IP アドレスからだけ許可する場合は、SSH の行を次のように置き換えます。

        ip saddr { 203.0.113.10, 198.51.100.0/24 } tcp dport 22 accept

ファイル転送には FTP ではなく SFTP(SSH の 22 番ポートを使う)を使えば、追加のポートは不要です。

締め出しを防ぎながら反映する

1. 構文を確認する

sudo nft -c -f /etc/nftables.conf

-c はルールを読み込まずに確認だけ行います。何も表示されなければ構文は正しい状態です。

2. 自動で元に戻すタイマーを仕掛ける

SSH で作業している場合は、反映する前に「5分後にルールを全部消す」タイマーを systemd に登録しておきます。設定が間違っていて接続が切れても、5分待てば入り直せます。

sudo systemd-run --on-active=5min /usr/sbin/nft flush ruleset

実行すると Running timer as unit: run-u123.timer のように単位名が表示されるので控えておきます。

3. ルールを読み込む

sudo nft -f /etc/nftables.conf

4. 別の端末から接続を確かめる

今の SSH セッションは閉じずに、新しい端末から SSH でログインできること、ブラウザーでサイトが表示されることを確かめます。

5. タイマーを止めて、起動時に読み込むようにする

問題がなければ、手順2で表示された名前のタイマーを止めます。

sudo systemctl stop run-u123.timer
sudo systemctl enable --now nftables

nftables サービスを有効にすると、再起動後も /etc/nftables.conf が読み込まれます。

接続できなくなった場合は、5分待ってから入り直すか、VPS のコンソールから sudo nft flush ruleset を実行してください。保存しておいた元のルールに戻すには、sudo nft flush ruleset の後に sudo nft -f /root/nft-before.nft を実行します。

ルールを確認・追加・削除する

現在のルール全体を表示します。

sudo nft list ruleset

一時的にルールを追加する場合は nft add rule を使います(例: 8080 番ポートを開ける)。add はチェーンの末尾に、insert は先頭に追加します。

sudo nft add rule inet filter input tcp dport 8080 accept

ルールを削除するには、-a で各ルールの番号(handle)を表示し、その番号を指定します。

sudo nft -a list chain inet filter input
# 出力の例: tcp dport 8080 accept # handle 12
sudo nft delete rule inet filter input handle 12

nft add や nft delete で変えたルールは、再起動や systemctl restart nftables で消えます。 残したいルールは /etc/nftables.conf に書き、構文を確認してから sudo nft -f /etc/nftables.conf で読み込み直してください。

拒否されたパケットを調べる

設定ファイルの最後に入れた log 行の記録は、カーネルログに残ります。

sudo journalctl -k | grep nft-input-drop

出力の SRC=(送信元)、DPT=(宛先ポート)、PROTO=(プロトコル)を見ると、どの通信が拒否されたかが分かります。必要な通信が拒否されていれば、そのポートの許可ルールを追加します。

特定のルールに一致した数を知りたい場合は、ルールに counter を付けます。

        tcp dport { 80, 443 } counter accept

sudo nft list ruleset を実行すると、counter packets 1234 bytes 567890 のように一致した数が表示されます。

よくあるつまずき

症状 原因と対処
ルールを読み込んだ直後に SSH が切れた SSH の許可ルールが無い、またはポート番号が違う。タイマーで戻るのを待つか、コンソールから nft flush ruleset
サーバーから外部への接続(apt update、DNS)ができない ct state established,related accept が無い、または output のポリシーを drop にしている
IPv6 で接続できない ICMPv6 を許可していない。meta l4proto ipv6-icmp accept を入れる
再起動後にルールが消えた nftables サービスが無効、または nft add で追加しただけで設定ファイルに書いていない
Docker のコンテナに外から届かない forward チェーンを drop にしている、または flush ruleset で Docker のルールが消えた

まとめ

  • nftables では、hook 付きのチェーンにポリシーを設定して初めてパケットが止まります。
  • 受信は既定で拒否し、ct state established,related accept と、SSH・HTTP・HTTPS など必要なポートだけを許可します。
  • ルールは /etc/nftables.conf に書き、nft -c -f で確認してから、自動ロールバックのタイマーを仕掛けて反映します。

iptables のルールから移行する場合は iptablesからnftablesへの移行 も参考にしてください。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲