2026年8月11日
2026年8月11日
iptablesでポートを開放・閉鎖する方法(ルールの順番・永続化・SSH締め出し対策)
iptables でポートを開けるには iptables -I INPUT -p tcp --dport 番号 -j ACCEPT、閉じるにはそのルールを -D で削除するか DROP のルールを入れます。ただし、ルールの順番を間違えると開けたつもりのポートが開かず、デフォルトポリシーの変更を誤ると SSH で入れなくなります。この記事では、今の環境が iptables と nftables のどちらで動いているかの確認から、ポートの開放・閉鎖、再起動後も残す永続化、締め出しを防ぐ手順までを説明します。
最初に確認すること:iptablesのバックエンドと管理ツール
現在の主要なディストリビューションでは、iptables コマンドの中身が nftables になっていることがほとんどです。次のコマンドで確認できます。
iptables -V
# iptables v1.8.x (nf_tables) ← nftables バックエンド(iptables-nft)
# iptables v1.8.x (legacy) ← 従来のバックエンド
(nf_tables) と表示される場合、iptables の書式で書いたルールは内部で nftables のルールに変換されて登録されます。iptables のコマンドはそのまま使えますが、nft list ruleset でも同じルールが見えます。注意したいのは、legacy と nf_tables のルールを混在させないことです。両方にルールがあると、どちらが効いているのか分かりにくくなります。Debian/Ubuntu では update-alternatives --config iptables で切り替えられますが、特別な理由がなければ既定のままにします。
次に、firewalld や ufw が動いていないかを確認します。これらは iptables(または nftables)のルールを自分で管理するツールで、横から iptables コマンドで追加したルールは、ツールの再読み込みや再起動のたびに消えたり競合したりします。
systemctl is-active firewalld
sudo ufw status
firewalld や ufw が有効なら、ポートの開閉はそれぞれのコマンドで行うのが正解です(後述)。この記事の iptables の手順は、どちらも使っていないサーバーを前提にしています。
現在のルールを確認する
# 番号付きで INPUT チェーンを表示
sudo iptables -L INPUT -n -v --line-numbers
# 登録したときの書式で表示(設定の控えに便利)
sudo iptables -S
-n は IPアドレスやポートを名前解決せずに数字で表示するオプションで、表示が速くなります。-v を付けるとルールごとのパケット数(pkts)が出るので、ルールが実際に使われているかを確かめられます。出力の先頭にある policy ACCEPT や policy DROP は、どのルールにも一致しなかったパケットの扱い(デフォルトポリシー)です。
iptables はルールを上から順に評価し、最初に一致したルールで処理を決めます。この「上から順に、最初の一致で決まる」という性質が、ポートの開閉でつまずく原因のほとんどを占めます。
ポートを開放する
作業の前に、現在のルールを保存しておきます。
sudo iptables-save > ~/iptables-backup-$(date +%Y%m%d).rules
HTTP(80番)と HTTPS(443番)を開ける例です。
# 1つのポートを開ける(先頭に挿入)
sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT
# 複数のポートをまとめて開ける
sudo iptables -I INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
# 送信元を限定して開ける(例: 管理用の 8443 番を特定のIPだけに)
sudo iptables -I INPUT -p tcp -s 203.0.113.10 --dport 8443 -j ACCEPT
ここで -A(末尾に追加)ではなく -I(挿入)を使っているのが要点です。多くのサーバーでは、INPUT チェーンの最後に「残りはすべて拒否する」REJECT や DROP のルールが入っています。-A で追加するとその拒否ルールより後ろに入るため、パケットは先に拒否されてしまい、ポートは開きません。-I INPUT は番号を省略すると先頭に入り、-I INPUT 5 のように番号を付けるとその位置に入ります。
開けたら、サービス側が待ち受けているかと、外から届くかを確認します。
# サーバー上で待ち受けを確認
sudo ss -tlnp | grep -E ':80 |:443 '
# 別のマシンから到達を確認
nc -zv example.com 443
ss に表示されないなら、ファイアウォールではなくサービス(Nginx や Apache)の設定が原因です。また、VPS やクラウドでは、サーバーの外側にもファイアウォール(AWS のセキュリティグループや、VPS 管理画面のパケットフィルター)があります。iptables で開けても届かない場合は、そちらの設定も確認してください。
ポートを閉鎖する
開けたポートを閉じるには、そのルールを削除します。番号で消す方法と、登録時と同じ条件で消す方法があります。
# 番号を確認してから、番号で削除
sudo iptables -L INPUT -n --line-numbers
sudo iptables -D INPUT 3
# 登録時と同じ条件を書いて削除
sudo iptables -D INPUT -p tcp --dport 8080 -j ACCEPT
番号で削除すると、それより下のルールの番号が1つずつ繰り上がります。続けて複数のルールを番号で消すときは、毎回一覧を表示し直すか、番号の大きい方から消すと誤削除を防げます。
デフォルトポリシーが ACCEPT のサーバーでは、ACCEPT のルールを消しただけではポートは閉じません。その場合は拒否のルールを明示的に入れます。
# 3306番(MySQL)への外部からの接続を拒否
sudo iptables -I INPUT -p tcp --dport 3306 -j DROP
DROP はパケットを黙って捨て、相手側はタイムアウトまで待たされます。REJECT は拒否したことを相手に返すので、接続はすぐに失敗します。外部への応答を減らしたいなら DROP、社内ネットワークなどで原因を分かりやすくしたいなら REJECT、という使い分けが一般的です。
IPv6 は ip6tables で別に設定する
iptables のルールは IPv4 にしか効きません。サーバーに IPv6 アドレスがあると、IPv4 で閉じたポートが IPv6 では開いたままになっていることがあります。同じ内容を ip6tables でも設定してください。
SSHで締め出されないための手順
リモートのサーバーで最も避けたいのが、自分の SSH 接続まで遮断してしまうことです。ルールが空の状態から組み立てる場合、安全側の基本形は次の順番です。
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
sudo iptables -P INPUT DROP
ループバック、確立済みの通信、SSH を先に許可し、デフォルトポリシーを DROP にするのは最後です。順番を逆にして先に -P INPUT DROP を実行すると、その時点で接続が切れます。SSH のポートを変更している場合は 22 を実際の番号に置き換えます。
もう一つの落とし穴が iptables -F(全ルールの削除)です。-F はデフォルトポリシーを戻さないため、ポリシーが DROP のまま -F を実行すると、すべての通信が遮断されます。ルールを一から作り直すときは、先に iptables -P INPUT ACCEPT に戻してから -F を実行します。
さらに安全に進めるには、一定時間後に元のルールへ戻る仕掛けを用意してから変更します。Debian/Ubuntu の iptables パッケージには、ルールファイルを適用して確認がなければ自動で戻す iptables-apply が含まれています。
# ルールファイルを適用し、60秒以内に確認しなければ元に戻す
sudo iptables-apply -t 60 /etc/iptables/rules.v4
iptables-apply が使えない環境では、変更前に「5分後にバックアップから戻す」処理を裏で動かしておき、新しい SSH セッションで接続できることを確認してから止める方法があります。また、作業中は今の SSH セッションを閉じず、別のターミナルから新しく接続できるかを試してください。VPS の管理画面にあるコンソール機能の使い方も、事前に確認しておくと安心です。
再起動後もルールを残す(永続化)
iptables コマンドで追加したルールはメモリ上にしかなく、再起動すると消えます。
Debian/Ubuntu では iptables-persistent パッケージを使います。インストール時に現在のルールを保存するか聞かれ、以後は netfilter-persistent がブート時に読み込みます。
sudo apt install -y iptables-persistent
# ルールを変更したら保存
sudo netfilter-persistent save
# → /etc/iptables/rules.v4 と /etc/iptables/rules.v6 に書き出される
RHEL 系(Rocky Linux、AlmaLinux など)では firewalld が標準です。iptables のルールファイルで管理したい場合は iptables-services 系のパッケージを入れて firewalld を止める必要があり、パッケージ名や扱いはバージョンによって異なります(2026年10月現在、RHEL 9 以降では iptables 自体が非推奨扱いです)。特別な理由がなければ、RHEL 系では firewalld を使う方が保守しやすいでしょう。
firewalld・ufw・nftablesを使っている場合
firewalld や ufw が有効なサーバーでは、次のように各ツールのコマンドで開閉します。
# firewalld
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --remove-port=8080/tcp
sudo firewall-cmd --reload
# ufw
sudo ufw allow 8080/tcp
sudo ufw delete allow 8080/tcp
これから新しく設計するなら、nftables を直接使う選択肢もあります。手元の iptables のルールを nftables の書式で確認したいときは、iptables-translate に iptables のコマンドを渡すと対応する nft のコマンドが表示されます。
Docker を使っているサーバーでは、Docker がコンテナの公開ポート用に独自のルールを追加します。公開したポートは INPUT チェーンを通らないため、INPUT で閉じても外から届くことがあります。コンテナの通信を制限する場合は DOCKER-USER チェーンにルールを入れてください。
まとめ
iptables -Vでバックエンド(nf_tables か legacy)を確認し、firewalld や ufw が動いているならそちらで操作します。- ルールは上から順に評価されるため、ポートを開けるときは
-Aではなく-Iで拒否ルールより前に入れます。 - 閉じるときは
-Dで削除し、ポリシーが ACCEPT なら DROP のルールを明示します。IPv6 はip6tablesで別に設定します。 - 締め出しを防ぐため、SSH を許可してから
-P INPUT DROPにし、iptables-applyや自動復元の仕掛け、別セッションでの接続確認を組み合わせます。 - 再起動後も残すには、Debian/Ubuntu では
iptables-persistentを入れてnetfilter-persistent saveを実行します。