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への移行 も参考にしてください。