RHEL 基礎 #7 基本セキュリティ — firewalld、SSH ハードニング

読了 10分

ここまでパッケージをインストールし、サービスを立ち上げ、ユーザーを作り、ディスクを付けてきました。最後は 外部から入ってくる経路を狭める作業 — ファイアウォール (firewalld) と SSH ハードニング。RHEL マシン一台を安全に運用するために必要な最後のステップです。

RHEL 基礎 シリーズでこの記事の位置:

firewalld — RHEL のファイアウォール抽象化 #

Linux カーネルのパケットフィルタリングは netfilter (昔は iptables、今は nftables) が担います。その上の 抽象化ツール が RHEL では firewalld です。Ubuntu の ufw と同じ役割。

firewalld の状態
$ sudo systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
     Active: active (running)
       ...

$ sudo firewall-cmd --state
running

RHEL 9 ではデフォルトでオンになっており、public zone が活性化されています。

zone モデルが核心 #

iptables が単調なルールチェーンであるのに対し、firewalldインターフェースを zone にまとめ、zone ごとにポリシー を決めます。同じマシンに入ってくるパケットでも、どの NIC から入ってくるかで違うポリシーを受けさせられます。

zone の形
   eth0 (外部網)  ──→  public zone   →  厳しいポリシー
   eth1 (社内網)  ──→  internal zone →  緩いポリシー
   wg0  (VPN)    ──→  trusted zone  →  ほぼすべて許可

定義済みの zone:

zone説明
drop最も厳しい。受信トラフィックを応答なしで遮断
blockdrop と似ているが拒否応答を返す
publicデフォルト — 一部のサービスのみ許可
externalNAT (マスカレーディング) 有効
dmzDMZ 内のサーバー用
work / home / internalデスクトップの信頼度別
trustedすべてのトラフィックを許可

現在の zone と有効ルールを見る #

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) のように。

service 追加 / 削除
$ 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= を差し込めます。

internal zone だけに mysql
$ sudo firewall-cmd --permanent --zone=internal --add-service=mysql
$ sudo firewall-cmd --reload

zone を指定しなければデフォルト zone (通常 public) に適用されます。

Rich Rule — もう一段細かく #

基本の service/port ルールは「許可 / 遮断」の二状態のみですが、特定 IP にだけ / 特定時間にだけ / ログを残しながら といったケースには足りません。そこで使うのが Rich Rule

特定 IP だけに SSH を許可
$ 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
特定 IP を拒否
$ 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.pub

ed25519 が現在推奨のアルゴリズム。RSA に慣れていれば -t rsa -b 4096 でも OK。

公開鍵を RHEL マシンへコピー:

ホスト側 — コピー
$ ssh-copy-id curtis@192.168.64.15

ssh-copy-id が自動で ~/.ssh/authorized_keys に公開鍵を追加し、権限も合わせてくれます (#5 の SSH 権限の部分)。

2) sshd_config.d 分離 #

RHEL 9 の sshd は /etc/ssh/sshd_config.d/ にある .conf ファイルをメイン設定より先に読みます。メインを触らずに自分の設定だけを分離しておけば、パッケージ更新で衝突しません。

/etc/ssh/sshd_config.d/01-hardening.conf
# パスワード認証無効 — 鍵認証のみ
PasswordAuthentication no
KbdInteractiveAuthentication no

# root への SSH ログインを遮断
PermitRootLogin no

# (任意) 標準の 22 番ポートを変更 — 自動ボットが半減
# Port 2222

# 空パスワード遮断
PermitEmptyPasswords no

# X11 / エージェントフォワーディング — 不要なら閉じる
X11Forwarding no
AllowAgentForwarding no

# 認証試行回数 / 同時未認証接続数
MaxAuthTries 3
MaxStartups 10:30:60

3) 適用前の検証と適用 #

設定を一行間違えると 現在の 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 も一緒に手を入れます。

ポートを 2222 に変えた場合
# 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 グループ
  • #3dnf 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/configdisabled) は運用では絶対に禁止。

AlmaLinux / Rocky の違い #

この記事のすべてのコマンドが そのまま動きます。firewalld / sshd / SELinux は RHEL のパッケージをそのまま持ってきているので違いがありません。fail2ban も EPEL から同じ。

よく出会う落とし穴 #

「firewalld のルールを適用したのに消える」 #

--permanent を付けずに --reload した場合。永続にするには --permanent--reload がセット。

「SSH が切れたまま繋がらない」 #

設定変更後、コンソールアクセスがない状態 で間違った sshd 設定で reload してしまったケース。仮想化環境なら VM コンソールから入って戻します。クラウドなら Serial Console か、インスタンス停止後にディスクをマウントして修正。だから 必ず新しいターミナルで検証 してから既存セッションを閉じる。

「ポートを開けたのにアクセスできない」 #

三箇所を一緒に見てください。

  1. firewalldfirewall-cmd --list-all
  2. SELinuxsemanage port -l | grep <ポート> で登録の有無
  3. アプリ自体が 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-zoneszone
firewall-cmd --permanent --add-service=httpservice 追加
firewall-cmd --permanent --add-port=8000/tcpport 追加
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 -tsshd 設定の文法チェック
systemctl reload sshdsshd 再適用 (接続は切れない)
semanage port -a -t ssh_port_t -p tcp 2222SELinux にポート登録

まとめ #

この記事で押さえた絵:

  • 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 マシン一台を立ち上げて登録まで
  • #3dnf でパッケージをインストール・削除・ロールバック / AppStream と modules
  • #4systemd でサービスを立ち上げ / 最初の 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資格トラック — 試験ドメインベース

このシリーズが次のトラックの出発点になります。自分の環境のニーズに合わせて選んで追ってください。

X