RHEL 基礎 #7 基本セキュリティ — firewalld、SSH ハードニング
ここまでパッケージをインストールし、サービスを立ち上げ、ユーザーを作り、ディスクを付けてきました。最後は 外部から入ってくる経路を狭める作業 — ファイアウォール (firewalld) と SSH ハードニング。RHEL マシン一台を安全に運用するために必要な最後のステップです。
RHEL 基礎 シリーズでこの記事の位置:
- #1 RHEL とは — Fedora から RHEL まで、そして AlmaLinux と Rocky Linux
- #2 セットアップ — RHEL 9 インストール、Subscription Manager、初ログイン
- #3 dnf とパッケージ管理 — repo、modules、AppStream
- #4 systemd 入門 — サービス、target、journalctl
- #5 ユーザー/グループ/権限 — UID/GID、sudo、ACL
- #6 ファイルシステムの基本 — XFS、mount、/etc/fstab
- #7 基本セキュリティ — firewalld、SSH ハードニング ← この記事
firewalld — RHEL のファイアウォール抽象化 #
Linux カーネルのパケットフィルタリングは netfilter (昔は iptables、今は nftables) が担います。その上の 抽象化ツール が RHEL では firewalld です。Ubuntu の ufw と同じ役割。
$ sudo systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Active: active (running)
...
$ sudo firewall-cmd --state
runningRHEL 9 ではデフォルトでオンになっており、public zone が活性化されています。
zone モデルが核心 #
iptables が単調なルールチェーンであるのに対し、firewalld は インターフェースを zone にまとめ、zone ごとにポリシー を決めます。同じマシンに入ってくるパケットでも、どの NIC から入ってくるかで違うポリシーを受けさせられます。
eth0 (外部網) ──→ public zone → 厳しいポリシー
eth1 (社内網) ──→ internal zone → 緩いポリシー
wg0 (VPN) ──→ trusted zone → ほぼすべて許可定義済みの zone:
| zone | 説明 |
|---|---|
drop | 最も厳しい。受信トラフィックを応答なしで遮断 |
block | drop と似ているが拒否応答を返す |
public | デフォルト — 一部のサービスのみ許可 |
external | NAT (マスカレーディング) 有効 |
dmz | DMZ 内のサーバー用 |
work / home / internal | デスクトップの信頼度別 |
trusted | すべてのトラフィックを許可 |
現在の zone と有効ルールを見る #
$ sudo firewall-cmd --get-default-zone
public
$ sudo firewall-cmd --get-active-zones
public
interfaces: enp0s1
$ sudo firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: enp0s1
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
rich rules:ここに見える services: cockpit dhcpv6-client ssh が現在許可されているサービスです。SSH がデフォルトで開いているおかげで #2 で SSH 経由で入れたわけです。
firewall-cmd — 永続と一時
#
最も混同しやすい部分から。firewalld の変更には 二つの次元 があります。
| オプション | どこに適用 |
|---|---|
| (なし) | 現在動作中のランタイムだけに — 再起動や reload で消える |
--permanent | 永続設定ファイルだけに — 適用は reload 後 |
運用パターンはほぼ常に:
$ sudo firewall-cmd --permanent --add-service=http # 永続設定に追加
$ sudo firewall-cmd --reload # 永続設定をランタイムに反映または 二段に分けず一度で:
$ sudo firewall-cmd --add-service=http # ランタイムに即時
$ sudo firewall-cmd --runtime-to-permanent # ランタイムの状態を永続にコピー--reload はアクティブな接続を切りません — SSH で作業中に reload しても切れません。ただし変更が意図通り入ったかをもう一度 --list-all で確認する習慣が良いです。
service / port を追加・削除 #
定義済みの service #
firewalld はよく使うサービスに名前を付けてあります。ssh (22)、http (80)、https (443)、cockpit (9090) のように。
$ sudo firewall-cmd --permanent --add-service=http
$ sudo firewall-cmd --permanent --add-service=https
$ sudo firewall-cmd --permanent --remove-service=cockpit
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-services
ssh dhcpv6-client http https定義済みの service 一覧:
$ sudo firewall-cmd --get-services | tr ' ' '\n' | head -20
RH-Satellite-6
RH-Satellite-6-capsule
amanda-client
amanda-k5-client
amqp
...ポートを直接開く #
定義にないポートは直接:
$ sudo firewall-cmd --permanent --add-port=8000/tcp
$ sudo firewall-cmd --permanent --add-port=5000-5010/tcp # 範囲
$ sudo firewall-cmd --permanent --remove-port=8000/tcp
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-ports
8000/tcp 5000-5010/tcp特定 zone だけに #
オプションのどこにでも --zone= を差し込めます。
$ sudo firewall-cmd --permanent --zone=internal --add-service=mysql
$ sudo firewall-cmd --reloadzone を指定しなければデフォルト zone (通常 public) に適用されます。
Rich Rule — もう一段細かく #
基本の service/port ルールは「許可 / 遮断」の二状態のみですが、特定 IP にだけ / 特定時間にだけ / ログを残しながら といったケースには足りません。そこで使うのが Rich Rule。
$ sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="192.168.64.0/24"
service name="ssh"
accept'
$ sudo firewall-cmd --reload$ sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="203.0.113.50"
reject'$ sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
port port="8443" protocol="tcp"
log prefix="HTTPS-ALT: " level="info"
accept'Rich rule はコマンドが長いので、運用ではテキストファイルに整理しておいて一行ずつ適用します。
iptables を少しだけ #
古い資料には iptables -A INPUT ... のようなコマンドがよく出ます。RHEL 9 では firewalld がその上の抽象化で、iptables コマンドで直接ルールを入れても firewalld と衝突して消えます。新しく覚える方は firewalld だけ で。
運用の自動化で firewalld が重すぎるなら
nftablesを直接使う道もあり、クラウド環境ではホストの firewalld よりも AWS Security Group / GCP Firewall のようなクラウドファイアウォールに手がいくことが多いです。それでもマシン内側の保護として firewalld を一緒にオンにしておくのが二重ロックです。
SSH ハードニング — 標準 4 種 #
SSH は外部から最も狙われる的です。インターネットに公開された 22 番ポートには平均で 1 分間に数百回の自動化された試行 が来ます。次の四つを一度に処理するのが標準です。
1) 鍵認証を作って登録 — パスワードログインを閉じる前に #
まずホスト (作業しているノート PC) で SSH 鍵を作っておきます。既にあればスキップで OK。
$ ssh-keygen -t ed25519 -C "curtis@laptop"
Generating public/private ed25519 key pair.
Enter file in which to save the key (~/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase): ← 強い passphrase 推奨
...
Your identification has been saved in ~/.ssh/id_ed25519
Your public key has been saved in ~/.ssh/id_ed25519.pubed25519 が現在推奨のアルゴリズム。RSA に慣れていれば -t rsa -b 4096 でも OK。
公開鍵を RHEL マシンへコピー:
$ ssh-copy-id curtis@192.168.64.15ssh-copy-id が自動で ~/.ssh/authorized_keys に公開鍵を追加し、権限も合わせてくれます (#5 の SSH 権限の部分)。
2) sshd_config.d 分離
#
RHEL 9 の sshd は /etc/ssh/sshd_config.d/ にある .conf ファイルをメイン設定より先に読みます。メインを触らずに自分の設定だけを分離しておけば、パッケージ更新で衝突しません。
# パスワード認証無効 — 鍵認証のみ
PasswordAuthentication no
KbdInteractiveAuthentication no
# root への SSH ログインを遮断
PermitRootLogin no
# (任意) 標準の 22 番ポートを変更 — 自動ボットが半減
# Port 2222
# 空パスワード遮断
PermitEmptyPasswords no
# X11 / エージェントフォワーディング — 不要なら閉じる
X11Forwarding no
AllowAgentForwarding no
# 認証試行回数 / 同時未認証接続数
MaxAuthTries 3
MaxStartups 10:30:603) 適用前の検証と適用 #
設定を一行間違えると 現在の SSH セッションは維持されるけれど新しい SSH が繋がらない ロック状態になります (コンソールアクセスがなければ手詰まり)。適用前に必ず検証。
$ sudo sshd -t # 出力なしなら OK$ sudo systemctl reload sshd現在の SSH セッションはそのままにし、新しいターミナルからもう一度 SSH で接続して動作確認。繋がらなければ現在のセッションで設定を戻します。新しい SSH が繋がれば安全。
$ ssh curtis@192.168.64.15 # 新しいターミナルから
[curtis@rhel9-lab ~]$ # 鍵認証で入れた (パスワードを聞かれない)4) ポート変更時 — firewalld と SELinux も一緒に #
ポートを変えたら firewalld と SELinux も一緒に手を入れます。
# 1) sshd_config.d に 'Port 2222' を書く
# 2) SELinux に新しいポートを ssh 用と知らせる
$ sudo dnf install -y policycoreutils-python-utils
$ sudo semanage port -a -t ssh_port_t -p tcp 2222
# 3) firewalld で開ける (既存の 22 もしばらく一緒に置き、検証後に閉じる)
$ sudo firewall-cmd --permanent --add-port=2222/tcp
$ sudo firewall-cmd --reload
# 4) sshd reload + 新ターミナルで確認
$ sudo systemctl reload sshd
$ ssh -p 2222 curtis@192.168.64.15
# 5) 新ポートが正常に動くことを確認してから 22 を閉じる
$ sudo firewall-cmd --permanent --remove-service=ssh
$ sudo firewall-cmd --reload重要 — ポート変更はセキュリティの本質ではなく 自動化されたボットのノイズを減らす程度 のものです。本当の保護は鍵認証 + パスワード遮断 + root 遮断の三つから来ます。ポート変更はオプション。
fail2ban — もう一段 #
自動化ボットが絶え間なく叩いてくるのが気になるなら、fail2ban で自動遮断を仕込めます。EPEL から。
$ sudo dnf install -y fail2ban
$ sudo systemctl enable --now fail2banデフォルトポリシーは SSH に 5 回失敗したらその IP を 10 分遮断。詳しいチューニングは 上級 #5 で。
その他に手を入れる部分 #
基礎の最後なので ここまでで十分 な部分と 次のシリーズで 扱う部分を分けておきます。
このシリーズで触れた範囲 (= 十分) #
- #5 — 日常作業用ユーザー作成、root で作業しない、sudo の wheel グループ
- #3 —
dnf updateでセキュリティパッチを定期適用 - #7 (この記事) — firewalld + SSH ハードニング
次のシリーズで #
| テーマ | シリーズ |
|---|---|
| SELinux 深掘り — ラベル、ブール、ポリシー | 中級 #1 |
| auditd / OpenSCAP / FIPS コンプライアンス | 上級 #5 |
自動セキュリティパッチ (dnf-automatic) | 中級 #6 |
| TLS 証明書運用 | 実践 #1 |
SELinux 一行だけ — RHEL 9 で SELinux はデフォルト Enforcing。セキュリティの最後の一層であり、オフにしてはいけません。「なぜ nginx が 80 で立ち上がらない?」の半分が SELinux のラベル問題で、中級 #1 でトラブルシューティングのパターンを整理します。一時的にオフにしたいときも
setenforce 0(Permissive モード) 程度で — 完全無効化 (/etc/selinux/configでdisabled) は運用では絶対に禁止。
AlmaLinux / Rocky の違い #
この記事のすべてのコマンドが そのまま動きます。firewalld / sshd / SELinux は RHEL のパッケージをそのまま持ってきているので違いがありません。fail2ban も EPEL から同じ。
よく出会う落とし穴 #
「firewalld のルールを適用したのに消える」 #
--permanent を付けずに --reload した場合。永続にするには --permanent と --reload がセット。
「SSH が切れたまま繋がらない」 #
設定変更後、コンソールアクセスがない状態 で間違った sshd 設定で reload してしまったケース。仮想化環境なら VM コンソールから入って戻します。クラウドなら Serial Console か、インスタンス停止後にディスクをマウントして修正。だから 必ず新しいターミナルで検証 してから既存セッションを閉じる。
「ポートを開けたのにアクセスできない」 #
三箇所を一緒に見てください。
- firewalld —
firewall-cmd --list-all - SELinux —
semanage port -l | grep <ポート>で登録の有無 - アプリ自体が 0.0.0.0 ではなく 127.0.0.1 にバインド —
ss -tlnpで確認
三つのうち一つが抜けても外部から繋がりません。
「鍵認証を切ってパスワードも切ってロックされた」 #
sshd_config.d/01-hardening.conf の適用直前に、必ず別の新しいターミナルで鍵認証で一度入って動作を確認してから reload。これが SSH 作業の黄金律です。
よく使うコマンド一表 #
| コマンド | する仕事 |
|---|---|
firewall-cmd --state / --list-all | 状態 / 現在のルール |
firewall-cmd --get-default-zone / --get-active-zones | zone |
firewall-cmd --permanent --add-service=http | service 追加 |
firewall-cmd --permanent --add-port=8000/tcp | port 追加 |
firewall-cmd --permanent --add-rich-rule='...' | rich rule |
firewall-cmd --reload | 永続設定をランタイムに |
firewall-cmd --runtime-to-permanent | ランタイムを永続に |
ssh-keygen -t ed25519 | 鍵生成 |
ssh-copy-id user@host | 公開鍵登録 |
sshd -t | sshd 設定の文法チェック |
systemctl reload sshd | sshd 再適用 (接続は切れない) |
semanage port -a -t ssh_port_t -p tcp 2222 | SELinux にポート登録 |
まとめ #
この記事で押さえた絵:
- RHEL のファイアウォール抽象化は firewalld、インターフェースを zone にまとめて zone ごとにポリシー。
firewall-cmdの二つの次元 —--permanent(永続設定) +--reload(ランタイム反映)。または--runtime-to-permanent。- service / port / rich rule の三段でだんだん細かく。
- SSH ハードニング標準 4 種 — 鍵認証登録 → パスワード遮断 → root 遮断 → (任意) ポート変更。
- すべての sshd 変更は
sshd -t検証 → reload → 新ターミナルで確認 の流れ。 - SELinux はオフにしません。深掘りは 中級 #1。
- 自動ボット対策は EPEL の
fail2banで一行。
シリーズの締めくくり #
この 7 編で押さえたこと:
- #1 — Red Hat ファミリーの地図 / RHEL の位置 / AlmaLinux/Rocky
- #2 — RHEL マシン一台を立ち上げて登録まで
- #3 —
dnfでパッケージをインストール・削除・ロールバック / AppStream と modules - #4 —
systemdでサービスを立ち上げ / 最初の unit を直接書く /journalctl - #5 — ユーザー/グループ/権限モデル /
chmod/ ACL / sudo - #6 — XFS の上に新しいディスクを付けてマウントし
/etc/fstabに永続登録 - #7 — firewalld の zone モデルと SSH ハードニング
ここまでで 一人で RHEL マシン一台を運用できるレベル です。会社のサーバーに SSH で入っても基本動作で詰まりません。
次のステップは深掘りに入るシリーズです。
| シリーズ | 何を |
|---|---|
| RHEL 中級 | SELinux 深掘り、LVM、Stratis、NFS、NetworkManager、Podman 入門 |
| RHEL 上級 | ブート / カーネルチューニング / 性能解析 / OpenSCAP / Cockpit |
| RHEL 実践 | Web・DB・Podman 運用、Cockpit、Ansible 自動化 |
| RHCSA / RHCE | 資格トラック — 試験ドメインベース |
このシリーズが次のトラックの出発点になります。自分の環境のニーズに合わせて選んで追ってください。