タイトル負けしてます。解析なんてしてません。はい。ごめんなさい。
ということで、毎度のおまじない。
この記事は忘備録です。ド素人が思い付きでやっつけ仕事で行ったので、何が起こっても責任は取りませんので悪しからず。
さて、先日パスワードが漏れてるぞーとwordpressに注意されたので、せっかくならログ解析モドキでもしてみようと思いました。なのでその忘備録です。
到着地点:dockerの吐き出したログから怪しげなモノを拾い出し、Discordに通知を送る。
ではまず、docker のログを拾い集めるところから。
$/usr/local/bin/docker-log-discord.sh
#!/bin/bash
WEBHOOK_URL="https://discord.com/api/webhooks/aaaaa/xxxxx"
ALERT_LOG="/tmp/docker-alerts.log"
echo "[DEBUG] $(date): docker-log-discord.sh started" >> /var/log/docker-log-discord.log
echo "==== $(date) ====" > $ALERT_LOG
for c in $(docker ps --format "{{.Names}}"); do
echo "### $c" >> $ALERT_LOG
docker logs --since 6m "$c" 2>&1 \
| grep -vE "(tt-rss|Channel)" \
| grep -Ei "(failed|unauthorized|forbidden|denied|attack|intrusion| 403 | 401 )" >> $ALERT_LOG
done
# Pythonにログ送信を任せる
/usr/bin/python3 /usr/local/bin/docker-log-discord.py
docker logsから6分間のログを引っ張る。:–since 6m
これが一時間引っ張りたいなら1hとすればいい。
いろんなdockerが動いているので、forループで名前の数だけ繰り返す:–format “{{.Names}}”
この状態では全部のログが集まってるはずなので、この中から拾いたいところだけをピックアップする。まずはいらない部分を消す:grep -vE
次に必要な部分をチョイスする:grep -Ei
さて、これで見たいログだけが残ったことになる。これを、DiscordのWeb hookが受け取れるjson形式に整形して投げ渡す。この部分はPythonに任せる。
では、そのPythonに取り掛かる。
$ cat /usr/local/bin/docker-log-discord.py
#!/usr/bin/env python3
import requests
import os
import hashlib
WEBHOOK_URL = "https://discord.com/api/webhooks/aaaaa/xxxxx"
ALERT_LOG = "/tmp/docker-alerts.log"
HASH_RECORD_FILE = "/var/lib/docker-log-alert/docker-alerts-hash.txt"
# 過去に送ったログのハッシュを読み込み
sent_hashes = set()
os.makedirs(os.path.dirname(HASH_RECORD_FILE), exist_ok=True)
if os.path.exists(HASH_RECORD_FILE):
with open(HASH_RECORD_FILE, "r") as f:
sent_hashes = set(line.strip() for line in f.readlines())
if os.path.exists(ALERT_LOG) and os.path.getsize(ALERT_LOG) > 0:
with open(ALERT_LOG, "r") as f:
lines = f.readlines()
current_container = None
for line in lines:
line = line.strip()
if not line:
continue
if line.startswith("===="):
continue # 日付行はスキップ
if line.startswith("### "):
current_container = line.replace("###", "").strip()
continue
# ハッシュを計算
log_hash = hashlib.sha256(line.encode("utf-8")).hexdigest()
# 既に送信済みならスキップ
if log_hash in sent_hashes:
continue
# メッセージ送信
log_snippet = line[:1900]
container_label = f"[`{current_container}`]" if current_container else "[unknown]"
payload = {
"content": f"🚨 **Dockerログアラート** {container_label} 🚨\n```log\n{log_snippet}\n```"
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(WEBHOOK_URL, json=payload, headers=headers)
if response.status_code == 204:
print(f"[OK] {container_label} -> {line}")
sent_hashes.add(log_hash) # 成功したら記録
else:
print(f"[ERROR] Discord通知失敗: {response.status_code}")
# 成功したハッシュをファイルに保存
with open(HASH_RECORD_FILE, "w") as f:
for h in sent_hashes:
f.write(h + "\n")
else:
print("ログが空なので送信しませんでした。")
さて、ここで一つお知らせが。
Pythonなんて全く知りません。これっぽっちも触ったことありません。ちょっとは勉強してみようかなーと半年くらい前に思っただけです。
なので、chatGPTにポイっと丸投げしたら、上記が返ってきました。すごいね。
ということで、完成。
これを、5分おきにcronで実行すればOK.
$docker-log-discord
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * root /usr/local/bin/docker-log-discord.sh >> /var/log/docker-log-discord.log 2>&1
おわり。