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"}]