【実践シェルスクリプト講座 第4回】鉄壁の守り!「SSH総当たり攻撃」を検知してIPを自動ブロックする

【実践シェルスクリプト講座 第4回】あなたのサーバーは狙われている!「SSH総当たり攻撃」を自動で撃退せよ

こんにちは!「リナックス先生」です。
全4回にわたるこの講座も、今回でいよいよ最終回。
前回までに、容量監視、バックアップ、ユーザー管理の自動化と、サーバー管理者の「三大面倒くさい業務」をスクリプトに任せることに成功しましたね。
コウ君、その後サーバーは平和?

コウ君

先生!おかげさまで、夜もぐっすり眠れています!
…と言いたいところなんですが、昨日なんとなくログを見ていたら、知らないIPアドレスから大量のログイン失敗の記録があったんです。
これって、もしかして攻撃されてますか!?

リナックス先生

気づいてしまったわね。
SSHのポート(22番)をインターネットに公開しているサーバーは、世界中のボットから「1分間に数十回」という頻度でログイン試行(攻撃)を受けているのが日常なの。
パスワードが強固ならすぐには破られないけれど、気持ち悪いし、サーバーの負荷にもなるわ。
最終回は、この「不届き者」を自動検知して、ファイアウォールで門前払いする「自動防衛システム」を作りましょう!

「実践シェルスクリプト講座」最終回のテーマは、「セキュリティの自動化」です。
ログ解析の王道ツール awk で攻撃者を特定し、AlmaLinux 9(RHEL 9)時代の標準ファイアウォールである firewalld を操作して、悪意あるIPアドレスをブラックリストに放り込むスクリプトを作成します。

本講座のカリキュラム(全4回)

これが最後の課題です。今まで学んだ知識を総動員して、サーバーを守り抜きましょう。
過去の記事を復習したい場合は、以下のリンクからジャンプできます。

  1. サーバーの異変を未然に防ぐ!「ディスク容量監視」と「ログローテーション」
  2. データ消失の悲劇を防ぐ!「自動バックアップ」と「エラー通知」
  3. ユーザー管理の効率化:CSVファイルから大量のユーザーとSSH鍵を一括生成する
  4. 【今回】セキュリティ監視:不正アクセス(SSH総当たり攻撃)を検知してIPを自動ブロックする

1. 敵を知る:攻撃の痕跡「secureログ」を読む

まずは、敵がどこに痕跡を残しているかを知る必要があります。
RHEL系(AlmaLinux, CentOS)のLinuxでは、認証に関するログは /var/log/secure に記録されます(Ubuntuなどは /var/log/auth.log です)。

実際に、以下のコマンドで「ログイン失敗」の記録を見てみましょう。

[root@localhost ~]# grep "Failed password" /var/log/secure | tail -5
Jan 25 10:00:01 server sshd[12345]: Failed password for root from 192.0.2.100 port 54321 ssh2
Jan 25 10:00:03 server sshd[12345]: Failed password for invalid user admin from 192.0.2.100 port 54322 ssh2
...

このように、「誰が(rootやadmin)」「どこから(192.0.2.100)」失敗したかが記録されています。
これを目視でチェックして手動でブロックするのは不可能です。
そこで、スクリプトの出番です。

2. ログ解析の魔法「awk」と「uniq」

スクリプトを作る前に、コマンドラインで「攻撃回数ランキング」を作ってみましょう。
これがスクリプトの心臓部になります。

ステップごとの解説

1. 失敗ログだけを抜き出す

grep "Failed password" /var/log/secure

2. IPアドレスの部分だけを抜き出す(awkを使用)

... | awk '{print $(NF-3)}'

💡 awkのテクニック

ログのフォーマットによっては、IPアドレスが「前から何番目」にあるか変わることがあります。
しかし、from 192.0.2.100 port... という並び順は概ね決まっています。
$(NF-3) は「後ろ(NF)から数えて4番目の要素」という意味です。これなら行の長さが変わってもIPを捕まえやすいです(※環境によって微調整が必要です)。

3. 集計して多い順に並べる

... | sort | uniq -c | sort -nr
  • sort: 同じIPを隣り合わせにする
  • uniq -c: 重複している行をまとめて、回数をカウントする
  • sort -nr: 回数(Number)の逆順(Reverse)で並べ替える

完成したコマンドライン

[root@localhost ~]# grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -5
   150 192.0.2.100
    45 203.0.113.10
    12 198.51.100.5

出ました!犯人は 192.0.2.100 ですね。
これを自動的にファイアウォールに登録するのが今回の目標です。

3. 鉄壁の守護神「block_attacker.sh」の実装

それでは、自動ブロックを行うスクリプトを作成します。
AlmaLinux 9では hosts.deny(TCP Wrapper)が廃止されているため、現代的な firewalld を使って制御します。

スクリプトの仕様

  1. /var/log/secure を解析し、過去5000行から失敗履歴を探す。
  2. 同じIPからの失敗が「5回」を超えていたら攻撃とみなす。
  3. そのIPを自身のホワイトリスト(除外リスト)と照合する(自分を締め出さないため)。
  4. Firewalldの「dropゾーン」にIPを追加して接続を拒否する。
  5. 結果をログと通知(前回作成した関数を使用)で知らせる。

スクリプト作成:block_attacker.sh

vim block_attacker.sh を作成します。

#!/bin/bash

# ==========================================
# 設定エリア
# ==========================================
LOG_FILE="/var/log/secure"
BLOCK_LOG="/var/log/block_attacker.log"
# 許容する失敗回数
LIMIT=5
# 解析する過去のログ行数(全行読むと重いため)
READ_LINES=5000

# ホワイトリスト(自分のIPや監視サーバーのIPなど)
# 正規表現で記述(例: 127.0.0.1 または 192.168.1.xxx)
WHITELIST="^127\.0\.0\.1$|^192\.168\.1\."

# ==========================================
# 関数:ログ出力
# ==========================================
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$BLOCK_LOG"
}

# ==========================================
# メイン処理
# ==========================================

# 1. ログファイルが存在するか確認
if [ ! -f "$LOG_FILE" ]; then
    log "ログファイルが見つかりません: $LOG_FILE"
    exit 1
fi

log "--- ログ解析開始 ---"

# 2. 攻撃者のIPリストを作成
# tailで直近のログを切り出し -> grepで失敗行抽出 -> awkでIP抽出 -> sort/uniqでカウント
# while read で「回数」と「IP」を変数に入れる
tail -n "$READ_LINES" "$LOG_FILE" \
| grep "Failed password" \
| awk '{print $(NF-3)}' \
| sort | uniq -c \
| while read COUNT IP; do

    # カウント数が閾値未満なら何もしない
    if [ "$COUNT" -lt "$LIMIT" ]; then
        continue
    fi

    # 3. ホワイトリストチェック
    if [[ "$IP" =~ $WHITELIST ]]; then
        # 許可IPなのでスキップ
        continue
    fi

    # 4. 既にブロック済みかチェック
    # firewall-cmd --list-sources --zone=drop で現在ブロック中のIP一覧が取れる
    # grep -q は「見つかったら成功(0)、見つからなかったら失敗(1)」を返すオプション
    firewall-cmd --zone=drop --list-sources | grep -q "$IP"
    if [ $? -eq 0 ]; then
        # 既に登録済みなのでスキップ
        continue
    fi

    # 5. ブロック実行!
    # --permanent を付けると再起動後も有効になるが、即時反映のためには reload が必要
    # 今回は即時反映(--permanentなし)と永続化(--permanentあり)の両方を打つのが確実
    
    log "【検知】攻撃IPを検出: $IP (失敗回数: $COUNT)"
    
    firewall-cmd --zone=drop --add-source="$IP"
    firewall-cmd --permanent --zone=drop --add-source="$IP"
    
    if [ $? -eq 0 ]; then
        log "【ブロック成功】$IP をdropゾーンに追加しました。"
        # ここに第2回で作った通知関数(send_alert)を入れると完璧!
        # send_alert "SSH攻撃検知: $IP をブロックしました (失敗 $COUNT 回)"
    else
        log "【エラー】$IP のブロックに失敗しました。"
    fi

done

log "--- 解析終了 ---"

⚠️ 最重要:ホワイトリストの設定を忘れずに!

このスクリプトは容赦なくブロックを行います。
もしあなたがパスワードを5回間違えたら、あなた自身がサーバーから締め出され、二度とログインできなくなります(いわゆるセルフBAN)。
WHITELIST 変数には、必ずあなたの会社の固定IPや、VPNのIP帯域などを記述してください。
もし固定IPがない場合は、一時的に閾値(LIMIT)を100回など大きくして様子を見るのが安全です。

4. Firewalldの「ゾーン」とは?

スクリプト内で登場した --zone=drop について補足します。
Firewalldには「信頼度」に応じた複数のゾーン(エリア)があります。

ゾーン名 挙動 用途
public デフォルト。許可した通信以外は拒否(reject)。 通常のインターネット公開用
drop 全ての通信を無視して破棄(drop)。相手に返事すらしない。 攻撃者・ブラックリスト用
trusted 全ての通信を許可。 信頼できる社内ネットワーク用

攻撃者のIPを drop ゾーンに追加するということは、サーバーがそのIPに対して「無(Null)」になるということです。
相手からはサーバーが存在しないかのように見えるため、最も効果的な防御手段となります。

5. 運用とメンテナンス:ブロック解除方法

万が一、間違って正常なIPをブロックしてしまった場合は、手動で解除する必要があります。
慌てず以下のコマンドを実行しましょう。

ブロック中のIP一覧を確認

[root@localhost ~]# firewall-cmd --zone=drop --list-sources
192.0.2.100 203.0.113.10

ブロック解除(許可)

# 一時的な設定からの削除
[root@localhost ~]# firewall-cmd --zone=drop --remove-source=192.0.2.100
# 永続的な設定からの削除
[root@localhost ~]# firewall-cmd --permanent --zone=drop --remove-source=192.0.2.100

6. 既存ツール「Fail2Ban」との違い

詳しい方なら、「それ、Fail2Banというツールを入れればいいのでは?」と思ったかもしれません。
正解です!実務では Fail2Ban という素晴らしいオープンソースソフトウェアを使うのが一般的です。

しかし、今回あえてスクリプトを自作したのには理由があります。

  1. 仕組みの理解: ツールに頼りきりだと、「なぜブロックされたか」「ログのどこを見ればいいか」がわからなくなります。
  2. カスタマイズ性: 自作スクリプトなら、「ブロックしたらSlackに通知」「社内のDBにIPを登録」といった独自の連携が自由自在です。
  3. 学習効果: ログ解析(grep/awk)とシステム制御(systemctl/firewalld)は、インフラエンジニアの必須スキルセットそのものです。

このスクリプトが書けるようになれば、SSHだけでなく、Webサーバー(Nginx/Apache)への攻撃や、アプリケーションのエラー検知にも応用が効くようになります。

講座のまとめ:エンジニアとしての第一歩

全4回にわたる「実践シェルスクリプト講座」、いかがでしたか?
最初は Hello World しか表示できなかったコウ君も、今ではこれだけのシステムを構築できるようになりました。

作ったもの 身についたスキル
第1回 ディスク監視・ログ掃除 変数、if文、cron定期実行
第2回 バックアップ・通知 tar/rsync、エラーハンドリング、Webhook通知
第3回 ユーザー一括作成 whileループ、テキスト読み込み、SSH鍵管理
第4回 自動ブロックシステム ログ解析(grep/awk)、Firewalld操作、セキュリティ意識
コウ君

先生、本当にありがとうございました!
最初は黒い画面(ターミナル)が怖かったけど、今は自分でスクリプトを書いてサーバーを動かすのが楽しくて仕方ありません!
もっと色々な自動化に挑戦してみます!

リナックス先生

その意気よ、コウ君!
「面倒くさい」を放置せず、「どうすれば楽できるか(自動化できるか)」を考え続けること。
それがプロのエンジニアへの最短ルートよ。
困ったときはいつでも man コマンドと、この講座を思い出してね!

これで本講座は完結ですが、Linuxの世界はまだまだ広いです。
ぜひ、あなた自身の専用サーバー(VPS)を手に入れて、壊しても怒られない環境で、思う存分スクリプトを書き殴ってください。
失敗の数だけ、あなたは強くなれます。

▼スクリプトの実験場に最適!推奨VPS

【2026年最新】Linuxサーバー構築におすすめのVPS比較3選!現役エンジニアが速度とコスパで厳選
Linuxの勉強、まだ「自分のPC」でやって消耗していませんか?「Linuxを覚えたいけど、環境構築でエラーが出て先に進めない…」「VirtualBoxを入れたらパソコンが重くなった…」これは、Linux学習を始める9割の人がぶつかる壁です...

コメント