SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-09-04

Rocky・AlmaLinux VPSのfirewalld設定入門

Rocky LinuxとAlmaLinuxのVPSでfirewalldを使い、SSHやWebポートを開閉する手順を解説します。ゾーン、再起動後も維持する--permanentの注意点も確認できます。

firewalld とは何か、Rocky と AlmaLinux に含まれる理由

firewalld は、Rocky Linux、AlmaLinux、および Red Hat Enterprise Linux (RHEL) のその他のリビルドでデフォルトでインストールされるファイアウォール管理ツールです。両ディストリビューションがこのデフォルトを採用しているのは、独自に選択したからではありません。Rocky と AlmaLinux が CentOS の方針変更後に Red Hat の成果物をどのようにリビルドするようになったかを知ると、その理由が分かります。firewalld 自体がパケットを検査するわけではありません。保存済みの設定を管理し、その設定を nftables ルールに変換します。firewall-cmd コマンドを使うと、サーバーを稼働させたまま設定を編集できます。このガイドの内容は両ディストリビューションで変わりません。Rocky と AlmaLinux を実際に分ける要素は、ファイアウォールではなく、互換性の保証と引き続きサポートされる CPU の範囲だからです。

Ubuntu VPS で ufw がどのように動作するかをすでに知っていれば、役割は理解できます。firewalld には、ufw にはない2つの考え方があります。1つ目はゾーンです。これは、パケットを振り分ける名前付きのポリシーです。2つ目は、現在有効なルールと保存済みのルールを分けて管理する仕組みです。これを指定するのが --permanent フラグであり、firewalld で最も混乱しやすい点です。

以下に示す操作はすべて、自分のサーバーで実行するコマンドです。変更のたびに、2台目のマシンから動作を確認してください。サーバー上では正しく見えるルールでも、インターネットから見ると誤っている可能性があります。

他の作業を始める前に SSH を許可する

Rocky と AlmaLinux の多くのインストール環境では、firewalld がすでに導入され、実行されています。また、出荷時の設定で SSH が許可されています。ただし、最小構成のクラウドイメージでは firewalld が削除されていることがあります。思い込みで判断せず、確認してください。

sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --state

firewall-cmd --state は running を出力します。サービスが停止している場合、他の firewall-cmd 呼び出しはすべて FirewallD is not running を返し、0 以外の終了ステータスで終了します。コマンドが何も実行していないように見える場合、最初に確認すべき点です。

現在許可されている内容を確認します。

sudo firewall-cmd --list-all

実際の出力には、さらに数行含まれます。重要なのは次の行です。

public (active)
  target: default
  interfaces: eth0
  sources:
  services: cockpit dhcpv6-client ssh
  ports:
  rich rules:

services: 行の ssh が、現在のセッションがまだ動作している理由です。これがない場合は、他の作業をする前に追加してください。SSH ルールがない状態でファイアウォールを起動すると、セッションが切断され、再接続できなくなります。

sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

target: default は、どのルールにも一致しないパケットを ICMP(Internet Control Message Protocol)の host-prohibited 応答で拒否することを示します。そのため、閉じたポートに接続するクライアントには、すぐに No route to host が表示されます。target を DROP に設定すると、サーバーは応答しなくなり、スキャナーはタイムアウトまで待機します。

sudo firewall-cmd --permanent --zone=public --set-target=DROP
sudo firewall-cmd --reload

実行する前に影響を理解してください。DROP はサーバーが ping に応答することも停止させるため、自分の監視も停止します。

「ルールが消えたのはなぜですか?」--permanent フラグ

firewalld は、2 つの設定を同時に保持します。runtime 設定は、現在カーネルが適用している設定です。permanent 設定は /etc/firewalld/zones/public.xml に保存され、reload または再起動後に復元される設定です。

--permanent を付けないコマンドは、runtime 設定だけを変更します。変更は直ちに反映されますが、次回の reload または起動時に消えます。--permanent を付けたコマンドはファイルに書き込みますが、実行中の設定は変更しません。そのため、reload するまでポートは閉じたままです。どちらもバグではありません。どちらの場合もコマンドが success と表示されるため、混乱しやすいだけです。

毎回、2 つを組み合わせて記述してください。

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload

2 つの設定を確認できます。どちらの間違いをしたのかを最も早く特定できます。

sudo firewall-cmd --list-services
sudo firewall-cmd --permanent --list-services

最初のコマンドは現在有効な設定を表示します。2 番目のコマンドは保存された設定を表示します。現在の設定にサービスがあり、保存された設定にない場合、そのルールは次回の reload で消えます。保存された設定にサービスがあり、現在の設定にない場合は、reload を忘れています。sudo firewall-cmd --runtime-to-permanent は現在の設定をすべて保存ファイルにコピーします。試行錯誤した後に便利です。

--reload は connection tracking の状態を保持するため、SSH セッションは維持されます。--complete-reload はカーネルモジュールも再読み込みし、その状態を失わせます。通常は、SSH セッションを含むすべての確立済み接続が切断されます。通常の reload を使用してください。

安全策も用意されています。runtime ルールは自動的に期限切れにできます。

sudo firewall-cmd --add-service=http --timeout=5m

このルールは5分後に自動的に削除されます。--permanent と組み合わせることはできません。変更に確信がない場合にテストするための機能だからです。以前からある安全策のほうが確実です。ルールを編集するときは、2 つ目の SSH セッションを開いたままにしてください。新しいルールでのログインに成功するまで、そのセッションを閉じないでください。

ゾーンと、VPS ではデフォルトゾーンだけが重要な理由

ゾーンは、信頼レベルが設定された権限の集合に名前を付けたものです。firewalld は、受信した各パケットを必ず1つのゾーンに割り当てます。まず、各ゾーンの sources: リストに対して、パケットの送信元アドレスを照合します。一致するものがなければ、受信インターフェースが割り当てられているゾーンを使用します。インターフェースがどのゾーンにも割り当てられていない場合、パケットはデフォルトゾーンに入ります。

sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zones

ネットワークインターフェースが1つしかない VPS では、最初の答えはほぼ必ず public であり、使用するゾーンもこれだけです。firewall-cmd は --zone= 引数なしで実行するとデフォルトゾーンに対して処理するため、このガイドの短いコマンドはすべてゾーン名を指定せずに動作します。

ここで、半日を無駄にする原因を説明します。インターフェースが別のゾーンに割り当てられていると、ルールは public に追加されますが、トラフィックは別のゾーンで処理されます。そのため、追加した設定は何の効果も持たず、警告も表示されません。--get-active-zones で割り当て先を確認できます。

public
  interfaces: eth0

インターフェースが別のゾーン名の下に表示される場合は、--zone= を使ってそのゾーンにルールを記述するか、インターフェースを移動します。

sudo firewall-cmd --permanent --zone=public --change-interface=eth0
sudo firewall-cmd --reload

Rocky と AlmaLinux では、NetworkManager がインターフェースを管理し、接続の開始時にゾーンの割り当てを再適用します。再起動で設定が元に戻らないよう、NetworkManager 側でも設定してください。接続名は最初のコマンドの出力から取得します。接続名はデバイス名と異なることがほとんどです。

sudo nmcli connection show
sudo nmcli connection modify "System eth0" connection.zone public

送信元アドレスの照合はインターフェースの照合より優先されます。これにより、1つのアドレスに別のポリシーを適用できます。組み込みの trusted ゾーンはすべての通信を許可します。

sudo firewall-cmd --permanent --zone=trusted --add-source=203.0.113.10/32
sudo firewall-cmd --reload

この設定には注意してください。そのアドレスからサーバー上のすべてのポートが開放され、非公開のつもりだったデータベースも対象になります。1つのホストではなく1つのポートだけを許可する場合は、rich rule を使用してください。

firewalld のサービスとは何ですか?

サービスは、ポートをまとめた名前付きの定義です。XML ファイルとして提供されます。--add-service=https が 443/tcp を開くのは、/usr/lib/firewalld/services/https.xml が https の意味を定義しているためです。

sudo firewall-cmd --get-services
sudo firewall-cmd --info-service=https

--info-service を実行すると、その名前に対応するポートが表示されます。

https
  ports: 443/tcp

定義がある場合は、その名前を使用してください。6 か月後に --list-all を読み返しても内容が明確です。また、Cockpit などのパッケージは独自のサービスファイルをインストールします。定義がないものには --add-port を使用してください。

注意すべき点があります。ssh サービスは 22/tcp だけを意味します。サーバーの SSH アクセスを強化する際に SSH を別のポートへ移動した場合、--add-service=ssh では実際に使用するポートは開きません。

sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload

RHEL を再構築する場合は、そのポートに対して別の制限もあります。SELinux (security-enhanced Linux) はポート番号にラベルを付けます。sshd は、そのラベルの範囲外にあるポートへ bind できません。その結果、起動を拒否し、ログには error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. と表示されます。先にポートへラベルを付けてください。

sudo dnf install -y policycoreutils-python-utils
sudo semanage port -a -t ssh_port_t -p tcp 2222

現在開いているポートを確認するにはどうすればよいですか?

sudo firewall-cmd --list-all
sudo firewall-cmd --list-rich-rules
sudo nft list table inet firewalld | head -n 40

最初の 2 つは、firewalld が認識している内容を表示します。3 つ目は、firewalld が管理するテーブルにカーネルが実際に保持しているルールを読み取ります。通常、これらは一致します。

ただし、これだけでは証明になりません。別のマシンからテストしてください。

nc -zv 203.0.113.20 443

サーバー自体でテストを実行しないでください。firewalld は loopback インターフェースから到着するすべての通信を受け入れるため、ルールの内容にかかわらず curl http://localhost:8080 は成功します。このテストで確認できるのは、サービスが稼働していることだけです。ファイアウォールについては何も確認できません。

Web ポートを許可する

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

最後のコマンドでは、以前の出力に加えて http https も表示されるはずです。それでもサイトに接続できない場合、問題はファイアウォールではない可能性があります。ルールはパケットを許可するだけです。そのパケットを受け取るプロセスが、ポートで待ち受けている必要があります。

sudo ss -tlnp

0.0.0.0:443 または *:443 と表示されるソケットは、任意のアドレスからの接続を受け付けます。127.0.0.1:443 と表示されるソケットはループバックでのみ応答します。ファイアウォールルールを追加しても、外部から到達できるようにはなりません。Linux のポートと待ち受けソケットでは、この違いを詳しく説明しています。

ポートをもう一度閉じるにはどうすればよいですか?

sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --reload

ここでも --permanent ルールが適用されますが、この方向では影響が大きくなります。実行時設定からサービスを削除するだけでは、ポートは閉じたように見えます。しかし、次回の reload または reboot で、保存済みのファイルから再び開かれます。実行した確認が成功するため、この穴には気付きません。

存在しなかったものを削除すると Warning: NOT_ENABLED: http と表示されますが、終了コードは 0 のままです。同じものを 2 回追加すると Warning: ALREADY_ENABLED: http と表示されます。どちらも安全な動作です。ただし、名前のスペルミスは別です。Error: INVALID_SERVICE は、その名前の定義が firewalld に存在せず、何も変更されなかったことを示します。

--list-all に cockpit と表示され、ポート 9090 の Cockpit Web コンソールを使用していない場合は、これを削除してください。開いているポートはすべて、継続してパッチを適用する必要があるサービスです。残すことにしたサービスについては、dnf-automatic でセキュリティ更新をタイマーでインストールできます。そのため、更新作業を自分の記憶だけに頼らずに済みます。ただし、パッチをインストールしただけでは適用されたことになりません。更新が適用された後は、needs-restarting で古いライブラリを保持しているサービスを確認できます。

1 つの送信元アドレスにポートを制限する

Rich rules は、通常のサービス名では意図した条件を表現できない場合に使う詳細な形式です。SSH の接続元を 1 つのオフィスアドレスに制限するには 2 つのコマンドが必要で、2 つ目のコマンドを忘れることがよくあります。

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload

ゾーンは権限の集合であり、最初に一致した項目で処理が止まる番号付きリストではありません。Rich rule は 1 つのアドレスからの接続を許可します。どの接続も拒否しません。ssh がまだ services: 行に残っている間は、インターネット全体からポート 22 に接続できるため、Rich rule を追加しても実際の動作は変わりません。広範なエントリを削除してください。削除しない限り、狭い条件のルールは飾りにすぎません。

サービス名がないポートでは、ポート番号を指定します。

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port port="5432" protocol="tcp" accept'

大量のネットワーク通信を破棄し、その記録を残すには、アクションの前に log 要素を置きます。Rich rule 言語ではこの順序が必要です。

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" log prefix="fw-drop " level="info" limit value="3/m" drop'
sudo firewall-cmd --reload
sudo journalctl -k -g fw-drop

limit の値により、大量のパケットで journal が埋まることを防ぎます。SSH の接続元を 1 つのアドレスに制限する前に、そのアドレスが固定されていることを確認してください。自宅の接続で IP アドレスが変わると、変更された時点で接続できなくなります。そのため、プロバイダーのコンソールアクセスを事前にテストし、利用できる状態にしておいてください。

ufw コマンドと対応する firewall-cmd

同じ作業でも、使用するツールが異なります。すべての --permanent 行には sudo firewall-cmd --reload が必要です。この点は、このような一覧だけでは示せません。

  • sudo ufw enable は sudo systemctl enable --now firewalld になります
  • sudo ufw disable は sudo systemctl disable --now firewalld になります
  • sudo ufw status verbose は sudo firewall-cmd --list-all になります
  • sudo ufw allow OpenSSH は sudo firewall-cmd --permanent --add-service=ssh になります
  • sudo ufw allow 443/tcp は sudo firewall-cmd --permanent --add-port=443/tcp になります
  • sudo ufw delete allow 443/tcp は sudo firewall-cmd --permanent --remove-port=443/tcp になります
  • sudo ufw allow from 203.0.113.10 to any port 22 は上記の rich rule になります
  • sudo ufw reload は sudo firewall-cmd --reload になります
  • sudo ufw default deny incoming は public zone の動作と同じで、--set-target=DROP はその無出力版です
  • sudo ufw logging on は sudo firewall-cmd --set-log-denied=all になります

1 つ、明確にしておくべき違いがあります。ufw は番号付きの一覧を保持するため、位置 1 にルールを挿入できます。firewalld にはルール番号がないため、「このルールを最初に置く」という指定は意味を持ちません。firewalld の 2 つのエントリが矛盾しているように見える場合、設定内に拒否ルールがないため、広範な許可ルールが優先されます。広範なエントリは自分で削除する必要があります。

Docker コンテナに到達できるのは、ファイアウォールが閉じているように見えるのに、なぜですか?

公開したコンテナポートは、zone が制御するファイアウォールの処理部分を通らないためです。docker run -d -p 8080:80 nginx は、Docker に独自の NAT(ネットワークアドレス変換)ルールと転送ルールを書き込ませます。8080 に到着したパケットは書き換えられ、コンテナへ転送されます。そのため、ホストに配信されるのではなく、転送として処理されます。zone の services: と ports: の行は、ホストに配信されるパケットを制御します。転送経路は Docker のルールが制御し、それらのルールが許可します。

その結果、sudo firewall-cmd --list-all では 8080 番ポートが表示されないのに、別のマシンから nc -zv 203.0.113.20 8080 を実行すると接続できるサーバーになります。Docker が追加した内容を確認してください。

sudo iptables -t nat -L DOCKER -n

修正するには、publish flag を変更します。ポートを loopback にバインドし、その前段に reverse proxy を配置してください。

docker run -d -p 127.0.0.1:8080:80 nginx

これで、コンテナはサーバー上の curl http://127.0.0.1:8080 に対してのみ応答し、外部からの接続には応答しません。Ubuntu ユーザーも同じ問題に遭遇します。詳しくは Docker コンテナが ufw を通り越してポートを直接公開する理由を参照してください。Rocky と AlmaLinux が base repository で提供している rootful Podman も、同じ NAT 方式でポートを公開します。そのため、zone の一覧を信用せず、別のマシンからテストしてください。この重複が、これらのディストリビューションへの Docker Engine のインストールで Ubuntu のガイドにはない手順がいくつか必要になる理由でもあります。最初の手順は、Podman がすでに docker コマンドを所有していることへの対応です。

再起動後も有効にする方法と、発生するエラー

sudo systemctl is-enabled firewalld
sudo systemctl status firewalld

enabled と active (running) が必要です。実行中でも有効化されていないファイアウォールは、最初の再起動までしかサーバーを保護しません。この確認は、新しい VPS で最初の 10 分間に行う作業の一覧に、SSH 鍵や更新の確認と併せて含めます。

Raw nftables コマンドと firewalld は併用できません。 firewalld は inet firewalld というテーブルを管理します。sudo nft flush ruleset を実行するとこのテーブルが削除され、サーバーはすべての通信に対して開放されます。それでも firewall-cmd --list-all は意図した設定を表示します。firewalld がカーネルに保持されている内容ではなく、自身が認識している設定を報告するためです。sudo firewall-cmd --reload でルールを再インストールできます。再読み込み後もルールを復元できるように、firewall-cmd でルールを記述してください。

1 台のサーバーで 2 つのファイアウォール管理ツールを使う場合。 firewalld と併せて ufw または iptables-services をインストールすると、互いを認識しない 2 つのプログラムがルールを書き込みます。どちらが有効になるかは、最後に起動したサービスによって決まります。どちらか 1 つを選んでください。Rocky と AlmaLinux では、ディストリビューションのサポート対象である firewalld を使用します。

サーバーの前段にあるプロバイダーのファイアウォール。 多くの VPS パネルには、別のネットワークファイアウォールがあります。--list-all でポートが開いているのに外部からの接続が失敗する場合は、サーバー上の設定を変更する前にパネルを確認してください。逆も同様です。パネル側のルールでポートを開いていても、firewalld がパケットを拒否すれば通信できません。

sudo なしで firewall-cmd を実行する場合。 変更には root が必要です。root 権限がない場合、認証チェックによって要求が拒否され、何も変更されません。そのため、コマンドが無視されたように見えます。

多くの作業は 6 つのコマンドで対応できます。状態の確認には --list-all、ポートなどを開くには --permanent --add-service または --add-port、閉じるには --permanent --remove-service、保存済みファイルを適用するには --reload、一連の試行後には --runtime-to-permanent を使用します。ゾーンは public、フラグは --permanent です。確実な確認は、別のマシンから実行してください。

FAQ

firewalld のルールが再起動後に消えるのはなぜですか?

ルールが runtime 設定にのみ追加されていたためです。sudo firewall-cmd --add-service=http は直ちに適用されますが、次回の reload または boot で破棄されます。/etc/firewalld/zones/public.xml の保存済み設定が変更されていないためです。--permanent を追加してから、sudo firewall-cmd --reload を実行してください。手動で追加済みのルールを保持するには、sudo firewall-cmd --runtime-to-permanent を実行します。これにより、現在の設定が保存ファイルへコピーされます。

--permanent を付けてルールを追加しても何も変わらないのはなぜですか?

--permanent はファイルに書き込みますが、実行中の firewall には適用しないためです。sudo firewall-cmd --reload が保存済み設定を kernel に読み込むまで、ポートは閉じたままです。sudo firewall-cmd --list-services と sudo firewall-cmd --permanent --list-services を比較してください。保存済みリストにあり、現在のリストにない項目があれば、不足しているのは reload です。

--add-service と --add-port のどちらを使うべきですか?

実行するサービスに名前が定義されている場合は --add-service を使います。意図を明確にでき、sudo firewall-cmd --info-service=https でその名前が対象とするポートを正確に確認できます。サービスを定義するものがない場合、または標準以外のポートで待ち受ける場合は --add-port を使います。ssh サービスは 22/tcp のみを意味します。そのため、SSH を 2222 に移動した場合は、--add-port=2222/tcp と、そのポート用の SELinux ラベルが必要です。

firewalld でポートが閉じているのに Docker コンテナへ接続できるのはなぜですか?

公開ポートは Docker 独自の NAT ルールによって書き換えられ、コンテナへ転送されます。そのため、パケットは host に配信されません。zone のサービス一覧とポート一覧が対象とするのは、host に配信されたパケットだけです。--list-all に何も表示されなくても、コンテナはインターネットからの接続に応答します。docker run -d -p 127.0.0.1:8080:80 nginx を使って loopback にのみ公開し、その前段に reverse proxy を配置してください。

firewalld の代わりに Rocky Linux へ ufw をインストールできますか?

1 台のサーバーで 2 つの firewall manager を使うと、両者は互いを認識せずにルールを書き込みます。どのルールセットが残るかは、どちらのサービスが最後に起動したかで決まります。firewalld は Rocky Linux と AlmaLinux でサポートされているツールです。すでにインストールされており、ufw と同じ nftables backend を操作します。デフォルト zone と --permanent flag を一度覚えれば、このツール全体を扱えるようになります。