SSH登录失败检测与IP封禁

代码脚本 · 安全

轻量级SSH暴力破解检测工具,分析auth.log登录失败日志,超过阈值自动用iptables封禁攻击IP,支持白名单和自动解封,无需安装fail2ban

详细内容

#!/bin/bash # ========================================== # SSH登录失败检测与IP封禁脚本 # 用途:轻量级防SSH暴力破解,自动封禁攻击IP # 使用方法:加入crontab每分钟执行一次 # ========================================== # ========== 配置 ========== MAX_FAILURES=5 # 最大失败次数(超过则封禁) BAN_TIME=3600 # 封禁时长(秒),0表示永久封禁 WHITELIST=("127.0.0.1" "::1") # IP白名单 LOG_FILE="/var/log/ssh_ban.log" AUTH_LOG="/var/log/auth.log" # Debian/Ubuntu # AUTH_LOG="/var/log/secure" # CentOS/RHEL STATE_FILE="/tmp/ssh_ban_state.txt" # ========================== mkdir -p "$(dirname "$LOG_FILE")" log_msg() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } is_whitelisted() { local ip="$1" for wl in "${WHITELIST[@]}"; do if [ "$ip" = "$wl" ]; then return 0 fi done # 检查是否是内网IP if echo "$ip" | grep -qE "^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)"; then return 0 fi return 1 } ban_ip() { local ip="$1" local reason="$2" if is_whitelisted "$ip"; then log_msg " 跳过白名单IP: $ip" return fi # 检查是否已封禁 if iptables -L INPUT -n --line-numbers 2>/dev/null | grep -q "$ip"; then return fi log_msg "🔴 封禁IP: $ip (原因: $reason)" iptables -I INPUT -s "$ip" -j DROP 2>/dev/null # 记录封禁时间(用于自动解封) if [ "$BAN_TIME" -gt 0 ]; then echo "$ip $(date +%s)" >> "$STATE_FILE" fi # 发送告警(可选) log_msg " 已封禁攻击IP: $ip" } unban_expired() { if [ "$BAN_TIME" -le 0 ] || [ ! -f "$STATE_FILE" ]; then return fi local now=$(date +%s) local new_state="" while read -r ip ban_time; do [ -z "$ip" ] && continue if [ $((now - ban_time)) -ge "$BAN_TIME" ]; then log_msg "🟢 自动解封IP: $ip (封禁时长已满)" iptables -D INPUT -s "$ip" -j DROP 2>/dev/null else new_state="$new_state$ip $ban_time\n" fi done < "$STATE_FILE" echo -e "$new_state" > "$STATE_FILE" } # 检查iptables是否可用 if ! command -v iptables >/dev/null 2>&1; then log_msg "❌ iptables未安装,无法封禁IP" exit 1 fi # 自动解封过期IP unban_expired # 检测登录失败 log_msg "===== 开始检测SSH登录失败 =====" # 从日志中提取失败登录的IP if [ -f "$AUTH_LOG" ]; then # 提取最近的失败登录(只分析最近10分钟) FAIL_IPS=$(grep -i "failed password\|authentication failure\|invalid user" "$AUTH_LOG" 2>/dev/null | awk -v cutoff="$(date -d '10 minutes ago' '+%b %e %H:%M')" ' { # 简单的时间过滤(匹配最近的日志) print $0 }' | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" | sort | uniq -c | sort -rn) else # 尝试用journalctl(systemd系统) if command -v journalctl >/dev/null 2>&1; then FAIL_IPS=$(journalctl -u sshd --since "10 minutes ago" 2>/dev/null | grep -i "failed password\|authentication failure\|invalid user" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" | sort | uniq -c | sort -rn) else log_msg "❌ 找不到SSH日志文件" exit 1 fi fi if [ -z "$FAIL_IPS" ]; then log_msg "✅ 最近10分钟无SSH登录失败记录" else TOTAL_BAN=0 while read -r count ip; do [ -z "$ip" ] && continue log_msg " IP $ip: 失败 $count 次" if [ "$count" -ge "$MAX_FAILURES" ]; then ban_ip "$ip" "10分钟内SSH登录失败${count}次" TOTAL_BAN=$((TOTAL_BAN + 1)) fi done <<< "$FAIL_IPS" log_msg "本次封禁 $TOTAL_BAN 个IP" fi # 显示当前封禁列表 BANNED=$(iptables -L INPUT -n --line-numbers 2>/dev/null | grep "DROP" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") if [ -n "$BANNED" ]; then log_msg "当前已封禁IP:" echo "$BANNED" | while read ip; do log_msg " - $ip" done fi log_msg "===== 检测完成 ====="

适配环境

适配系统:CentOS 7+,Ubuntu 18.04+,Debian 10+

依赖环境:bash 4.0+

参数说明

[{"name": "MAX_FAILURES", "label": "\u6700\u5927\u5931\u8d25\u6b21\u6570", "default": "5"}, {"name": "BAN_TIME", "label": "\u5c01\u7981\u65f6\u957f(\u79d2\uff0c0=\u6c38\u4e45)", "default": "3600"}, {"name": "WHITELIST", "label": "\u767d\u540d\u5355IP(\u7a7a\u683c\u5206\u9694)", "default": "127.0.0.1"}, {"name": "AUTH_LOG", "label": "SSH\u65e5\u5fd7\u8def\u5f84", "default": "/var/log/auth.log"}]
安全SSH暴力破解IP封禁fail2ban

更多安全