【実践シェルスクリプト講座 第4回】あなたのサーバーは狙われている!「SSH総当たり攻撃」を自動で撃退せよ
こんにちは!「リナックス先生」です。
全4回にわたるこの講座も、今回でいよいよ最終回。
前回までに、容量監視、バックアップ、ユーザー管理の自動化と、サーバー管理者の「三大面倒くさい業務」をスクリプトに任せることに成功しましたね。
コウ君、その後サーバーは平和?
先生!おかげさまで、夜もぐっすり眠れています!
…と言いたいところなんですが、昨日なんとなくログを見ていたら、知らないIPアドレスから大量のログイン失敗の記録があったんです。
これって、もしかして攻撃されてますか!?
気づいてしまったわね。
SSHのポート(22番)をインターネットに公開しているサーバーは、世界中のボットから「1分間に数十回」という頻度でログイン試行(攻撃)を受けているのが日常なの。
パスワードが強固ならすぐには破られないけれど、気持ち悪いし、サーバーの負荷にもなるわ。
最終回は、この「不届き者」を自動検知して、ファイアウォールで門前払いする「自動防衛システム」を作りましょう!
「実践シェルスクリプト講座」最終回のテーマは、「セキュリティの自動化」です。
ログ解析の王道ツール awk で攻撃者を特定し、AlmaLinux 9(RHEL 9)時代の標準ファイアウォールである firewalld を操作して、悪意あるIPアドレスをブラックリストに放り込むスクリプトを作成します。
本講座のカリキュラム(全4回)
これが最後の課題です。今まで学んだ知識を総動員して、サーバーを守り抜きましょう。
過去の記事を復習したい場合は、以下のリンクからジャンプできます。
- サーバーの異変を未然に防ぐ!「ディスク容量監視」と「ログローテーション」
- データ消失の悲劇を防ぐ!「自動バックアップ」と「エラー通知」
- ユーザー管理の効率化:CSVファイルから大量のユーザーとSSH鍵を一括生成する
- 【今回】セキュリティ監視:不正アクセス(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 を使って制御します。
スクリプトの仕様
/var/log/secureを解析し、過去5000行から失敗履歴を探す。- 同じIPからの失敗が「5回」を超えていたら攻撃とみなす。
- そのIPを自身のホワイトリスト(除外リスト)と照合する(自分を締め出さないため)。
- Firewalldの「dropゾーン」にIPを追加して接続を拒否する。
- 結果をログと通知(前回作成した関数を使用)で知らせる。
スクリプト作成: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 という素晴らしいオープンソースソフトウェアを使うのが一般的です。
しかし、今回あえてスクリプトを自作したのには理由があります。
- 仕組みの理解: ツールに頼りきりだと、「なぜブロックされたか」「ログのどこを見ればいいか」がわからなくなります。
- カスタマイズ性: 自作スクリプトなら、「ブロックしたらSlackに通知」「社内のDBにIPを登録」といった独自の連携が自由自在です。
- 学習効果: ログ解析(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



コメント