Files
cdn/bash/setup_f2b.sh
T
Mr.Lin e270f0f569 Add Fail2Ban installation and configuration script
This script installs and configures Fail2Ban on Debian 12/13, including old version detection, uninstallation, and whitelist migration. It also performs an automatic ban test while excluding the current public IP.
2026-05-21 21:43:08 +08:00

176 lines
5.1 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
set -euo pipefail
# Debian 12/13 Fail2Ban SSH 硬化脚本(含旧版检测/卸载/迁移白名单/封禁测试排除自己公网IP)
# 用法:
# bash install-fail2ban-debian.sh
# 可选白名单:
# IGNORE_IPS="127.0.0.1/8 ::1 你的固定IP" bash install-fail2ban-debian.sh
if [ "$(id -u)" -ne 0 ]; then
echo "请以 root 权限运行"
exit 1
fi
CONF_FILE="/etc/fail2ban/jail.d/99-sshd-hardening.local"
F2B_LOCAL="/etc/fail2ban/fail2ban.local"
TS="$(date +%Y%m%d-%H%M%S)"
IGNORE_IPS="${IGNORE_IPS:-127.0.0.1/8 ::1}"
# 获取当前公网 IP,用于封禁测试排除
PUB_IP=$(curl -s https://ifconfig.me || true)
if [[ -n "$PUB_IP" ]]; then
echo "检测到当前公网 IP: $PUB_IP (封禁测试将自动排除)"
else
PUB_IP=""
echo "未能检测公网 IP,封禁测试需手动注意不要封自己的 IP"
fi
OLD_INSTALLED=false
if command -v fail2ban-client >/dev/null 2>&1; then
OLD_INSTALLED=true
echo "检测到已有 Fail2Ban 安装"
read -p "是否删除旧版并安装新版本?(y/n) " yn
case "$yn" in
[Yy]* )
echo "停止 Fail2Ban 服务..."
systemctl stop fail2ban || true
echo "备份旧配置..."
mkdir -p /root/fail2ban-backup-${TS}
cp -r /etc/fail2ban/* /root/fail2ban-backup-${TS}/ || true
echo "已备份到 /root/fail2ban-backup-${TS}/"
# 自动提取旧 sshd jail ignoreip
for OLD_FILE in /etc/fail2ban/jail.local /etc/fail2ban/jail.d/sshd.local; do
if [ -f "$OLD_FILE" ]; then
OLD_IGNORE=$(grep -E '^ignoreip' "$OLD_FILE" | awk -F= '{print $2}' | xargs || true)
if [ -n "$OLD_IGNORE" ]; then
echo "检测到旧 ignoreip: $OLD_IGNORE"
IGNORE_IPS="$IGNORE_IPS $OLD_IGNORE"
fi
fi
done
echo "卸载 Fail2Ban..."
apt-get remove --purge -y fail2ban
rm -rf /etc/fail2ban
;;
* )
echo "保留旧版 Fail2Ban"
echo "检测当前 sshd jail 状态:"
fail2ban-client status sshd || echo "未启用 sshd jail 或旧版不支持 systemd backend"
echo "查看旧封禁 IP(如有):"
fail2ban-client status sshd | grep "Banned IPs" || echo "无已封禁 IP"
exit 0
;;
esac
fi
echo "安装 Fail2Ban 与 nftables..."
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y fail2ban nftables python3-systemd
echo "检测 SSH 端口..."
SSHD_PORTS="$(sshd -T 2>/dev/null | awk '$1 == "port" {print $2}' | paste -sd, - || true)"
SSHD_PORTS="${SSHD_PORTS:-22}"
echo "SSH 端口:${SSHD_PORTS}"
echo "备份已有配置(如果存在)..."
[ -f "$CONF_FILE" ] && cp "$CONF_FILE" "${CONF_FILE}.bak.${TS}"
[ -f "$F2B_LOCAL" ] && cp "$F2B_LOCAL" "${F2B_LOCAL}.bak.${TS}"
mkdir -p /etc/fail2ban/jail.d
echo "写入 Debian 12/13 兼容配置:$CONF_FILE"
cat > "$CONF_FILE" <<EOF
[DEFAULT]
ignoreip = ${IGNORE_IPS}
backend = systemd
banaction = nftables-multiport
banaction_allports = nftables-allports
allowipv6 = auto
usedns = no
bantime = 10d
findtime = 1h
maxretry = 3
bantime.increment = true
bantime.factor = 1
bantime.maxtime = 20w
bantime.rndtime = 10m
[sshd]
enabled = true
port = ${SSHD_PORTS}
filter = sshd[mode=aggressive]
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd
EOF
echo "写入 Fail2Ban 本地配置:$F2B_LOCAL"
cat > "$F2B_LOCAL" <<EOF
[Definition]
dbpurgeage = 180d
EOF
echo "测试 Fail2Ban 配置..."
fail2ban-client -t
echo "启用并重启 Fail2Ban..."
systemctl enable --now fail2ban
systemctl restart fail2ban
echo "------------------------------------------------"
echo "安装与配置完成!"
echo "已合并旧版白名单,当前 ignoreip: $IGNORE_IPS"
echo "查看 sshd jail 状态: fail2ban-client status sshd"
echo "查看 nftables 封禁规则: nft list ruleset | grep -i f2b -A20"
echo "查看 Fail2Ban 日志: journalctl -u fail2ban -n 100 --no-pager"
echo "------------------------------------------------"
# 自动封禁测试(排除当前公网 IP
read -p "是否进行自动封禁测试?(y/n) " testyn
if [[ "$testyn" =~ ^[Yy] ]]; then
NUM_IP=3
echo "抓取最近 $NUM_IP 个失败登录 IP ..."
FAILED_IPS=$(journalctl -u ssh.service -p err --no-pager \
| grep "Failed password" \
| awk '{for(i=1;i<=NF;i++){if($i ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/){print $i}}}' \
| sort | uniq -c | sort -nr | awk '{print $2}' | head -n $NUM_IP)
if [ -z "$FAILED_IPS" ]; then
echo "未找到失败登录 IP,建议手动尝试几次错误密码登录后再测试"
exit 0
fi
# 排除当前公网 IP
SAFE_IPS=""
for IP in $FAILED_IPS; do
if [[ "$IP" == "$PUB_IP" ]]; then
echo "跳过封禁当前公网 IP: $IP"
else
SAFE_IPS="$SAFE_IPS $IP"
fi
done
if [[ -z "$SAFE_IPS" ]]; then
echo "没有可封禁的测试 IP(都被排除掉了),跳过封禁测试"
exit 0
fi
echo "将封禁以下测试 IP $SAFE_IPS"
for IP in $SAFE_IPS; do
echo "模拟封禁 IP: $IP"
fail2ban-client set sshd banip $IP
done
echo
echo "查看 sshd jail 状态:"
fail2ban-client status sshd
echo
echo "如需解封测试 IP,可运行:"
for IP in $SAFE_IPS; do
echo " fail2ban-client set sshd unbanip $IP"
done
fi