前回、firewalld か iptables か という記事で「Rocky 9 ではどちらも下は nftables なので、これから低レベルを学ぶなら iptables 構文より nft を覚えた方が将来性がある」と書きました。今回はその続きとして、nftables を素のまま書いてみる入門です。
iptables に慣れた人ほど最初は文法に戸惑いますが、モデル自体はむしろシンプルで一貫しています。概念を押さえてから、ゼロから SSH/HTTP を通す実用ルールを書いて永続化し、firewalld を止めて nft に一本化するところまでやります。
- Rocky Linux 9.x が動作するサーバ
- sudo 権限
- SSH 以外のコンソール手段(VPS のコンソール等)。設定ミスで締め出される事故を避けるため
- firewalld か iptables か を先に読んでおくと背景が分かりやすい
1. モデルを「鉄道路線」で理解する
nftables の文法に入る前に、そもそもファイアウォールがパケットをどう捌いているかを掴んでおきましょう。ここを 鉄道の路線図にたとえると、iptables も nftables も一気に見通しが良くなります。
| たとえ | 実体 | nft / iptables の用語 |
|---|---|---|
| 乗客 | 1つのパケット | packet |
| 路線(通る駅の並び) | パケットの通り道 | hook の経路 |
| 駅 | パケットを評価する地点 | chain(INPUT 等) |
| 駅の改札の係員リスト | 上から順に照合する判定 | rule |
| 改札の既定対応 | どれにも当たらなかったときの動作 | policy |
ポイントは、路線(通る駅)は乗客の行き先で変わること。すべての駅に停まるわけではありません。
flowchart LR
IN["パケット到着"] --> PRE["PREROUTING 駅"]
PRE --> J{"宛先は<br/>自ホスト?"}
J -->|自分宛| INP["INPUT 駅"] --> APP["ローカルプロセス<br/>(sshd 等)"]
J -->|通過するだけ| FWD["FORWARD 駅"] --> POST["POSTROUTING 駅"] --> OUTNIC["外へ"]
APP --> OUT["OUTPUT 駅"] --> POST- 外から SSH で入ってくるパケット →
PREROUTING駅 → INPUT駅を通ってローカルの sshd へ。FORWARD 駅には停まらない - サーバが素通りさせるだけのパケット →
PREROUTING駅 → FORWARD駅 → POSTROUTING駅 - サーバ自身が出す応答 →
OUTPUT駅 → POSTROUTING駅
そして 各駅の改札(rule)は上から順に照合し、当たったらそこで判定が確定します。
accept… 通してOK、次の駅へdrop… ここで降ろして終了(黙って破棄)reject… 「お断り」と通知して終了- どの改札にも当たらなければ、最後に
policy(駅の既定対応) が適用される
公開サーバで主に整備するのは INPUT 駅(自分宛の着信をどう通すか)です。
iptables と nftables の違いは「路線が敷設済みか、自分で引くか」だけ
ここまでの「駅」と「改札」は、iptables にも nftables にもまったく同じ仕組みで存在します。違うのは一点だけ。
- iptables … 路線図(どの駅にどの順で停まるか)が最初から敷設済みで見えない。
INPUTという名前を使えば、自動的に「INPUT 駅」に置かれる - nftables … その路線を自分で宣言して引く。だから登場する概念が増えるわけではなく、iptables が裏で勝手にやっていたことが表に出てくるだけ
nft でベースチェイン(駅)を作るときは、必ず type / hook / priority / policy を書きます。
type filter hook input priority 0; policy drop;
│ │ │ └ 改札の既定対応(マッチしなければ drop)
│ │ └ 同じ駅に複数の改札があるときの順番(小さいほど先)
│ └ どの駅か(input/forward/output/prerouting/postrouting)
└ 駅の種類(filter / nat / route)つまり hook input は「INPUT 駅を作る」、priority 0 は「その駅での改札の並び順」の宣言。iptables が最初から敷いていた路線を、自分の手で描いているだけ——と分かれば、もう怖くありません。
2. 現状を確認する
まず Rocky 9 の今の状態を見ます。標準では firewalld が動いていて、その実体が nftables のルールとして入っています。
# 現在 firewalld が動いているか
systemctl is-active firewalld
# nftables の「実体」を覗く(firewalld が入れたルールが見える)
sudo nft list ruleset | head -40firewalld が動いている間は、ここに firewalld 由来の長いルールが並びます。これを自分の手書きルールに置き換えるのがこの記事のゴールです。
この先はロックアウト事故に直結します。 policy drop のチェインを、SSH を許可する前に適用すると即座に締め出されます。必ず (1) SSH 許可ルールを含んだ状態で適用し、(2) 今の SSH セッションは開いたまま別セッションで疎通確認し、(3) VPS のコンソールをいつでも開けるようにしておいてください。
3. ルールセットを書く
nftables の設定は1ファイルにまとめて書けます。Rocky 9 の nftables.service は /etc/sysconfig/nftables.conf を読み込むので、ここに書きます。
sudo cp /etc/sysconfig/nftables.conf /etc/sysconfig/nftables.conf.bak
sudo vi /etc/sysconfig/nftables.conf最小構成の実用ルールがこちらです。inet ファミリにすると IPv4 と IPv6 をまとめて扱えます。
#!/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 近隣探索など
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# 公開するサービス(SSH を必ず含めること!)
tcp dport { 22, 80, 443 } counter accept
}
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を input の先頭付近に置く。これが無いと、こちらから出した通信の応答まで drop されて「繋がらない」状態になりますtcp dport { 22, 80, 443 }の{ }は集合(set)。複数ポートを 1 行で書けますcounterを付けると、そのルールに当たったパケット数がnft list rulesetに表示されます。「どのルールが効いているか」を一覧で追える、nft 流の見やすさですpolicy dropなので、明示的に許可した以外は全部落ちます
SSH のポートを標準の 22 から変えている場合は、22 をそのポート番号に必ず置き換えてください。ここを間違えると次の適用で締め出されます。
4. 適用して動作確認
文法チェック → 適用 → 確認の順で進めます。
# 文法だけ検査(-c はチェックのみ、適用しない)
sudo nft -c -f /etc/sysconfig/nftables.conf
# 問題なければ適用
sudo nft -f /etc/sysconfig/nftables.conf
# 効いているルールを確認(-a で handle 番号も表示)
sudo nft -a list rulesettable inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
...
tcp dport { 22, 80, 443 } counter packets 128 bytes 9216 accept # handle 9
}
}counter packets ... のように、当たった回数が見えます。ここで 必ず別の端末から SSH で入り直せることを確認してください。入れなければ、開いたままの今のセッションで設定を戻します。
5. firewalld を止めて nftables に一本化
疎通が確認できたら、firewalld を停止して nftables.service に寄せます。前回書いたとおり、両者を同時に動かすと衝突するので、片方に寄せるのが鉄則です。
# firewalld を停止・無効化
sudo systemctl disable --now firewalld
# nftables を有効化(起動時に nftables.conf を自動ロード)
sudo systemctl enable --now nftables
# 状態確認
systemctl is-enabled nftables firewalldenabled
disabledこれで再起動後も、/etc/sysconfig/nftables.conf の内容がそのまま復元されます。firewalld 由来のルールはもう出てこず、nft list ruleset に映るのは自分が書いた分だけになります。
6. 運用でよく使う操作
設定ファイルを編集して nft -f で読み直すのが基本ですが、一時的な追加・削除も覚えておくと便利です。
# 一時的にルールを足す(例: 8080 を開ける)
sudo nft add rule inet filter input tcp dport 8080 counter accept
# handle 番号を調べて、その行だけ消す
sudo nft -a list chain inet filter input
sudo nft delete rule inet filter input handle 9nft add での変更はその場限り(再起動で消える)。恒久化したいときは必ず /etc/sysconfig/nftables.conf 側にも反映してください。「ファイルが正本、nft add は実験」と割り切るのが安全です。
IP 単位の許可リスト/ブロックリスト(fail2ban 的な使い方)には named set が便利です。これは別記事で掘り下げます。
set blocklist {
type ipv4_addr
flags interval
}
chain input {
ip saddr @blocklist drop
# ...
}まとめ
- nftables の登場人物は table / chain / rule の3つだけ。組み込みが無いぶん「効いている設定=自分が書いた全部」で見通しが良い
- ベースチェインは type / hook / priority / policy を宣言する。公開サーバは主に input フック
- 実用ルールは
ct state established,related acceptを先頭に、SSH を必ず許可してからpolicy drop - 設定は
/etc/sysconfig/nftables.confに書いてsystemctl enable nftablesで永続化 - firewalld は 止めて一本化。混在は厳禁
counterを付ければ、どのルールが効いているか一覧で追える
iptables 構文を覚え直すより、最初から nft で書いた方が「実体をそのまま触っている」感覚があって、個人的にはこちらの方が見通しが良いと感じています。



