当前位置:首页>Linux>Linux 日志实时告警实战:Nginx 5xx 与 SSH 暴力破解监控

Linux 日志实时告警实战:Nginx 5xx 与 SSH 暴力破解监控

  • 2026-10-11 07:57:20
Linux 日志实时告警实战:Nginx 5xx 与 SSH 暴力破解监控

前言

在 Linux 运维中,有常见的两类风险:

  • Nginx 出现持续 5xx,业务异常但无人感知
  • SSH 被持续暴力破解,安全风险不断累积

很多人第一反应是上 ELK、Prometheus,但对中小规模场景来说,这往往成本过高。

其实,只用系统自带工具(tail + awk + curl),就可以实现一套稳定、实时、低成本的日志告警方案。

但要注意:👉 绝大多数“网上脚本”都存在严重问题,例如:

  • pipeline 子 shell 导致计数失效
  • 伪“1分钟窗口”(实际只是定时清零)
  • grep 缓冲导致告警延迟
  • 日志轮转后监控中断

一、设计原则

这套方案遵循以下原则:

1 单进程状态管理(避免 shell 坑)

所有统计逻辑在 awk 内完成,避免:

  • 子进程变量隔离
  • 状态不一致

2 真正的滑动时间窗口

采用:

  • systime()
     + 事件清理机制
  • 而不是简单“每分钟清零”

👉 实现“近似实时滑动窗口”


3 不依赖日志格式细节

只依赖:

  • $1
     → IP
  • $9
     → HTTP 状态码

避免 fragile 正则


4 低依赖

仅依赖:

  • tail
  • awk
  • curl

5 安全优先

  • 不使用 eval
  • 支持白名单
  • webhook 校验严格

二、终极生产脚本

保存为:

/usr/local/bin/log-alert.sh
#!/bin/bash# ================= 配置项 =================NGINX_LOG="/var/log/nginx/access.log"SSH_MODE="file"# file 或 journaldSSH_LOG="/var/log/secure"WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的key"NGINX_THRESHOLD=10SSH_THRESHOLD=5SILENT=300STATE_DIR="/dev/shm/log_alert"mkdir -p "$STATE_DIR"chmod 700 "$STATE_DIR"WHITELIST=("127.0.0.1""10.0.0.0/8""192.168.0.0/16")# ================= 工具检查 =================for cmd in tail awk curl; docommand -v $cmd >/dev/null || { echo"缺少依赖: $cmd"; exit 1; }done# ================= webhook 校验 =================resp=$(curl -s --max-time 5 "$WEBHOOK")echo"$resp" | grep -q '"errcode":0' || {echo"Webhook 无效或不可用"exit 1}# ================= 告警函数 =================send_alert() {local title="$1"local content="$2"    curl -s -o /dev/null -X POST "$WEBHOOK" \    -H "Content-Type: application/json" \    -d "{        \"msgtype\": \"text\",        \"text\": {\"content\": \"${title}\n${content}\"}    }" &}# ================= 白名单判断 =================is_white() {local ip="$1"    [[ "$ip" == "127.0.0.1" ]] && return 0for w in"${WHITELIST[@]}"; do        [[ "$ip" == "$w" ]] && return 0donereturn 1}# ================= NGINX 监控 =================tail -F "$NGINX_LOG" | awk -v t="$NGINX_THRESHOLD" -v s="$SILENT"'{    ip=$1    code=$9    now=systime()    if (code !~ /^5[0-9][0-9]$/) next    key=ip    events[key][++cnt[key]]=now    # 清理旧数据    for (i in events[key]) {        if (now - events[key][i] > 60) delete events[key][i]    }    n=0    for (i in events[key]) n++    if (n >= t && now - last[key] > s) {        last[key]=now        print "NGINX|"ip"|"n        fflush()    }}' | while IFS='|'read -r type ip count; do    is_white "$ip" && continue    send_alert "Nginx 5xx告警""IP: $ip\n1分钟: $count 次"done &# ================= SSH 监控 =================if [[ "$SSH_MODE" == "journald" ]]; then    SRC="journalctl -f _COMM=sshd"else    SRC="tail -F $SSH_LOG"fibash -c "$SRC" | grep "Failed password" | awk -v t="$SSH_THRESHOLD" -v s="$SILENT"'{    for(i=1;i<=NF;i++){        if($i=="from"){ ip=$(i+1); break }    }    if(ip=="") next    now=systime()    key=ip    events[key][++cnt[key]]=now    for(i in events[key]){        if(now - events[key][i] > 60) delete events[key][i]    }    n=0    for(i in events[key]) n++    if(n >= t && now - last[key] > s){        last[key]=now        print "SSH|"ip"|"n        fflush()    }}' | while IFS='|'read -r type ip count; do    is_white "$ip" && continue    send_alert "SSH暴力破解告警""IP: $ip\n失败次数: $count"done &wait -nexit 1

三、systemd

[Unit]Description=Log Alert ServiceAfter=network.target[Service]ExecStart=/bin/bash /usr/local/bin/log-alert.shRestart=alwaysRestartSec=3StandardOutput=journalStandardError=journalNoNewPrivileges=yesProtectSystem=full[Install]WantedBy=multi-user.target

四、必须知道的几个关键点

1 这是“近似滑动窗口”

不是精确时间序列系统,但对运维告警完全够用


2 /dev/shm 是内存

👉 重启后状态会丢失(正常)


3 默认只支持简单白名单

CIDR 未做完整实现(避免复杂度)


4 日志格式必须标准

否则需要改 $1 $9

如果你觉得本文对你有帮助,欢迎点赞、推荐、转发,关注我,后续会分享更多Linux入门干货!

文 / 零距技术仓记录每一次真实的折腾 (#^.^#)🚀 想看到更多实用折腾技巧?👉 先关注💬 评论区说说你的经历或想看的内容👍 点赞表示支持🔁 顺手分享给也在折腾的人,让大家都少踩坑 😎

最新文章

随机文章