2026年5月24日
2026年5月24日
ファイアウォールで特定IPだけ許可する設定(ufw・firewalld・nftables)
SSH や管理画面、データベースのポートを「自分のIPアドレスからだけ接続できる」ようにするには、ファイアウォールで「特定の送信元IPを許可するルール」を先に入れ、そのあとで「それ以外を拒否する」順番で設定します。順番を逆にすると、作業中の自分の接続まで切れてしまいます。この記事では、締め出しを防ぐ準備から、ufw・firewalld・nftables それぞれの書き方、クラウドのセキュリティグループや Docker との関係、確認方法までを説明します。
先に決めておくこと:何を、誰に許可するのか
IP制限は「どのポートを」「どの送信元に」開けるかを決める作業です。Webサイトとして公開している80/443番は、一般の訪問者が接続するので通常は全体に開けたままにします。特定IPだけに絞る対象になるのは、たとえば次のようなものです。
- SSH(22番、または変更したポート)
- データベース(MySQL/MariaDB の3306番など。そもそも外部に開ける必要がないことが多い)
- 監視エージェントや管理ツールのポート
- 検証環境のWebサイト全体
WordPress のログイン画面(wp-login.php)や管理画面だけを制限したい場合は、ポート単位で動くファイアウォールではなく、Webサーバー(Nginx や Apache)の設定で URL ごとに制限するのが向いています。ファイアウォールで443番を絞ると、サイト全体が見えなくなるためです。
許可するIPアドレスは、自宅や会社の回線が「固定IP」かどうかを確認しておきます。一般的な家庭向け回線は、再接続などでIPアドレスが変わることがあります。IPが変わる回線で厳しく絞ると、ある日突然SSHに入れなくなります。その場合は、固定IPのある踏み台サーバーやVPN(WireGuard など)を経由し、許可するのは踏み台やVPNのアドレスだけにする方法が現実的です。
締め出しを防ぐ準備
リモートのサーバーでファイアウォールを触るときは、設定を誤ると自分のSSH接続が切れ、元に戻せなくなることがあります。作業の前に次の準備をしてください。
- VPS やクラウドの管理画面から使える「コンソール接続」(ブラウザ上のシリアルコンソールなど)の入り方を確認しておく
- 現在のルールを保存しておく(後述のコマンドで出力をファイルに残す)
- SSH のセッションを2つ開いておき、片方で設定、もう片方で接続が維持されるかを確かめる
- 自分の今のグローバルIPを、サーバー側から確認する
サーバー側から見た自分の送信元IPは、SSHで接続した状態で次のように確認できます。
# SSH 接続元のIPアドレス(左側が自分の送信元)
echo "$SSH_CLIENT"
who -m
ブラウザで「IP確認サイト」を見る方法もありますが、会社のプロキシやVPNを通っていると、サーバーに届くアドレスと異なることがあります。許可ルールには、サーバー側で見たアドレスを使ってください。
ufw(Ubuntu・Debian)で特定IPだけ許可する
ufw はルールを上から順に評価し、最初に一致したルールが適用されます。そのため「許可したいIP」のルールが、より広い拒否ルールよりも上にある必要があります。
# 1. 現在の状態を確認(無効なら inactive と表示される)
sudo ufw status verbose
# 2. 既定の方針:受信は拒否、送信は許可
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 3. 自分のIP(例:203.0.113.10)からの SSH だけ許可
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp comment 'admin ssh'
# 4. 公開している Web は全体に許可
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 5. ルールを確認してから有効化
sudo ufw show added
sudo ufw enable
すでに 22/tcp を全体に許可するルール(OpenSSH のアプリケーションプロファイルなど)がある場合は、IP限定のルールを追加しただけでは意味がありません。新しいルールで接続できることを確かめてから、全体許可のルールを削除します。
sudo ufw status numbered
# 番号を確認してから削除(例:3番)
sudo ufw delete 3
ネットワーク単位で許可するなら 203.0.113.0/24 のようにCIDR表記で書けます。IPv6 で接続している場合は IPv6 のアドレスも同じように許可が必要です。ufw の IPv6 対応は /etc/default/ufw の IPV6=yes で有効になります(多くの環境で既定値です)。
firewalld(RHEL・AlmaLinux・Rocky Linux など)で特定IPだけ許可する
firewalld は「ゾーン」という単位で、どの送信元やインターフェースにどのサービスを許可するかを管理します。管理用のゾーンを作り、そこに許可するIPとSSHを登録して、公開用のゾーンからSSHを外す、という形にすると整理しやすくなります。
# 現在の設定を確認
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=public --list-all
# 管理用ゾーンを作成し、許可するIPと SSH を登録
sudo firewall-cmd --permanent --new-zone=admin
sudo firewall-cmd --permanent --zone=admin --add-source=203.0.113.10/32
sudo firewall-cmd --permanent --zone=admin --add-service=ssh
sudo firewall-cmd --reload
# admin ゾーン経由で SSH できることを、別セッションで確かめてから
# 公開用ゾーンの SSH 許可を外す
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
sudo firewall-cmd --reload
送信元アドレスが admin ゾーンに登録されているIPからの通信は admin ゾーンのルールで処理され、それ以外は public ゾーンのルールで処理されます。現在の firewalld では、admin ゾーンで許可していない通信は public ゾーンへ回されずに拒否される動作が基本です(古い版にあった「ゾーンドリフト」は無効化されています)。そのため、管理用のIPから自分のサイト(80/443)も見るなら、admin ゾーンにも --add-service=http と --add-service=https を追加しておきます。--permanent を付けたルールは再読み込み後に反映され、付けないルールは実行中の設定だけに入り、再起動で消えます。
試しに入れてみて問題があれば自動で消えるようにしたい場合は、--permanent を付けずに --timeout を指定する方法もあります。
# 5分間だけ有効なルール(実行中の設定のみ)
sudo firewall-cmd --zone=admin --add-service=ssh --timeout=300
trusted ゾーンに送信元IPを入れる方法もよく紹介されますが、trusted はすべての通信を許可するゾーンです。SSH だけを許可したい場合は、上のように必要なサービスだけを持つゾーンを使う方が意図に合います。
nftables で直接書く場合
ufw や firewalld を使わず、nftables のルールを直接管理する場合は、許可するIPを「セット」にまとめると管理しやすくなります。
#!/usr/sbin/nft -f
# /etc/nftables.conf の例(既存のルールを置き換えるので、適用前に内容を必ず確認する)
flush ruleset
table inet filter {
set admin_v4 {
type ipv4_addr
flags interval
elements = { 203.0.113.10, 198.51.100.0/24 }
}
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
ct state invalid drop
iif "lo" accept
meta l4proto { icmp, ipv6-icmp } accept
ip saddr @admin_v4 tcp dport 22 accept
tcp dport { 80, 443 } accept
}
}
# 構文チェック(適用はしない)
sudo nft -c -f /etc/nftables.conf
# 適用
sudo nft -f /etc/nftables.conf
既存の接続を許可する ct state established,related accept を入れ忘れると、適用した瞬間に今のSSHセッションの応答が通らなくなることがあります。ICMP(特に IPv6 の ICMPv6)を止めると、IPv6 の通信自体が不安定になるので、通常は許可しておきます。ufw や firewalld が動いている環境で nftables を直接書き換えると、管理ツールのルールと混ざって分かりにくくなるため、どれか1つに統一してください。
クラウドのセキュリティグループや Docker との関係
クラウドやVPSのパケットフィルター
AWS のセキュリティグループや、VPS 事業者の管理画面にあるパケットフィルターは、OS のファイアウォールより手前で通信を止めます。どちらか一方だけで制限していても動作しますが、OS 側とクラウド側の両方で同じ方針にしておくと、片方の設定ミスがあっても守りが残ります。接続できないときは、両方のルールを確認するのが切り分けの基本です。
Docker が公開したポート
Docker で -p 8080:80 のように公開したポートは、Docker が iptables(または nftables)のルールを直接追加するため、ufw のルールを通らずに外部へ開いてしまうことがあります。ufw で拒否しているつもりでもコンテナのポートに外から接続できる、という状況はこれが原因であることが多いです。対策としては、コンテナを 127.0.0.1:8080:80 のようにローカルだけに公開する、Docker のドキュメントで説明されている DOCKER-USER チェーンに制限ルールを入れる、といった方法があります。Docker のバージョンによってファイアウォールとの連携の仕組みが変わることがあるため(2026年10月現在)、利用中のバージョンのドキュメントを確認してください。
CDN を前に置いている場合
Cloudflare などの CDN を経由している場合、Webサーバーに届く通信の送信元は CDN のIPアドレスです。オリジンサーバーの80/443を CDN からの接続だけに絞ることもできますが、CDN のIP範囲は変わることがあるので、公式に公開されている一覧を定期的に取り込む仕組みが必要です。
設定後の確認
設定が終わったら、許可したIPからは接続でき、それ以外からは接続できないことを、両方の立場から確認します。
# サーバー側でルールを確認
sudo ufw status verbose # ufw
sudo firewall-cmd --zone=admin --list-all # firewalld
sudo nft list ruleset # nftables
# 許可していない別の回線(スマートフォンのテザリングなど)から
nc -vz -w 3 203.0.113.200 22 # 203.0.113.200 はサーバーのIPの例
許可していない回線から試してタイムアウトすれば、制限が効いています。最後に、サーバーを再起動してもルールが残るかを、コンソール接続を用意したうえで一度確かめておくと安心です。
まとめ
- 先に「許可するIPのルール」を入れ、接続できることを確認してから「それ以外の拒否」や全体許可の削除を行います
- 作業前にコンソール接続の方法を確認し、SSHセッションを2つ開いておくと締め出しを防げます
- ufw は上から順に評価、firewalld は送信元ごとのゾーン、nftables はセットでまとめると管理しやすくなります
- IPが変わる回線では、踏み台サーバーやVPNのアドレスを許可する方法が現実的です
- クラウドのセキュリティグループ、Docker の公開ポート、CDN 経由の送信元IPも合わせて確認します