タグ: RHEL

firewalldのゾーンと設定方法のケーススタディ

RHEL系Linuxに備わっているfirewalld。非常に柔軟で(比較的)直感的に使える仕組みだったのでメモを残します。

特に驚きだったのがzoneの概念。

ufwが基本的に「システム全体に対してポートを開けるか閉じるか」をシンプルに管理するのに対し、firewalldは「接続するネットワークの信頼度に応じて、ファイアウォールのルールを瞬時に切り替える」という柔軟性がありました。

そもそもfirewalldにおけるゾーンとは?

一言で言うと、ゾーンとは「接続元のネットワークやインターフェース(LANカードなど)の『信頼度』に応じたグループ分け」のことです。

従来の iptables などでは、「このIPアドレスからのこのポートへの通信を許可する」といった細かいルールを1つずつ書く必要があります。

一方、firewalld では以下のようなステップで考えます。

  1. あらかじめ「拒否」「自宅用」「パブリック(公共)」といった、ルールの異なる箱(ゾーン)を用意しておく。
  2. ネットワークインターフェース(例: eth0)や、特定のIPアドレスをその箱に割り当てます。

これにより、「カフェのWi-Fiに繋いだ時は『パブリック』ゾーンに切り替える」「会社のLANに繋いだ時は『社内』ゾーンに切り替える」といった管理が、一瞬でできるようになります。

代表的なプリセットゾーン

firewalld には、最初からいくつかのゾーンが用意されています。

ゾーン名信頼度主な用途・特徴
drop最低すべての受信パッケージを破棄します(応答すら返さない)。こちらからの送信は可能です。
blockすべての受信を拒否します。drop と違い、相手に「拒否しました」という通知(ICMP)を返します。
public低〜中デフォルトのゾーン。 不特定多数がいる公共のネットワーク用。自分が許可した通信(sshなど)だけを通します。
externalルーターとして使う場合の「外側(インターネット側)」用。マスカレード(NAT)が有効になります。
**home / internal**自宅や社内LANなど、周囲のコンピューターを信頼できる場合用。お互いの通信が少し緩く許可されています。
trusted最高すべてを許可します。完全に安全だと分かっているネットワーク専用です。

ゾーンの3つの重要なルール

インターフェースは必ずどこかのゾーンに属する

ネットワークカード(eth0wlan0 など)は、必ずいずれか1つのゾーンに紐付けられます。何も設定していない場合、自動的に public(デフォルトゾーン)に属します。

IPアドレス単位での割り当ても可能

「インターフェース全体は public だけど、上司のPCのIPアドレス(192.168.1.50)だけは trusted ゾーンとして扱う」といった柔軟な設定が可能です。

ルールはゾーンごとに設定する

Webサーバー(80番ポート)を開放したい」となったら、「public ゾーンに対して80番ポートを許可する」というように、ゾーンに対して設定を紐付けます。

もっと有り体に言うと

『ジョジョの奇妙な冒険』第5部における『マン・イン・ザ・ミラー』です。

ここには『スタンド力』は おれの許可なくしては入る事はできない
『おまえ本体』だけ入る事を許可した
ここにある物は全て命のない『物質』だけだ―――おまえとオレだけ!他に『生きてる物』はいない………

という「許可」と「許可しない」を、鏡の世界ではなく「ゾーン」ごとに決められる能力、と言っていいでしょう。

よく使う基本コマンド

ゾーンの状態を確認・操作するための、代表的な firewall-cmd コマンドです。

現在のデフォルトゾーンを確認する

firewall-cmd --get-default-zone

すべてのゾーンの設定を確認する

firewall-cmd --list-all-zones

特定のインターフェース(例: eth0)のゾーンを変更する

firewall-cmd --zone=home --change-interface=eth0

特定のゾーン(例: public)にサービス(例: http)を許可する

 firewall-cmd --zone=public --add-service=http --permanent

`

  • --permanent をつけた後は、設定を反映させるために以下が必要です
firewall-cmd --reload

Linuxサーバーを構築する際は、まず「このサーバーはどこに置かれていて、どのゾーンを適用すべきか」を考えていきましょう。

そのケーススタディを行っていきます。

ケーススタディ

各部署が持っているサーバを管理するためシステム部が一括でzabbixのエージェントを入れたいという状況。

  • システム部だけにzabbixサーバへのssh接続(ターミナル操作)と管理画面(Web管理画面)の閲覧と操作を許可
  • それ以外の部署が所属するNWには上記2つを拒否。
  • zabbixサーバそのものを司る大本のzabbixサーバはエージェントの通信を許可。
  • ただし、zabbixのエージェントは通るようにします。

Step 1: システム部専用ゾーンの作成と「IP」の紐付け

まずは 192.168.1.0/24 だけが所属する専用のゾーン illuso を作ります。

  • ゾーンの新規作成
sudo firewall-cmd --permanent --new-zone=illuso
  • 一度リロードして、OSに新しいゾーンを認識させる(重要)
sudo firewall-cmd --reload

作成したゾーンに「部内NWのIPセグメント」を紐付ける

sudo firewall-cmd --permanent --zone=illuso --add-source=192.168.1.0/24

Step 2: システム部専用ゾーンに「許可サービス」を追加

Step 1 で作ったゾーンに、ssh, http, https の鍵(許可)を配置します。

  • システム部専用ゾーンに対して SSH を許可
sudo firewall-cmd --permanent --zone=illuso --add-service=ssh
  • システム部専用ゾーンに対して HTTP を許可
sudo firewall-cmd --permanent --zone=illuso --add-service=http
  • システム部専用ゾーンに対して HTTPS を許可
sudo firewall-cmd --permanent --zone=illuso --add-service=https

Step 3: 上位監視サーバー専用のゾーン作成と設定

社内のZabbixサーバー(192.168.12.6)だけが所属する man-in-the-mirror を作り、Zabbix Agent用の 10050 ポートを許可します。

  • 上位サーバー用のゾーンを新規作成
sudo firewall-cmd --permanent --new-zone=man-in-the-mirror
  • 再度リロードして、新しいゾーンを認識させる
sudo firewall-cmd --reload
  • そのゾーンに「上位ZabbixのIP」を紐付ける
sudo firewall-cmd --permanent --zone=man-in-the-mirror --add-source=192.168.12.6
  • そのゾーンに 10050 ポート(tcp)の許可を与える
sudo firewall-cmd --permanent --zone=man-in-the-mirror --add-port=10050/tcp

Step 4: 全員に開くポートの追加 と public の掃除

誰からでも受け付ける Zabbix Server(10051)や SMTP(25)を public に設定し、同時に、先ほど部内限定へお引越しさせた不要なサービス(ssh, http, https)を public から削除します。

  • 自身の Zabbix Server ポート(10051)を全員に開放
sudo firewall-cmd --permanent --zone=public --add-port=10051/tcp
  • SMTP(25)を全員に開放
sudo firewall-cmd --permanent --zone=public --add-service=smtp
  • 【とても重要】どこからでも SSH できる状態を public から削除
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
  • どこからでも HTTP できる状態を public から削除
sudo firewall-cmd --permanent --zone=public --remove-service=http
  • HTTPS も public から削除
sudo firewall-cmd --permanent --zone=public --remove-service=https

Step 5: 設定の最終反映と確認

ここまでの --permanent(次回起動用の設定)を、一気に本番環境へ反映(リロード)させます。

  • すべての設定を反映させます。
sudo firewall-cmd --reload

反映が完了したら、正しく設定できているか各ゾーンを覗いてみましょう。

  • 部内限定ゾーンの確認(sourcesにIP、servicesにssh http httpsがあること)
sudo firewall-cmd --zone=illuso --list-all
  • 上位Zabbix用ゾーンの確認(sourceにIP、portsに10050/tcpがあること)
sudo firewall-cmd --zone=man-in-the-mirror --list-all
  • 共通ゾーンの確認(servicesからssh, http, httpsが消え、zabbix-serverやsmtp、portsに10051/tcpがあること)
sudo firewall-cmd --zone=public --list-all

まとめ

以上、手順は多いものの

  • こいつは許可
  • こいつは拒否

を極めて柔軟に行えるのはRHEL系Linuxの持つ特権だと思いました。とはいえ、

この条件は「これから入ろうとする邪魔者」を拒否するには有効ですが、入ってしまったものを取り除くのは極めて厄介です。とくにウィルス(マルウェア)の除去は

『マン・イン・ザ・ミラー』オレだけが外に出る事を許可しろォォォォーーーーッ
うおおおががががが だが! ウイルスは許可しないィィィィィーーーッ
感染した部分は出る事は 許可しないィィィィィィィーーーッ!!

とはならないので注意が必要です。

(最後のこれが言いたいだけのエントリーを書き終えました)

RHEL9系でMySQLのrootパスワードを忘れてしまったときの再設定メモ

本手順は、MySQLのrootパスワードを紛失し、通常の方法でログインできなくなった場合に実施する「非常時用」のリカバリ手順です。

作業の前に

この作業は本来ならばあってはならない作業です。パスワード失念はセキュリティ事故の筆頭。ましてやWebシステムの神に等しいDBを司る通行証が消えた。なので、

  • 事前にアカウント情報を記したファイルを保存する
  • 適切な場所、適切なアクセス権で保管する

は必須ですが

往々にしてこの事故は起きます。なので、「マジで起きてしまった。取り敢えずの迅速な復旧」を望んでいる(つまり今回の私のような)方へのメモとなります。

この手順が笑える状況

  • 構築中
  • 検証作業中

のいずれかのみ。本番稼働中だったらまず笑えませんし、許可を得るための政治力・交渉力は甚大なものになります。筆者は「構築中」のパターンです。

環境

  • RHEL9系(RockyLinux9系)
  • MySQL 8

注記:RHEL 9系での注意点

  • 従来の mysqld_safe コマンドは廃止されているため、systemctl set-environment を使用して起動オプションを制御します。
  • MySQL 8.0以降は skip-grant-tables モード中でも FLUSH PRIVILEGES を実行しない限り ALTER USER コマンドが受け付けられない仕様となっています。

作業影響

  • 作業中にMySQLが止まる

これに尽きますが、「起きてしまったことは仕方ない。潔くサパッと止めてサクッと終わらせる」を心がけます。

さっくりとした手順

  1. MySQLサービスを停止します。
  2. 認証をスキップするための環境変数をセットします。
  3. 認証スキップ状態でMySQLを起動します。
  4. rootパスワードのリセットを行います。
  5. 認証を有効化してMySQLサービスを起動します。
  6. リセットされたパスワードでMySQLに入れることを確認します。

MySQLサービスの停止

  • MySQL停止
sudo systemctl stop mysqld
  • MySQL停止確認
systemctl status mysld

inactive(dead)を確認します。

認証をスキップするための環境変数をセットします。

  • 認証スキップのオプションを環境変数に一時セット
systemctl set-environment MYSQLD_OPTS="--skip-grant-tables --skip-networking"

(--skip-networkingを付けることで、作業中の外部接続を遮断し安全を確保します。)

認証スキップ状態でMySQLを起動します。

sudo systemctl start mysqld
  • MySQL起動確認
systemctl status mysld

active(running)を確認します。

rootパスワードのリセットを行います。

認証がスキップされている状態でログインし、権限テーブルを強制ロードしてからパスワードを書き換えます。

  • パスワードなしでrootログイン
mysql -u root

これでログインできたらひとまず成功です。ここからはSQL捜査を行います。

  • 権限テーブルをリロード (ALTER USER を実行可能にするために必須)
FLUSH PRIVILEGES;
  • パスワードの変更を実施します。
ALTER USER 'root'@'localhost' IDENTIFIED BY 'your_strong_password';
  • 変更を確定して終了
FLUSH PRIVILEGES;
exit

認証を有効化してMySQLサービスを起動します。

一時的な環境変数を削除し、通常の認証が有効な状態で再起動します。

  • MySQLを一旦停止
systemctl stop mysqld
  • MySQL停止確認
systemctl status mysld

inactive(dead)を確認します。

  • セットした環境変数を必ず解除 (これを忘れると誰でも入れる状態が続くため重要)
systemctl unset-environment MYSQLD_OPTS
  • 通常どおりサービスを起動
sudo systemctl start mysqld
  • MySQL起動確認
systemctl status mysld

リセットされたパスワードでMySQLに入れることを確認します。

mysql -u root -p

新しいパスワードでログインできたことを確認します。

まとめ

「起きないことが第一」とは言いますが、本当にくだらない理由でこういう事象は発生します。なので

  • 起きてしまったことは潔く認める
  • そこから原状の復旧を目指す。
  • 責任の追及とか誰がイモを引くかはその後で考える

の三段活用。“全裸大佐”が言う

過ちを気に病むことはない。ただ認めて、次の糧にすればいい

という「大人の特権」をフル活用しましょう。

レポジトリ一覧を確認するためのワンライナー。

RHEL系のサーバ設定で結構重要になってくる「サーバがどのレポジトリを使っているか?

これを簡単に調べるワンライナーです。

環境

Rocky Linux 9.10

ワンライナー

echo -e "|Repo-id|Repo-name|\n|---|---|" && dnf repolist -q | awk '$1 != "repo" && NF > 1 {id=$1; $1=""; sub(/^[ \t]+/, ""); print "|" id "|" $0 "|"}'

出力結果

Repo-idRepo-name
appstreamRocky Linux 9 - AppStream
baseosRocky Linux 9 - BaseOS
epelExtra Packages for Enterprise Linux 9 - x86_64
epel-cisco-openh264Extra Packages for Enterprise Linux 9 openh264 (From Cisco) - x86_64
extrasRocky Linux 9 - Extras
remi-modularRemi's Modular repository for Enterprise Linux 9 - x86_64
remi-safeSafe Remi's RPM repository for Enterprise Linux 9 - x86_64
zabbixZabbix Official Repository - x86_64
zabbix-non-supportedZabbix Official Repository (non-supported) - x86_64
zabbix-toolsZabbix Official Repository (tools) - x86_64

と、このままマークダウン記事として貼り付けられるようになっています。

仕組み

1. echo -e "|Repo-id|Repo-name|\n|---|---|"

Markdown形式の表の「見出し」を無理やり作っている部分です。

  • echo -e: 「バックスラッシュ記法」を有効にするオプションです。
  • \n: これがあることで、1行目の見出しと2行目の区切り線(|---|---|)の間で改行されます。

2. dnf repolist -q

システムのレポジトリ一覧を取得するコマンドです。

  • repolist: 有効なレポジトリの ID と名前を表示します。
  • -q (quiet): 「メタデータの期限切れ確認」などの余計なメッセージを非表示にし、純粋なリストの結果だけを出力します。

3. awk '$1 != "repo" && NF > 1 { ... }'

受け取ったテキストを1行ずつ加工しています。

要素意味
$1 != "repo"1番目の項目(Repo-id)が "repo" という文字列ではない行だけを処理する(見出し除外)。
NF > 1項目の数(Number of Fields)が1つより多い行=空行などを除外。
id=$11番目の項目(Repo-id)を変数 id にキープ。
$1=""1番目の項目をデータから消去。これで残りの $0(行全体)が Repo-name だけになります。
sub(/^[ \t]+/, "")1列目を消した後に残ってしまう「先頭の空白」を削除して綺麗にします。
print "|"...最後に、Markdownの枠組み | で囲って出力します。

まとめ

  1. echo で表の見た目を作り、
  2. dnf でデータを静かに(-q)呼び出し、
  3. awk で「いらない行(repo)」を捨てつつ、1列目(ID)とそれ以降(Name)をバラバラにして | で挟み直す。

という流れになっています。

Powered by WordPress & Theme by Anders Norén