3-4. ファイアウォール - iptables / nftables / ufw / fail2ban

所要時間: 40-50分(がっつりなら2セッション分) ゴール: パケットフィルタリングの本質を理解し、VPS初期セットアップで最小許可ポリシーを組める。fail2ban で総当たり攻撃を自動防御できる コミット内容: ufw / fail2ban の設定と確認ログを ~/learn/linux/day16 にコミット

この章が終わるとできること

  • パケットフィルタの3動作(ACCEPT / DROP / REJECT)を区別できる
  • iptables / nftables / ufw の 3層の関係 を説明できる
  • ufw default deny incoming + ufw allow 22/tcp最小許可ポリシー を書ける
  • fail2ban で SSH 総当たり攻撃を自動 ban できる
  • AWS の Security Group と OS の ufw の二段防御を説明できる

Day 15 / Day 17 とのつながり

  • Day 15 (SSH) で開けたポート 22 を、今日は「外から守る」側に立つ
  • Day 17 (Nginx) で開けたポート 80/443 も、今日のファイアウォールの 裏側 に立つ
  • Day 13 (ログ管理) の /var/log/auth.log が、fail2ban の入力ソースになる

これができると何が嬉しいか

  • VPS を契約してから 数時間以内に攻撃の餌食 になる事故を絶対回避
  • ポート開けただけ vs 多層防御」の差を体感し、本番設計の解像度が上がる
  • Day 21(デプロイ)の前に 「公開する前に固める順番」 が頭に入る

大前提: ファイアウォールを設定せずに本番サーバーを公開してはいけない

「VPSを契約して、SSHできるようにして、Nginx立てて、ふう完成」── これだけで本番稼働させると数時間で攻撃の餌食になります。

実際にVPSを立てて何もしないと、起動から:

  • 数分以内: ポートスキャンが始まる(22, 80, 443, 3306, 6379, 27017 など)
  • 数十分以内: SSH への総当たり攻撃が秒間数回ペースで来る
  • 数時間以内: 何百ものIPから攻撃ログが /var/log/auth.log に溜まる
  • 数日以内: 設定が緩いとアカウント乗っ取り、マイニング、踏み台化

これを防ぐ最初の砦がファイアウォール。L3/L4 で「許可していないパケットは入口で捨てる」シンプルかつ強力な仕組み。

そして、これは「VPS固有の話」ではなく、AWS / GCP / Azure のSecurity Group や VPC ACL も中身は同じ概念です。クラウドであっても VM の中で更に ufw を設定する「二段防御」が定石。今日はこのレイヤを腹落ちさせます。


セッション①: ファイアウォールの本質と Linux の仕組み(25-30分)

0. 録画スタート

mkdir -p ~/log ~/learn/linux/day16
cd ~/learn/linux/day16
script ~/log/linux_day16.log

1. ファイアウォールとは(パケットフィルタリング)

ファイアウォールの本質

ネットワーク経由のパケットを、ルールに基づいて通す/捨てる機能。OSのカーネルがネットワークインターフェース直下で動作する。ルールの判定項目: 送信元/宛先IP・ポート・プロトコル・パケット状態・入出力方向。

INPUT vs OUTPUT vs FORWARD の方向

ざっくり言うと

パケットには「自分宛か・自分発か・通過するだけか」の3種類があり、ファイアウォールはこの3つを別々に判定する。

まず「サーバーの立場」を想像する
[インターネット]
       │
       ▼
   ┌───────┐
   │ サーバー │
   └───────┘
       │
       ▼
[内部ネットワーク]

サーバーから見ると、パケットには3つの流れがある:

                   ┌───────────┐
       INPUT ──→  │           │
                   │  サーバー   │ ──→ OUTPUT
   FORWARD ──→  │           │ ──→ FORWARD
                   └───────────┘
3種類の意味
[INPUT]    インターネット → サーバー自身
            「自分宛」のパケット
            例: 80番に来た HTTP リクエスト

[OUTPUT]   サーバー自身 → インターネット
            「自分発」のパケット
            例: apt update で外部リポジトリに繋ぐ

[FORWARD]  インターネット → サーバー → 別ネットワーク
            「通過するだけ」のパケット
            例: ルーター・VPN・コンテナのブリッジ
ユースケース別の対応
パケット例チェーン何を制御
外部から SSH (22)INPUT「22 番を許可」「特定IPだけ許可」
外部から HTTP (80)INPUT「80 番を許可」
内部から外部 APIOUTPUT「外向き接続の制限」(通常は許可)
サーバーが踏み台になって裏のDBへFORWARD「ルーター用途の時だけ許可」
Docker コンテナ間通信FORWARD「Docker が裏で操作する領域」
普通のサーバーでの設定例
sudo iptables -P INPUT DROP        # 着信はデフォルト拒否
sudo iptables -P OUTPUT ACCEPT     # 送信はデフォルト許可
sudo iptables -P FORWARD DROP      # 通過は拒否(ルーターでないなら)

イメージ:

  • INPUT = 玄関ドア(誰を入れるか)
  • OUTPUT = 裏口ドア(自分が出る時)
  • FORWARD = 建物の中の通り抜け(ルーター・配送業者の通路)
一番覚えやすい説明
  • 「自分宛」 = INPUT
  • 「自分発」 = OUTPUT
  • 「通過するだけ」 = FORWARD

通常のサーバーは INPUT を厳しく、OUTPUT は緩く、FORWARD は閉じる。

allow vs deny vs reject の違い

ざっくり言うと

「通さない」にも2種類ある。黙って無視する (drop/deny) か、「拒否しました」と返事する (reject) か。

まず3つの判定結果を整理
パケットが届いた
   ↓
[ALLOW / ACCEPT]    通す
[DENY / DROP]       黙って捨てる(無視)
[REJECT]            「拒否」と明示的に返事する
DROP した時の攻撃者視点
攻撃者: nmap で 22 番をスキャン
   ↓
サーバー: パケットを DROP(無反応)
   ↓
攻撃者の画面: タイムアウトまで待つ(数秒)
   ↓
「このポートは閉じてるのか?それともそもそもサーバーがいない?分からない」

イメージ: 居留守。ドアをノックされても応答しない。

REJECT した時の攻撃者視点
攻撃者: nmap で 22 番をスキャン
   ↓
サーバー: ICMP "port unreachable" を返す
   ↓
攻撃者の画面: 即座に "closed" 表示
   ↓
「ここは閉じてるな。次のポートに行こう」

イメージ: インターホン越しの「対応できません」。明確な拒否応答が返る。

対比表
ALLOW (ACCEPT)DROP (DENY)REJECT
パケットを通すはいいいえいいえ
送信元に返事(アプリが返す)何も返さない”unreachable” を返す
攻撃者から見た反応接続成功タイムアウト即「閉鎖」
スキャン速度-遅くなる(攻撃者にとって不利)速い
運用の判別正常サーバー停止と区別しにくい「拒否されている」と明確
推奨用途通したい通信本番のデフォルト内部ネットワーク・デバッグ時
イメージ
  • ALLOW = 「どうぞお入りください」
  • DROP = 居留守(スキャナーがイラつく)
  • REJECT = インターホンで「いまお出かけ中」と返答(運用は楽だが情報を与える)
一番覚えやすい説明
  • 本番で世界に晒すポートDROP(攻撃者を困らせる)
  • 内部で運用するルールREJECT(エラーがすぐ分かって楽)

マッチ順序の原則

ファイアウォールは 「最初に当たったルールが採用、当たらなければデフォルトポリシー」。順序が決定的に重要。

ファイアウォールの「層」

ネットワーク全体には複数のファイアウォール層がある:

[インターネット]
    │
[クラウドプロバイダのEdge ACL] ← L3、AWS なら Network ACL
    │
[VPC / Subnet]
    │
[Security Group / Firewall Rule] ← L4、AWS なら SG (state-ful)
    │
[仮想マシン]
    │
[Linux iptables/nftables/ufw] ← VM内のファイアウォール(さらに細かく)
    │
[アプリケーション層のWAF / RateLimit] ← L7、Nginx の limit_req など
    │
[アプリ]

多層防御(Defense in Depth)」の考え方。1層が破られても他で止める。本番運用では複数層を組み合わせる。

2. ufw vs iptables vs nftables vs firewalld の違い

ざっくり言うと

これら全部、カーネルの netfilter を操作する道具。違いは「どの抽象レベルで使うか」だけ。

ユーザー(あなた)
    │
    ▼
[ufw / firewalld]  ← 簡単ラッパー(人間向け)
    │
    ▼
[iptables / nft]   ← 直接コマンド(中級)
    │
    ▼
[netfilter]        ← カーネル内の本体(実体)

まず素朴な誤解

「iptables と nftables と ufw が別々のファイアウォールで、競合する」── これは間違い。

実体は全部 netfilter。ufw も firewalld も、内部では iptables や nftables のコマンドを生成して呼んでいるだけ。

歴史の流れ

〜1999    ipchains (古い)
   ↓
1999〜    iptables 登場(長年のデファクト)
   ↓
2014〜    nftables 登場(後継、高速・柔軟)
   ↓
現代      iptables コマンドが内部で nftables を呼ぶ(iptables-nft)

Ubuntu 22.04 や Debian 12 では、iptables と打っているように見えても実体は nftables

対比表

ufwfirewalldiptablesnftables
抽象レベル高(人間向け)高(人間向け)中(直接)中(直接)
構文の難易度易 (ufw allow 22)中 (zone 概念)難(長い)中(宣言的)
用途Ubuntu の VPSRHEL/CentOS のデスクトップ・サーバー既存システム / スクリプト新規プロジェクト
ディストリUbuntu/DebianRHEL/Fedora/CentOSどこでもどこでも (modern)
構文例ufw allow 80/tcpfirewall-cmd --add-port=80/tcpiptables -A INPUT -p tcp --dport 80 -j ACCEPTnft add rule inet filter input tcp dport 80 accept
zone(信頼レベル)なしあり(home/public 切替)なしなし
実体iptables/nft を生成iptables/nft を生成直接 netfilter直接 netfilter

イメージ

  • ufw = 「コンビニで弁当を買う」(楽、選択肢は少なめ)
  • firewalld = 「ファミレスで注文する」(zone でメニュー切替)
  • iptables = 「自炊する」(細かく作れる、手間がかかる)
  • nftables = 「最新キッチンで自炊」(自炊の現代版、効率良い)

一番覚えやすい説明

  • Ubuntu のVPSで楽に固めたいufw
  • RHEL 系で zone を活用したいfirewalld
  • 細かい制御・自動化nftables(新規)/ iptables(既存)
  • 全部内部は netfilter。喧嘩しない、レイヤが違うだけ。
# iptables のルール一覧
sudo iptables -L -n -v
 
# nftables のルール一覧
sudo nft list ruleset

3. ufw - Ubuntu の簡単 wrapper

ufw(Uncomplicated Firewall)の位置付け

Ubuntu 由来の iptables/nftables ラッパー。「複雑な iptables を簡単に」を目指したフロントエンド。

内部的には iptables/nftables のルールを生成しているだけ。なので:

  • ufw で設定 → iptables -L で確認できる
  • 高度な要件は iptables/nftables 直書きで対応可能

使い所: 個人VPSや小規模本番。「とにかく最小許可で固める」が数コマンドでできる。

ufw 以外の選択肢:

  • firewalld: RHEL/CentOS系のデフォルト。zone(信頼レベル)の概念があり、ノートPCのネットワーク切替に強い
  • iptables 直書き / iptables-save: 細かい制御が必要な時
  • nftables 直書き: 新規プロジェクトの推奨

4. ufw 基本操作

理屈が分かったら実際に手を動かすフェーズ。ufw は数コマンドで「最小許可」が組めるので、まずはここを体で覚えます。

  • 何のコマンドか: ufw status / ufw allow / ufw deny / ufw enable を中心とした、宣言的なファイアウォール操作コマンド群
  • いつ使うか: VPS の初期セットアップ、特定 IP からの SSH を一時許可する時、社内ネットワークからだけ Postgres を許可する時、緊急で怪しい IP をブロックする時
  • 解決する具体的な問題: 「iptables の長い構文を覚えていられない」を、ufw allow 22/tcp のような自然な英語に近い構文で吸収する
# 状態確認
sudo ufw status
# Status: inactive
 
# 詳細表示
sudo ufw status verbose
sudo ufw status numbered     # 番号付きで表示(後で番号指定削除に便利)
 
# デフォルトポリシー設定(最初にやる)
sudo ufw default deny incoming     # 着信はデフォルト拒否
sudo ufw default allow outgoing    # 送信はデフォルト許可
 
# SSH を許可(ufw enable する前に必ず!)
sudo ufw allow ssh                 # = ufw allow 22/tcp
sudo ufw allow 22/tcp              # 明示的に書いてもOK
 
# Web (HTTP/HTTPS)
sudo ufw allow http                # = 80/tcp
sudo ufw allow https               # = 443/tcp
sudo ufw allow 80,443/tcp          # まとめて
 
# 特定IPからのみ許可(管理用)
sudo ufw allow from 203.0.113.5 to any port 22
 
# 特定IPレンジから許可
sudo ufw allow from 10.0.0.0/8 to any port 5432   # 内部ネットワークからのみPostgres
 
# 拒否
sudo ufw deny 23/tcp               # telnet を明示的に塞ぐ
sudo ufw deny from 198.51.100.0/24 # 怪しいIPレンジをブロック
 
# 有効化(これが最重要)
sudo ufw enable

ufw enable の前に SSH を必ず許可せよ

# NG: SSH を許可せずに enable
sudo ufw default deny incoming
sudo ufw enable               # 次の瞬間、自分のSSHセッションが切れる

リモートサーバーで上記をやると自分が締め出される。enable コマンドは確認プロンプトを出すが、無視して進めると詰む。

正しい手順:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh           # ← これを先にやる
sudo ufw allow http
sudo ufw allow https
sudo ufw enable              # 安全

保険として別端末を確保: 1つのターミナルで設定中、別ターミナルで ssh してログイン状態を維持しておく。最悪、設定でセッションが切れても、もう一方は生きている = ufw disable で復旧可能。

クラウドコンソール経由のアクセス: AWSのEC2シリアルコンソール、Linodeのlish、Vultr/DigitalOceanの「Console」機能を事前に動作確認しておく。SSHで締め出されてもここから入れる。

5. ルールの順序と番号

ファイアウォールのルールは並べる順序で挙動が変わります。同じルール3つでも、並び方を間違えると意図しない接続が通ったり止まったりします。

  • 何のコマンドか: ufw status numbered で番号付き表示、ufw delete <番号> で個別削除、ufw insert <番号> で途中挿入する
  • いつ使うか: 怪しい IP の DENY を既存 ALLOW より前に挿入する時、誤って追加したルールを削除する時、ルール順序を整理して可読性を上げる時
  • 解決する具体的な問題: 「最初にマッチしたルールが採用」というファイアウォールの大原則を、番号と順序で意識的に制御する
# 番号付きで表示
sudo ufw status numbered
# [ 1] 22/tcp                    ALLOW IN    Anywhere
# [ 2] 80/tcp                    ALLOW IN    Anywhere
# [ 3] 22/tcp                    DENY IN     198.51.100.10
 
# 番号指定で削除
sudo ufw delete 3
 
# ルールを途中に挿入
sudo ufw insert 1 deny from 198.51.100.0/24
# → 既存ルールより前に評価される(最優先)

ファイアウォールは「最初にマッチしたルールが採用」

ルールはリストの上から評価され、最初に当たったルールが採用される。

[1] DENY from 198.51.100.10
[2] ALLOW 22/tcp from any

順序が大事。198.51.100.10 からの22番ポート接続は [1] で拒否される([2] には到達しない)。

逆だと:

[1] ALLOW 22/tcp from any
[2] DENY from 198.51.100.10

[1] でACCEPTされるので、悪意あるIPもSSH可能。特定IP拒否は前に挿入する

6. 最小許可の設計(VPS初期セットアップ)

VPS を契約した直後の 「黄金の手順」 を組み立てます。順番を間違えると自分が締め出されるので、ここはコピペで動く完全な手順として記憶します。

  • 何のコマンドか: ufw default deny incomingufw allow ssh/http/httpsufw enable順序が厳密に決まった初期セットアップ手順
  • いつ使うか: 新規 VPS 契約直後、テンプレートからクローンした VM の初期設定、CIS Benchmark に沿ったハードニング作業
  • 解決する具体的な問題: 「とりあえず動かす」で全開放する事故、enable 後に SSH が切れる事故、設定を後回しにして攻撃を受ける事故
# 1. SSH のポートを変える場合は先に sshd_config を編集
# sudo vi /etc/ssh/sshd_config
# Port 22022
# sudo systemctl restart sshd
 
# 2. ufw でクリーンスタート
sudo ufw --force reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw default deny forward       # ルーター用途でないなら deny
 
# 3. 必要なポートだけ許可
sudo ufw allow 22022/tcp comment 'SSH (custom port)'
sudo ufw allow 80/tcp    comment 'HTTP'
sudo ufw allow 443/tcp   comment 'HTTPS'
 
# 4. 内部DBへのアクセスは管理ネットワークからのみ
# sudo ufw allow from 10.0.0.0/8 to any port 5432 comment 'Postgres from internal'
 
# 5. 有効化
sudo ufw enable
 
# 6. 確認
sudo ufw status verbose

「最小許可」が原則

  • 基本姿勢: 「明示的に許可した通信だけ通す。それ以外は捨てる」
  • 本番で開けるべきはほぼ 80/443/SSH のみ
  • DBポート (5432, 3306) を world に開けない: 内部ネットワークか踏み台経由のみ
  • Redis (6379), MongoDB (27017) も同じ: デフォルトで認証なしのものもあり、開けると数時間で乗っ取り

「これ要るかな?」と思ったら閉じる方向で判断。実際に困ったら開ければいい。

7. SSH ポート変更の議論

SSH を 22 から変えるべきか

賛否両論ある問題。

変える派の主張:

  • 22番への自動攻撃が劇的に減る(/var/log/auth.log のノイズ減少)
  • fail2ban のCPU負荷軽減
  • スキャナーは22, 2222, 22222 など典型ポートを優先する → 完全ランダム(例: 32841)にすればほぼ無風

変えない派(security through obscurity 批判):

  • 攻撃者が本気で来れば nmap で数分で見つかる
  • 鍵認証+fail2ban+強パスフレーズで22番のままでも安全
  • ポート変更でツールの設定が増え、運用負荷が上がる
  • 「ポート変えたから安心」と思考停止するのが最大のリスク

現実的な落としどころ:

  • 個人VPS / 個人プロジェクト: 変えてOK(ログのノイズ激減)
  • 企業の本番: ポートは22のまま、別の対策を厚くする(IPホワイトリスト、Cloudflare Zero Trust、踏み台、MFA、fail2ban)
  • 絶対NG: ポートだけ変えて他は何もしない(パスワード認証有効・rootログイン可能のまま)

8. fail2ban - ログ監視による自動ban

ufw が「入口で全部止める」道具なら、fail2ban は「入ろうとして失敗したやつを自動でブラックリスト化」する道具です。両者は補完関係にあり、本番ではセットで使います。

  • 何のコマンドか: fail2ban/var/log/auth.log などを inotify でリアルタイム監視し、認証失敗が閾値を超えた IP を ufw / iptables で自動 ban する
  • いつ使うか: 22 番を world に晒している時、Nginx の Basic 認証を守る時、WordPress 管理画面への総当たり攻撃を緩和する時
  • 解決する具体的な問題: 「ボットからの秒間数十回ペースの SSH 総当たり」を、人間が関わらずに自動緩和する
# インストール
sudo apt install -y fail2ban
 
# 基本設定(jail.local をカスタマイズ)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo vi /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local の主要設定
[DEFAULT]
bantime  = 1h            # 1時間 ban
findtime = 10m           # 10分間内で
maxretry = 5             # 5回失敗したら ban
banaction = ufw          # ufw を使って ban(iptables-multiport から変更)
 
# SSH を監視(デフォルトで有効)
[sshd]
enabled = true
port    = 22022          # SSH のポート(変更した場合)
logpath = %(sshd_log)s
maxretry = 3
findtime = 5m
bantime  = 24h
 
# Nginx の認証失敗
[nginx-http-auth]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log
 
# Nginx の bad bot(カスタムフィルタ)
[nginx-badbots]
enabled = true
port    = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
# 起動
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban
 
# 状態確認
sudo fail2ban-client status                # 有効な jail 一覧
sudo fail2ban-client status sshd           # sshd の詳細(ban中のIP一覧)
 
# 手動 ban
sudo fail2ban-client set sshd banip 198.51.100.10
 
# 手動 unban
sudo fail2ban-client set sshd unbanip 198.51.100.10
 
# ログ
sudo tail -f /var/log/fail2ban.log

fail2ban の仕組み

  1. ログファイルを監視(inotify でリアルタイム)
  2. フィルタ正規表現に一致する行をカウント(例: SSH の認証失敗)
  3. 閾値を超えたIPを ban(findtime 内に maxretry 回失敗)
  4. ban の方法: iptables / ufw / route などでパケットドロップ
  5. bantime 経過後に自動unban

重要: fail2ban は「予防」ではなく「短時間内の総当たり攻撃の緩和」。鍵認証+強パスワード+IP制限の補助として使う。これだけに頼るのは危険。

fail2ban の落とし穴

  • 自分がbanされる事故: パスワード入力ミス連続でself-ban。bantime短めから始めるか、自分のIPを ignoreip に登録
  • 動的IPの問題: 自宅のIPが日々変わる環境では、ホワイトリスト運用は困難
  • ログローテーション後にIP情報が失われる: バックエンドDBとして /var/lib/fail2ban/fail2ban.sqlite3 を使うので比較的堅牢だが、logrotate の設定要確認
# 自分のIPを除外(jail.local の DEFAULT に)
ignoreip = 127.0.0.1/8 ::1 203.0.113.5 192.168.1.0/24

セッション②: 本番運用パターンと多層防御(25-30分)

9. VPS 初期セットアップ完全版

ここまでの章で学んだ要素(SSH 堅牢化 + ufw + fail2ban + 自動更新)を1つの手順書にまとめます。これが本番運用の出発点です。

  • 何のコマンドか: 新規 VPS でやるべき全コマンドを順序通りに並べた「初期セットアップのチェックリスト」
  • いつ使うか: クラウド/VPS で新しいインスタンスを立てた直後、テンプレートを更新する時、新メンバーに渡す手順書として
  • 解決する具体的な問題: 「やるべき作業を忘れて公開してしまう」事故、毎回ググりながら手順を組む時間の無駄、人によって設定がバラつく属人化

シナリオ: Ubuntu 24.04 LTS の新規VPSを契約した直後にやること。

# 0. SSH で初回接続(rootまたは初期ユーザー)
ssh root@vps-ip
 
# 1. システム更新
apt update && apt upgrade -y
 
# 2. タイムゾーン設定(ログの時刻を合わせる)
timedatectl set-timezone Asia/Tokyo
 
# 3. 一般ユーザー作成(rootで作業を続けない)
adduser deploy
usermod -aG sudo deploy
 
# 4. deploy ユーザーに鍵を登録
mkdir -p /home/deploy/.ssh
cp /root/.ssh/authorized_keys /home/deploy/.ssh/   # rootから引き継ぐ
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
 
# 5. 別端末で deploy ユーザーでログインできることを確認
# (これを確認する前に SSH 設定を変更してはいけない)
 
# 6. SSH の堅牢化
vi /etc/ssh/sshd_config
# PermitRootLogin no
# PasswordAuthentication no
# Port 22022                # 任意
 
sshd -t && systemctl reload sshd
 
# 7. ufw 設定
ufw default deny incoming
ufw default allow outgoing
ufw allow 22022/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
 
# 8. fail2ban
apt install -y fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# (jail.local を編集して sshd の port などを設定)
systemctl enable --now fail2ban
 
# 9. 自動セキュリティ更新
apt install -y unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades
 
# 10. システムログを集約・監視(journalctl, logrotate 確認)
journalctl --vacuum-time=30d   # 30日でログ削除
 
# 11. ここまで来たら再起動して全部反映確認
reboot

10. クラウドのSecurity Groupとの関係

[インターネット]
     │
[AWS ALB / CloudFront / Cloudflare] ← WAF
     │
[AWS Security Group]                  ← クラウド側ファイアウォール(必須)
     │
[EC2 インスタンス]
     │
[ufw / firewalld]                      ← VM内ファイアウォール(任意だが推奨)
     │
[Nginx]                                ← L7 制御(limit_req 等)
     │
[アプリ]

AWS Security Group と ufw の二段防御

項目Security Groupufw
動作レイヤクラウド側(インスタンスの外)OS内(カーネル netfilter)
設定方法AWSコンソール / Terraform / APIコマンド or 設定ファイル
ステートフルはいはい
適用範囲複数インスタンスに共通そのVM単体
変更コスト即時反映、無料コマンド数発
監査ログCloudTrailsystemd journal

両方やるべき理由:

  • SGの設定ミス防止: SGを開けすぎても ufw で止まる
  • インスタンス内部からの異常通信制御: SG では制御しにくい outgoing 制御や、特定プロセスのみ許可など
  • マルチクラウド・オンプレでも統一されたOS層防御: クラウド依存しない

ただし、「クラウドのSGだけ」も実務では多い。シンプル&クラウド機能でCPU負荷無し、というメリットを取る選択。「ufwも入れる」は安全側に倒した運用。

11. iptables 直書きの基本(参考)

ufw は便利ですが、裏で動いているのは iptables/nftables です。トラブルシュートや細かい制御では、結局直接書く力が必要になります。

  • 何のコマンドか: iptables で INPUT / OUTPUT / FORWARD チェーンを直接編集し、ACCEPT / DROP を細かく制御する
  • いつ使うか: ufw で表現できない複雑なルール(特定 IF だけ・特定時間帯だけ)を作る時、ufw が入っていないサーバーで一時的に設定する時、CI/CD で iptables を流し込みたい時
  • 解決する具体的な問題: 「ufw の抽象化を越えた要件」を満たし、本番障害時に iptables -L から状況を読み解く能力を身につける
# 現状確認
sudo iptables -L -n -v
 
# シンプルな最小許可設定(ufwがやってくれることを手で書くと)
sudo iptables -F                                                # ルールをクリア
sudo iptables -P INPUT DROP                                     # デフォルト拒否
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT                                  # 送信は許可
sudo iptables -A INPUT -i lo -j ACCEPT                          # ループバック許可
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT  # 既存接続
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT             # SSH
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT             # HTTP
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT            # HTTPS
 
# 保存(ディストリにより方法が違う)
sudo apt install -y iptables-persistent
sudo netfilter-persistent save

iptables のチェーンとテーブル

テーブル:

  • filter (デフォルト): 通常のパケットフィルタ
  • nat: アドレス変換
  • mangle: パケット書き換え
  • raw: 接続追跡の前段

チェーン(filter テーブル):

  • INPUT: 自分宛のパケット
  • OUTPUT: 自分から出るパケット
  • FORWARD: 自分を通過するパケット(ルーター用途)

ターゲット:

  • ACCEPT / DROP / REJECT / LOG / RETURN
  • カスタムチェーンへの分岐も可能(複雑なルール整理に便利)

慣れたら nftables 直書きの方が見やすい:

# /etc/nftables.conf
table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        iif lo accept
        ct state established,related accept
        tcp dport { 22, 80, 443 } accept
    }
}

12. アンチパターン集

アンチパターン: 全ポート開放

# NG: 動かないので全部開けた
sudo ufw allow from any to any
# または iptables -P INPUT ACCEPT

「とりあえず動かす」ためにポート全開放。ファイアウォールが事実上無効

「動かない」原因は別にある(アプリのバインドアドレス、認証、SELinux等)。それを調査せずポートを全開にすると、本番で本番DBにworldから接続可能などの事故になる。

アンチパターン: 22 を world に晒したまま放置

22/tcp ALLOW IN Anywhere

SSH を全世界に開放。これ自体は不可避な場面もあるが(自宅IPが固定でない、リモートワーカーがいる)、対策無しは危険。

必須の対策:

  • パスワード認証無効化(鍵認証のみ)
  • root login 無効化
  • fail2ban で総当たり攻撃を緩和
  • 可能なら ALLOW を信頼できるIP範囲に限定(オフィスIP、VPN経由)

アンチパターン: fail2ban なしでパスワード認証 + 22 world

# sshd_config
PasswordAuthentication yes
Port 22

# ufw
22/tcp ALLOW IN Anywhere

これは数時間〜数日で侵入される構成

攻撃者は:

  1. 22番に総当たりを開始(秒間数十回)
  2. admin, root, ubuntu, test などの一般的なユーザー名と弱パスワードを試行
  3. 1つでも当たれば侵入完了

修正:

PasswordAuthentication no
# → 鍵認証のみ。fail2ban も入れる

アンチパターン: クラウドSGとufwで矛盾する設定

AWSの Security Group では80/443/22のみ許可。 ufw では「全部開放」または fail2ban も入れず22世界に開放。

結果: SGが守ってくれているのでとりあえず動く → SGを後で広めたらVMが無防備に。

正解: SG と ufw の意図を同じ方向に揃える。SGが厳しいなら ufw はそれより緩くてもOK、SGが緩いなら ufw を厳しくする。

13. 監視と運用

ファイアウォールは設定して終わりではありません。「いま何を弾いているか」「banされている IP は何か」を定期的にチェックする運用フローが必要です。

  • 何のコマンドか: journalctl -u ufw / tail -f /var/log/ufw.log / fail2ban-client status で、リアルタイムの遮断状況とブラックリストを確認する
  • いつ使うか: 朝の業務開始時の流し見、「攻撃多くないか?」と感じた時、特定IPからの大量アクセスを集計したい時、自分が誤って banされた時の解除
  • 解決する具体的な問題: 「fail2ban を入れて満足」状態を脱却し、攻撃トレンドの把握・誤検知の発見・正規ユーザーの解除を回せるようにする
# ファイアウォールのログを見る
sudo journalctl -u ufw -f
sudo tail -f /var/log/ufw.log
 
# どのIPがどのポートに来たか集計
sudo grep "BLOCK" /var/log/ufw.log | awk '{print $14, $20}' | sort | uniq -c | sort -rn | head -20
 
# fail2ban の活動状況
sudo fail2ban-client status sshd
# Currently banned: 5
# IP list: 198.51.100.10 203.0.113.20 ...
 
# 過去24時間で何回banしたか
sudo grep "Ban " /var/log/fail2ban.log | grep "$(date +%Y-%m-%d)" | wc -l

監視を習慣化する

朝の業務開始時に5秒で見るチェックリスト:

  1. sudo fail2ban-client status sshd → ban中のIP数の異常はないか
  2. last | head -10 → 自分の知らないログインがないか
  3. sudo tail -100 /var/log/auth.log | grep -i fail → 認証失敗の傾向
  4. df -h → ディスク逼迫していないか(侵入されてマイニング装置にされるとディスクが減る)

Mackerel / Datadog などのSaaS監視を入れるとさらに楽。

3-5_監視 で詳しくやる。


練習課題

# Mac から Ubuntu の Docker コンテナで練習する(ufwはコンテナ内で動かしにくいので iptables で)
# (docker = 軽量な仮想環境を起動するツール。詳しくは「インフラ」章で扱う。
#  --cap-add=NET_ADMIN でコンテナにネットワーク権限を渡すと iptables を操作できる。
#  「使い捨ての Ubuntu 環境がワンコマンドで作れる」とだけ理解すればOK)
docker run -it --rm --cap-add=NET_ADMIN ubuntu:24.04 bash
 
# コンテナ内で
apt update && apt install -y iptables nftables fail2ban net-tools curl
 
# 1. 初期状態
iptables -L -n -v
 
# 2. デフォルト拒否を設定
iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -L -n -v
 
# 3. テスト用にHTTPサーバー起動
# (python3 -m http.server = Python 標準ライブラリの簡易 HTTP サーバー。
#  「指定ポートで適当な Web サーバーを 1 コマンドで立てる」便利機能。
#  Python 自体は別章で扱うが、ここでは「テスト用に 80 番を LISTEN させる」だけの用途)
python3 -m http.server 80 &
 
# 別ターミナルから curl してテスト(コンテナ外から実IPで)
# docker exec で別シェル起動 → curl localhost:80
 
# 4. nftables の書き方も見てみる
cat > /tmp/nftables.conf <<'EOF'
table inet myfilter {
    chain input {
        type filter hook input priority 0; policy drop;
        iif lo accept
        ct state established,related accept
        tcp dport { 22, 80, 443 } accept
    }
}
EOF
nft -f /tmp/nftables.conf
nft list ruleset

締め: git で証跡を残す

exit
 
cd ~/learn/linux/day16
git init
git add .
git commit -m "feat(linux): ufw / fail2ban の設計と VPS 初期セットアップ手順"

チェックリスト

  • パケットフィルタリングの本質(ACCEPT/DROP/REJECT)を言える
  • iptables と nftables の関係を1分で説明できる
  • ufw enable の前に SSH 許可が必須な理由を即答できる
  • 最小許可ポリシーで VPS 初期セットアップの手順を書ける
  • fail2ban の仕組み(ログ監視→自動ban)を説明できる
  • ignoreip で自分のIPを除外できる
  • AWS Security Group と ufw の二段防御の意味を説明できる
  • iptables の INPUT / OUTPUT / FORWARD チェーンの違いを言える
  • DROP と REJECT の使い分けの基準を答えられる

詰まった時のチートシート

やりたいことコマンド
ufw 状態確認sudo ufw status verbose
デフォルト拒否sudo ufw default deny incoming
ポート許可sudo ufw allow 22/tcp
特定IPから許可sudo ufw allow from 1.2.3.4 to any port 22
有効化sudo ufw enable
無効化sudo ufw disable
リセットsudo ufw --force reset
ルール番号付き表示sudo ufw status numbered
ルール削除sudo ufw delete <番号>
iptables 確認sudo iptables -L -n -v
nftables 確認sudo nft list ruleset
fail2ban 状態sudo fail2ban-client status sshd
手動unbansudo fail2ban-client set sshd unbanip <IP>
ufwログsudo tail -f /var/log/ufw.log
認証ログsudo tail -f /var/log/auth.log

「実務OK」基準

  • 新規VPS を最小許可で初期セットアップできる: 数十分でSSH/HTTP/HTTPSだけ通る状態に
  • 「動かない」時に「ファイアウォールか?」を切り分けられる: アプリエラー / ネットワーク / FW を順に確認
  • 自分を締め出さない: 別端末確保、クラウドコンソール把握、ufw enable 前のSSH許可
  • fail2ban の状態を読める: ban中IPと回数の意味
  • クラウドSGとOS層FWを使い分けられる: 多層防御の意義を言える
  • アンチパターンを即座に指摘できる: 「全部開放した」「fail2banなし」「ポート変えただけで安心」

さらに深掘るなら

  • 公式: https://netfilter.org/ (netfilter プロジェクト本家、ドキュメント豊富)
  • man ufw, man iptables, man nft (いずれも詳細な公式ドキュメント)
  • 書籍: 『Linuxサーバーセキュリティ徹底入門』(日経BP) - 日本語で網羅的
  • CIS Benchmarks: 各ディストリのセキュリティハードニング項目(ファイアウォール設定含む)
  • fail2ban 公式: https://github.com/fail2ban/fail2ban (フィルタ・アクションの書き方)
  • クラウドVPN(WireGuard, Tailscale): SSHをworldに晒さないモダンな代替

アンチパターン / 初心者やらかし事例

NG 1: ufw enable を先に打って自分を締め出す

sudo ufw enable   # SSH ルール忘れて自分をブロック

→ 対策: ufw allow 22/tcp を先に、それから enable。クラウドのコンソール経由で復旧手段を確保しておく。

NG 2: 0.0.0.0/0 ALLOW で全開放

sudo ufw allow from any   # ufw 入れる意味が無い

→ 対策: 必要なポートと IP のみ。社内向けなら allow from <office_cidr> to any port 22

NG 3: SSH ポート変更だけで「セキュリティ対策完了」

# /etc/ssh/sshd_config
Port 2222

→ ポートスキャンで秒で見つかる。「カモフラージュではなくフィルタリング」が本質。 → 対策: 鍵認証 + fail2ban + ufw の三層を必ず入れる。

NG 4: クラウド SG だけで OS 側 ufw を無効化

→ 仮想ネットワーク変更時・他テナント / 他インスタンスからのアクセスが想定外パスで通ることがある。 → 対策: クラウド SG + OS ufw の 二段防御。設定はコードで管理(Terraform / Ansible)。


自己評価チェックリスト

  • DROPREJECT の違いを答えられる(パケットを黙って捨てる vs 拒否応答返す)
  • ufw default deny incoming + 必要ポートのみ許可、を書ける
  • fail2ban-client status sshd で ban 中の IP を確認できる
  • AWS SG / GCP FW と OS の ufw を 両方 設定すべき理由を言える
  • 「自分を締め出さない」ためのチェックリスト(別セッション / クラウドコンソール)を答えられる

次のレッスン: Day 19 - 監視

明日は 監視 ─ サーバーが「動いている」を客観的に観測する。

top / htop / vmstat / iostat / ss、SLI/SLO、Prometheus + Grafana、アラート設計、「サイトが遅い」調査フロー。今日まで「守る」だったが、明日は「異常を検知する」へ。

Day 19: 監視