被扫爆了?Fail2Ban + Webhook:给你的 Linux 服务器装个“带监控的智能防盗门”

4 阅读5分钟

image.png 原文传送门:halo.4w.ink/?p=019ce628… 刚买的云服务器,一查 lastb 居然满屏幕都是爆破记录? 别再只改 SSH 端口了,那只是掩耳盗铃。分享一套我最近刚落地的自动化防御方案

Fail2Ban 自动封禁:多平台一键安装脚本,毫秒级响应恶意爆破。

Webhook 实时告警:拦截成功的瞬间,企业微信/飞书/钉钉直接弹窗,安全感拉满。

时区/配置全避坑:手把手教你配置,附带常用维护命令。

1.你的服务器正在被“围攻”吗?

如果你拥有一台暴露在公网上的 Linux 服务器,不妨现在就执行一下 lastb 命令。 看到那满屏幕、甚至几分钟就跳动一次的 ssh:notty 记录了吗?这些都是来自全球各地的自动化脚本,正在利用弱口令字典对你的 22 端口进行疯狂的暴力破解。在黑暗的网络角落里,数以万计的机器人每天都在不停地尝试敲开你的大门。

2.改端口真的安全吗?

很多管理员的第一反应是修改 SSH 默认端口。诚然,这能过滤掉 90% 的低级扫描,但在专业的端口扫描工具(如 Nmap)面前,这种做法无异于掩耳盗铃。

  • 防护滞后:你不知道谁在攻击你,直到服务器被攻破的那一刻。
  • 反馈缺失:传统的防火墙只会默默地拦截,管理员无法实时感知攻击频率和来源,处于信息孤岛。
  • 误伤隐患:如果你自己输错密码被锁,可能需要通过繁琐的控制台(VNC)去救援。

我们真正需要的,不是一个死板的“防盗门”,而是一个带监控的智能安保系统:它既能自动驱逐入侵者,又能第一时间把“抓获现场”的消息推送到你的手机上。

3.Fail2Ban + Webhook 的强强联手

为了实现这种“自动防御+即时反馈”的机制,我们将目光投向了两个利器:

  • Fail2Ban(防火墙守卫) :它是 Linux 下最著名的入侵防御框架。通过监听日志文件,它能在检测到多次登录失败后,自动调用 iptablesufw 策略将恶意 IP 永久或暂时封禁。
  • Webhook(即时通讯桥梁) :它是现代应用间通信的纽带。我们将 Fail2Ban 的封禁动作(Action)与企业微信、飞书或钉钉的 Webhook API 进行联动。

最终效果: 只要有人尝试爆破你的 SSH 密码,Fail2Ban 会在几秒钟内将其 IP “拉黑”,并在你的手机即时通讯工具中弹出一张精美的告警卡片,告诉你: “别担心,不速之客已被拦截!”

4.Fail2Ban 安装配置

创建一个sh文件,将下方内容复制至文件中保存,通过 bash you-file-name.sh执行

💡该脚本会自动识别系统版本并安装必要的 rsyslog 组件,确保日志能够被正常读取。

#!/bin/bash

# Install Fail2ban
# Support Ubuntu/Debian/CentOS/RHEL/Almalinux/Alpine/Arch Linux

set -e

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color


detect_os() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS=$ID
        VERSION=$VERSION_ID
    elif type lsb_release >/dev/null 2>&1; then
        OS=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
        VERSION=$(lsb_release -sr)
    elif [ -f /etc/redhat-release ]; then
        OS="rhel"
        VERSION=$(grep -oE '[0-9]+.[0-9]+' /etc/redhat-release)
    elif [ -f /etc/alpine-release ]; then
        OS="alpine"
        VERSION=$(cat /etc/alpine-release)
    else
        OS=$(uname -s | tr '[:upper:]' '[:lower:]')
        VERSION=$(uname -r)
    fi
}

install_fail2ban() {
    echo -e "${GREEN}Detected system: $OS $VERSION${NC}"

    case "$OS" in
        ubuntu|debian)
            apt-get update
            if ! command -v rsyslogd >/dev/null 2>&1; then
                echo -e "${YELLOW}rsyslog not installed. installing rsyslog...${NC}"
                apt-get install -y rsyslog
            else
                echo -e "${GREEN}rsyslog is already installed.${NC}"
            fi
            apt-get install -y fail2ban
            ;;
        centos|rhel|fedora|almalinux)
            if [ "$OS" = "rhel" ] && [ "${VERSION%%.*}" -ge 8 ]; then
                dnf install -y epel-release
                dnf install -y fail2ban
            else
                yum install -y epel-release
                yum install -y fail2ban
            fi
            ;;
        alpine)
            apk add --no-cache fail2ban
            ;;
        arch)
            pacman -Sy --noconfirm fail2ban
            ;;
        *)
            echo -e "${RED}Unsupported system${NC}"
            exit 1
            ;;
    esac
}

configure_fail2ban() {
    echo -e "${GREEN}Configure Fail2ban...${NC}"
    
    FAIL2BAN_CONF="/etc/fail2ban/jail.local"
    LOG_FILE=""
    BAN_ACTION=""

    if systemctl is-active --quiet firewalld 2>/dev/null; then
        BAN_ACTION="firewallcmd-ipset"
    elif systemctl is-active --quiet ufw 2>/dev/null || service ufw status 2>/dev/null | grep -q "active"; then
        BAN_ACTION="ufw"
    else
        BAN_ACTION="iptables-allports"
    fi

    if [ -f /var/log/secure ]; then
        LOG_FILE="/var/log/secure"
    else
        LOG_FILE="/var/log/auth.log"
        [ -f "$LOG_FILE" ] || touch "$LOG_FILE"
    fi

    cat <<EOF > "$FAIL2BAN_CONF"
#DEFAULT-START
[DEFAULT]
bantime = 600
findtime = 300
maxretry = 5
banaction = $BAN_ACTION
action = %(action_mwl)s
#DEFAULT-END

[sshd]
ignoreip = 127.0.0.1/8
enabled = true
filter = sshd
port = 22
maxretry = 5
findtime = 300
bantime = 600
banaction = $BAN_ACTION
action = %(action_mwl)s
logpath = $LOG_FILE
EOF
}

start_service() {
    echo -e "${GREEN}Start Fail2ban...${NC}"
    
    case "$OS" in
        ubuntu|debian)
            systemctl enable fail2ban
            systemctl restart fail2ban
            ;;
        centos|rhel|fedora|almalinux)
            systemctl enable fail2ban
            systemctl restart fail2ban
            ;;
        alpine)
            rc-update add fail2ban
            rc-service fail2ban start
            ;;
        arch)
            systemctl enable fail2ban
            systemctl restart fail2ban
            ;;
        *)
            echo -e "${YELLOW}The service cannot be started automatically. Please start manually!${NC}"
            ;;
    esac

    if command -v systemctl &> /dev/null; then
        systemctl status fail2ban || true
    else
        rc-service fail2ban status || true
    fi

    echo -e "${GREEN}Fail2ban is installed and started${NC}"
}

main() {
    detect_os
    install_fail2ban
    configure_fail2ban
    start_service
}

main "$@"

5.Fail2Ban 配置文件修改

如果你使用了第 4 节的一键脚本,基本的 jail.local 已经创建好了。本节主要用于讲解各项参数的含义,方便你进行个性化微调(如修改封禁时长、白名单等)

配置文件目录: /etc/fail2ban/jail.local

[sshd]
# 激活此监狱配置,设置为 true 才会生效
enabled = true

# 监控的端口号,如果你修改了 SSH 默认端口(比如改为 2222),这里也要同步修改
port = 22

# 指定使用的过滤器,通常对应 /etc/fail2ban/filter.d/sshd.conf,定义了匹配错误登录的正则表达式
filter = sshd

# Fail2Ban 扫描的日志文件路径。Ubuntu/Debian 通常是 /var/log/auth.log,CentOS 则是 /var/log/secure
logpath = /var/log/auth.log

# 最大尝试次数。在 findtime 时间段内,如果失败次数达到 2 次,该 IP 就会被封禁
maxretry = 2

# 统计失败次数的时间范围。这里指 60 秒内如果失败 2 次,即触发封禁条件
findtime = 60s

# 封禁时长。设置为 -1 代表“永久封禁”(慎用,除非你有极强的安全信心或配置了白名单)
bantime = -1

# 触发封禁后执行的动作(Action)。
# 这里采取了多 Action 组合模式:
# 1. 调用 ufw 动作进行防火墙拦截
# 2. 调用自定义的 wechat-webhook 动作发送微信通知
# 注意:多行 action 的写法中,后续行必须有缩进,以表示它们隶属于同一个 action 配置项
action = ufw
         wechat-webhook

⚠️ 重要提醒:在正式重启 Fail2Ban 之前,请务必在 [sshd] 段落中加入 ignoreip = 127.0.0.1/8 你的办公室或家里的公网IP 。 否则,万一你自己输错两次密码,由于设置了 bantime = -1 ,你将被永久锁在服务器外

6.联动Webhook脚本

mkdir -p /etc/fail2ban/action.d/
vim /etc/fail2ban/action.d/wechat-webhook.conf

💡 小贴士:如果收到通知的时间比北京时间慢了 8 小时,请参考以下命令修正系统时区: sudo timedatectl set-timezone Asia/Shanghai

粘贴以下内容(替换你的 Webhook 地址)

[Definition]
# 封禁动作(红色/橙色警告)
actionban = curl -s -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=[替换你的key]" -H "Content-Type: application/json" -d "{"msgtype":"markdown","markdown":{"content":"⚠️ **SSH暴力破解告警**\n> 被封禁IP:<font color=\"warning\"> <ip> </font>\n> 封禁时间:$(date +'%%Y-%%m-%%d %%H:%%M:%%S')\n> 失败次数:<failures>次\n> 封禁时长:永久封禁"}}"
# 封禁时长:<bantime>秒,我的配置bantime = -1所以是永久封禁

# 解封动作(绿色通知)
actionunban = curl -s -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=[替换你的key]" -H "Content-Type: application/json" -d "{"msgtype":"markdown","markdown":{"content":"✅ **SSH封禁自动解除**\n> 解禁IP:<font color=\"comment\"> <ip> </font>\n> 解禁时间:$(date +'%%Y-%%m-%%d %%H:%%M:%%S')\n> 状态:防火墙规则已移除"}}"

[Init]

7.配置文件检查

# 配置文件自检
fail2ban-client -t 
# 返回OK: configuration test is successful 就是通过
# 重启fail2ban
systemctl restart fail2ban

8.通知测试

手动封禁

fail2ban-client set sshd banip 1.4.4.4

手动解封

fail2ban-client set sshd unbanip 1.4.4.4

当有人爆破时,你会收到这样的红色预警

image.png

当管理员确认安全并手动解封后,会收到绿色通知

image.png

9.常用命令

# 重启服务
sudo systemctl restart fail2ban

# 重载配置-不重启服务,仅重新加载配置文件,更平滑
sudo fail2ban-client reload

# 启动/停止/查看服务状态
sudo systemctl start fail2ban
sudo systemctl stop fail2ban
sudo systemctl status fail2ban

# 状态查询命令
查看 Fail2Ban 运行概况(可以看到当前激活了哪些“监狱”)
sudo fail2ban-client status

# 查看特定监狱的详细信息
sudo fail2ban-client status sshd

# 手动解禁某个IP
sudo fail2ban-client set sshd unbanip <你的IP地址>

# 手动封禁某个IP
sudo fail2ban-client set sshd banip <恶意IP地址>