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.log1. ファイアウォールとは(パケットフィルタリング)
ファイアウォールの本質
ネットワーク経由のパケットを、ルールに基づいて通す/捨てる機能。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 番を許可」 |
| 内部から外部 API | OUTPUT | 「外向き接続の制限」(通常は許可) |
| サーバーが踏み台になって裏の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。
対比表
| ufw | firewalld | iptables | nftables | |
|---|---|---|---|---|
| 抽象レベル | 高(人間向け) | 高(人間向け) | 中(直接) | 中(直接) |
| 構文の難易度 | 易 (ufw allow 22) | 中 (zone 概念) | 難(長い) | 中(宣言的) |
| 用途 | Ubuntu の VPS | RHEL/CentOS のデスクトップ・サーバー | 既存システム / スクリプト | 新規プロジェクト |
| ディストリ | Ubuntu/Debian | RHEL/Fedora/CentOS | どこでも | どこでも (modern) |
| 構文例 | ufw allow 80/tcp | firewall-cmd --add-port=80/tcp | iptables -A INPUT -p tcp --dport 80 -j ACCEPT | nft 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 ruleset3. 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 incoming→ufw allow ssh/http/https→ufw 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.logfail2ban の仕組み
- ログファイルを監視(inotify でリアルタイム)
- フィルタ正規表現に一致する行をカウント(例: SSH の認証失敗)
- 閾値を超えたIPを ban(findtime 内に maxretry 回失敗)
- ban の方法: iptables / ufw / route などでパケットドロップ
- 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. ここまで来たら再起動して全部反映確認
reboot10. クラウドのSecurity Groupとの関係
[インターネット]
│
[AWS ALB / CloudFront / Cloudflare] ← WAF
│
[AWS Security Group] ← クラウド側ファイアウォール(必須)
│
[EC2 インスタンス]
│
[ufw / firewalld] ← VM内ファイアウォール(任意だが推奨)
│
[Nginx] ← L7 制御(limit_req 等)
│
[アプリ]
AWS Security Group と ufw の二段防御
項目 Security Group ufw 動作レイヤ クラウド側(インスタンスの外) OS内(カーネル netfilter) 設定方法 AWSコンソール / Terraform / API コマンド or 設定ファイル ステートフル はい はい 適用範囲 複数インスタンスに共通 そのVM単体 変更コスト 即時反映、無料 コマンド数発 監査ログ CloudTrail systemd 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 saveiptables のチェーンとテーブル
テーブル:
- 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 AnywhereSSH を全世界に開放。これ自体は不可避な場面もあるが(自宅IPが固定でない、リモートワーカーがいる)、対策無しは危険。
必須の対策:
- パスワード認証無効化(鍵認証のみ)
- root login 無効化
- fail2ban で総当たり攻撃を緩和
- 可能なら ALLOW を信頼できるIP範囲に限定(オフィスIP、VPN経由)
アンチパターン: fail2ban なしでパスワード認証 + 22 world
# sshd_config PasswordAuthentication yes Port 22 # ufw 22/tcp ALLOW IN Anywhereこれは数時間〜数日で侵入される構成。
攻撃者は:
- 22番に総当たりを開始(秒間数十回)
admin,root,ubuntu,testなどの一般的なユーザー名と弱パスワードを試行- 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秒で見るチェックリスト:
sudo fail2ban-client status sshd→ ban中のIP数の異常はないかlast | head -10→ 自分の知らないログインがないかsudo tail -100 /var/log/auth.log | grep -i fail→ 認証失敗の傾向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 |
| 手動unban | sudo 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)。
自己評価チェックリスト
-
DROPとREJECTの違いを答えられる(パケットを黙って捨てる 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: 監視 へ