< 自用文 OS 有关 > (续)发现正在被攻击 后的自救 Fail2ban + IPset + UFW 工作流程详解
继上编:< 自用文 主机 USC 记录:> 发现正在被攻击 后的自救-CSDN博客
环境:
改进:
以下是把代码,懒得写,扔给了 AI ,让它出的:
Fail2ban + IPset + UFW 工作流程详解
工具概述
1. UFW (Uncomplicated Firewall)
- 作用: iptables 的简化前端
- 功能: 基础防火墙规则管理
- 特点: 用户友好的规则语法
2. IPset
- 作用: IP地址集合管理工具
- 功能: 高效存储和匹配大量IP地址
- 特点: 比传统iptables规则更高效
3. Fail2ban
- 作用: 入侵检测和自动封禁系统
- 功能: 分析日志、检测攻击、触发封禁动作
- 特点: 智能攻击模式识别
完整工作流程
阶段1: 初始化和基础防护
[系统启动] → [UFW启动] → [建立基础防火墙规则]↓
[IPset工具初始化] → [创建IP集合]↓
[Fail2ban启动] → [读取配置] → [开始监控日志]
阶段2: 攻击检测流程
[攻击者连接] → [服务日志记录] → [Fail2ban监控]↓
[模式匹配] → [计数器增加] → [达到阈值?]↓ ↓ ↓[继续监控] [重置计数器] [触发封禁]
阶段3: 封禁执行流程
[Fail2ban检测到攻击] ↓
[调用Action脚本]↓
[IPset添加恶意IP] → [ipset add fail2ban-ssh 192.168.1.100]↓
[UFW规则生效] → [DROP来自IPset集合的流量]↓
[攻击者被阻断]
技术架构图
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 网络流量 │────│ UFW/iptables │────│ 目标服务 │
│ │ │ │ │ (SSH/HTTP) │
└─────────────────┘ └─────────────────┘ └─────────────────┘│ ││ │┌─────────────┐ ┌─────────────┐│ IPset │ │ 日志文件 ││ (IP集合) │ │ │└─────────────┘ └─────────────┘│ │└──────┬──────────────────┘│┌─────────────┐│ Fail2ban ││ (监控分析) │└─────────────┘
详细配置示例
1. UFW基础配置
# 启用UFW
sudo ufw enable# 默认策略
sudo ufw default deny incoming
sudo ufw default allow outgoing# 允许SSH (在配置fail2ban之前)
sudo ufw allow ssh# 创建IPset规则 (UFW会自动处理)
# 实际在iptables中创建规则引用IPset
2. IPset配置
# 创建IP集合
sudo ipset create fail2ban-ssh hash:ip timeout 86400# 手动添加IP到集合 (通常由fail2ban自动执行)
sudo ipset add fail2ban-ssh 192.168.1.100# 查看集合内容
sudo ipset list fail2ban-ssh
3. Fail2ban配置 (/etc/fail2ban/jail.local)
[DEFAULT]
# 使用ipset作为封禁后端
banaction = iptables-ipset-proto6[name=fail2ban-ssh][sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400
findtime = 600# 使用ipset action
action = iptables-ipset-proto6[name=fail2ban-ssh, port=ssh, protocol=tcp]
数据流详解
1. 正常流量处理
外部请求 → UFW检查 → 基础规则匹配 → 允许通过 → 到达服务↓检查IPset集合↓IP不在黑名单 → 继续处理
2. 恶意流量处理
攻击流量 → UFW检查 → IPset集合匹配 → 发现恶意IP → 直接丢弃↓记录封禁日志
3. 新攻击检测
新攻击 → 到达服务 → 认证失败 → 写入日志↓Fail2ban监控 → 模式匹配 → 计数增加↓达到阈值 → 调用action → 添加IP到IPset↓下次请求 → UFW/IPset阻断
性能优势分析
传统iptables方式
# 每个被封IP都要一条规则
iptables -I INPUT -s 192.168.1.100 -j DROP
iptables -I INPUT -s 192.168.1.101 -j DROP
iptables -I INPUT -s 192.168.1.102 -j DROP
# ... 可能有数千条规则
问题: 规则数量多时,匹配效率低下
IPset优化方式
# 只需一条规则引用整个集合
iptables -I INPUT -m set --match-set fail2ban-ssh src -j DROP# 所有IP存储在高效的哈希表中
ipset add fail2ban-ssh 192.168.1.100
ipset add fail2ban-ssh 192.168.1.101
ipset add fail2ban-ssh 192.168.1.102
优势: O(1) 时间复杂度查找,支持数万IP
实际工作示例
场景:SSH暴力破解攻击
-
攻击开始
攻击者IP: 203.0.113.100 连续尝试SSH登录,密码错误
-
日志记录
/var/log/auth.log: Failed password for root from 203.0.113.100 port 22 ssh2 Failed password for admin from 203.0.113.100 port 22 ssh2 Failed password for user from 203.0.113.100 port 22 ssh2
-
Fail2ban检测
fail2ban监控到3次失败 达到maxretry=3的阈值 触发封禁action
-
IPset执行
# Fail2ban执行命令 ipset add fail2ban-ssh 203.0.113.100
-
UFW/iptables生效
# 现有规则开始匹配IPset iptables -I INPUT -m set --match-set fail2ban-ssh src -j DROP
-
结果
攻击者IP被立即阻断 后续请求直接在内核层面丢弃 不再到达SSH服务
监控和管理
查看当前状态
# 查看fail2ban状态
sudo fail2ban-client status sshd# 查看IPset内容
sudo ipset list fail2ban-ssh# 查看UFW状态
sudo ufw status verbose# 查看iptables规则
sudo iptables -L INPUT -n -v
手动管理
# 手动解封IP
sudo fail2ban-client set sshd unbanip 203.0.113.100# 直接从IPset移除
sudo ipset del fail2ban-ssh 203.0.113.100# 查看封禁历史
sudo tail -f /var/log/fail2ban.log
优势总结
- 高效性: IPset提供O(1)查找效率,支持大规模IP封禁
- 自动化: Fail2ban自动检测和响应,减少人工干预
- 灵活性: UFW提供简单的规则管理界面
- 可扩展: 支持多种服务和攻击模式检测
- 持久化: IPset支持超时和持久化存储
- 资源节约: 比传统方法消耗更少系统资源
这种组合为Linux系统提供了一套完整、高效的自动化安全防护解决方案。
代码:仅 fail2ban+ipset
这是我自用的装机 bash script, 之前有分享过,因为有两个 V 没过审核,留着自已来。
主要功能:
现在这个脚本 15千行:
这里包含几个 filter,测试脚本 ,还有硬写的日志文件名(这需要替换),参考时要记得修改。
# 13. Install Fail2Ban
install_fail2ban() {log_function_start "Installing Fail2Ban with IPSet integration"log_info "Installing Fail2Ban with IPSet and UFW integration for enhanced security..."# Verify nobody user exists for service isolationverify_nobody_user || log_warning "Nobody user not available for service isolation"# Safe to stop and disable Fail2Ban if already runningsafe_stop_and_disable_service "fail2ban" "Fail2Ban service"# Remove any existing Fail2Ban packageslog_info "Removing any existing Fail2Ban packages..."apt remove --purge -y fail2ban || truelog_success "Existing Fail2Ban packages removed"# Clean up orphaned dependencieslog_info "Cleaning up orphaned dependencies..."apt autoremove -y || trueapt autoclean -y || truelog_success "Orphaned dependencies cleaned up"# Remove any existing Fail2Ban configuration fileslog_info "Removing any existing Fail2Ban configuration files..."rm -rf /etc/fail2ban/* 2>/dev/null || truelog_info "Removed existing Fail2Ban configuration files"rm -rf /var/lib/fail2ban/* 2>/dev/null || truelog_info "Removed existing Fail2Ban data files"rm -rf /var/log/fail2ban.log 2>/dev/null || truelog_info "Removed existing Fail2Ban log file"log_success "Existing Fail2Ban configuration files removed"# Check system requirements for Fail2Banlog_info "Checking system requirements for Fail2Ban..."# Check log filesif [ ! -f /var/log/auth.log ]; thenlog_warning "Auth log file not found at /var/log/auth.log"touch /var/log/auth.logchmod 640 /var/log/auth.logchown root:adm /var/log/auth.logsleep 1log_info "Created missing auth log file at /var/log/auth.log with correct permissions"log_success "Auth log file created successfully"elselog_success "Auth log file found at /var/log/auth.log"fi# Check UFW statusif ! command -v ufw >/dev/null; thenlog_error "UFW is not installed. Installing it now..."if configure_ufw; thenlog_success "UFW installed and configured successfully"elselog_error "Failed to install UFW. Fail2Ban installation cannot proceed."log_function_end "Installing Fail2Ban" 1return 1fielselog_success "UFW is installed and available"fi# Install and configure IPSetlog_info "Installing IPSet package..."if apt install -y ipset; thenlog_success "IPSet installed successfully"elselog_error "Failed to install IPSet"log_function_end "Installing Fail2Ban with IPSet" 1return 1fi# Clean up any existing ipsetslog_info "Cleaning up any existing fail2ban ipsets..."ipset destroy fail2ban-banned 2>/dev/null || trueipset destroy fail2ban-recidive 2>/dev/null || truelog_info "Existing fail2ban ipsets cleaned up"# Create IPSet for fail2banlog_info "Creating IPSet for fail2ban..."if ipset create fail2ban-banned hash:ip timeout 0 2>/dev/null; thenlog_success "Created IPSet 'fail2ban-banned'"elselog_error "Failed to create IPSet 'fail2ban-banned'"log_function_end "Creating IPSet for fail2ban" 1return 1fi# Create additional IPSet for recidive (repeat offenders)log_info "Creating IPSet for repeat offenders..."if ipset create fail2ban-recidive hash:ip timeout 0 2>/dev/null; thenlog_success "Created IPSet 'fail2ban-recidive'"elselog_warning "Failed to create IPSet 'fail2ban-recidive' (may already exist)"fi# Create the initial persistent file for IPSet ruleslog_info "Creating the initial persistent file for IPSet rules..."if ipset save > /etc/ipset.rules; thenlog_success "Created the initial persistent file for IPSet rules"elselog_error "Failed to create the initial persistent file for IPSet rules"log_function_end "Creating the initial persistent file for IPSet rules" 1return 1fi# Create IPSet persistence servicelog_info "Creating IPSet persistence service..."cat > /etc/systemd/system/ipset-persistent.service << 'EOF'
[Unit]
Description=IPSet persistent rule service
Before=netfilter-persistent.service network-pre.target ufw.service fail2ban.service
ConditionFileNotEmpty=/etc/ipset.rules[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/ipset restore -exist -file /etc/ipset.rules
ExecStop=/sbin/ipset save -file /etc/ipset.rules
ExecReload=/sbin/ipset save -file /etc/ipset.rules[Install]
WantedBy=multi-user.target
EOFlog_success "Created IPSet persistence service"# Enable IPSet persistencesystemctl daemon-reloadsystemctl enable ipset-persistent.servicelog_success "Enabled IPSet persistence via systemd"# Create IPSet save script for graceful shutdownlog_info "Creating IPSet save script..."cat > /etc/systemd/system/ipset-save.service << 'EOF'
[Unit]
Description=Save IPSet rules on shutdown
DefaultDependencies=false
Before=shutdown.target reboot.target halt.target[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/true
ExecStop=/sbin/ipset save -file /etc/ipset.rules
TimeoutStopSec=30[Install]
WantedBy=multi-user.target
EOFsystemctl enable ipset-save.servicelog_success "Created IPSet save script for graceful shutdown"# Replace /etc/ufw/before.rules to integrate with IPSetlog_info "Updating /etc/ufw/before.rules to integrate with IPSet..."cp /etc/ufw/before.rules /etc/ufw/before.rules.backup.$(date +%s)log_info "Backed up existing /etc/ufw/before.rules"cat > /etc/ufw/before.rules << 'EOF'
#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
# ufw-before-input
# ufw-before-output
# ufw-before-forward
## Don't delete these required lines, otherwise there will be errors
*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-not-local - [0:0]# Fail2Ban IPSet integration
# Block all traffic from fail2ban banned IPs
-A ufw-before-input -m set --match-set fail2ban-banned src -j DROP
-A ufw-before-input -m set --match-set fail2ban-recidive src -j DROP# End required lines# allow all on loopback
-A ufw-before-input -i lo -j ACCEPT
-A ufw-before-output -o lo -j ACCEPT# quickly process packets for which we already have a connection
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT# drop INVALID packets (logs these in loglevel medium and higher)
-A ufw-before-input -m conntrack --ctstate INVALID -j ufw-logging-deny
-A ufw-before-input -m conntrack --ctstate INVALID -j DROP# ok icmp codes for INPUT
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT# ok icmp code for FORWARD
-A ufw-before-forward -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT# allow dhcp client to work
-A ufw-before-input -p udp --sport 67 --dport 68 -j ACCEPT#
# ufw-not-local
#
-A ufw-before-input -j ufw-not-local# if LOCAL, RETURN
-A ufw-not-local -m addrtype --dst-type LOCAL -j RETURN# if MULTICAST, RETURN
-A ufw-not-local -m addrtype --dst-type MULTICAST -j RETURN# if BROADCAST, RETURN
-A ufw-not-local -m addrtype --dst-type BROADCAST -j RETURN# all other non-local packets are dropped
-A ufw-not-local -m limit --limit 3/min --limit-burst 10 -j ufw-logging-deny
-A ufw-not-local -j DROP# allow MULTICAST mDNS for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 224.0.0.251 --dport 5353 -j ACCEPT# allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT
EOFlog_success "Updated /etc/ufw/before.rules with IPSet integration"# Install Fail2Banlog_info "Installing Fail2Ban package..."if apt install -y fail2ban; thenlog_success "Fail2Ban installed successfully"elselog_error "Failed to install Fail2Ban"log_function_end "Installing Fail2Ban" 1return 1fi# Create /etc/fail2ban/action.d/ipset-block.conflog_info "Creating Persistent /etc/fail2ban/action.d/ipset-block.conf"cat > /etc/fail2ban/action.d/ipset-block.conf << 'EOF'
# Fail2Ban action file for ipset
#
# This action blocks/unblocks an IP address using ipset.
#
[INCLUDES]before =[Definition]# The name of the ipset set to use.
setname = fail2ban-banned# Action startup (creates the set if it doesn't exist)
actionstart = /usr/sbin/ipset create <setname> hash:ip timeout 0 exist# Action shutdown (flushes the set when Fail2Ban stops)
#actionstop = ipset flush <setname>
actionstop = # Action to ban an IP
actionban = /usr/sbin/ipset add <setname> <ip> timeout 0 exist# Action to unban an IP
actionunban = /usr/sbin/ipset del <setname> <ip> exist[Init]
EOFlog_success "Created /etc/fail2ban/action.d/ipset-block.conf"# Create IPSet action for fail2banlog_info "Creating Fail2Ban IPSet action configuration..."mkdir -p /etc/fail2ban/action.dcat > /etc/fail2ban/action.d/ipset-ban.conf << 'EOF'
[INCLUDES]
before = iptables-common.conf[Definition]
# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
actionstart = ipset create <ipmset> hash:ip timeout <default-timeout> <family># Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
actionstop = ipset destroy <ipmset># Option: actionban
# Notes.: command executed when banning an IP.
actionban = ipset add <ipmset> <ip> timeout <timeout> -exist# Option: actionunban
# Notes.: command executed when unbanning an IP.
actionunban = ipset del <ipmset> <ip> -exist[Init]
# Default name of the ipset
ipmset = fail2ban-<name># Option: timeout
# Notes.: timeout for the ban, in seconds
timeout = 0# Option: default-timeout
# Notes.: default timeout for ipset creation
default-timeout = 0# Option: family
# Notes.: IP family, can be inet or inet6
family = inet
EOF# Create enhanced IPSet action for permanent banscat > /etc/fail2ban/action.d/ipset-persistent.conf << 'EOF'
[INCLUDES]
before = iptables-common.conf[Definition]
actionstart = ipset create <ipmset> hash:ip timeout 0 -existipset restore -file /etc/ipset.rules -existactionstop = ipset save -file /etc/ipset.rulesipset destroy <ipmset>actionban = ipset add <ipmset> <ip> timeout 0 -existipset save -file /etc/ipset.rulesactionunban = ipset del <ipmset> <ip> -existipset save -file /etc/ipset.rules[Init]
ipmset = fail2ban-banned
EOF# Create recidive action for repeat offenderscat > /etc/fail2ban/action.d/ipset-recidive.conf << 'EOF'
[INCLUDES]
before = iptables-common.conf[Definition]
actionstart = ipset create <ipmset> hash:ip timeout 0 -existactionstop = ipset save -file /etc/ipset.rulesactionban = ipset add <ipmset> <ip> timeout 0 -existipset save -file /etc/ipset.rulesactionunban = ipset del <ipmset> <ip> -existipset save -file /etc/ipset.rules[Init]
ipmset = fail2ban-recidive
EOFlog_success "Created Fail2Ban IPSet action configurations"# Create jail configurationlog_info "Configuring Fail2Ban jails with IPSet integration..."cat > /etc/fail2ban/jail.local << EOF
[DEFAULT]
# Ban settings
bantime = -1
findtime = 1800
maxretry = 3
backend = auto# IPSet integration
banaction = ipset-persistent
banaction_allports = ipset-persistent# Network settings
allowipv6 = auto
ignoreip = 127.0.0.1/8 ::1usw.daven.us jpt.daven.us bjn.daven.us bjt.daven.us usc.daven.us# Email settings (optional)
# destemail = admin@yourdomain.com
# sendername = Fail2Ban
# mta = sendmail[recidive]
enabled = true
logpath = /var/log/fail2ban.log
banaction = ipset-recidive
bantime = -1
findtime = 86400
maxretry = 3
filter = recidive[sshd]
enabled = true
port = $SSH_PORT
filter = sshd
logpath = /var/log/auth.log
backend = auto
bantime = 72000
findtime = 600
maxretry = 5[ssh-scanner]
enabled = true
port = $SSH_PORT
filter = ssh-scanner
logpath = /var/log/auth.log
maxretry = 5
bantime = 72000
findtime = 600[nginx-scan]
enabled = true
port = http,https,6033
logpath = /var/log/nginx/access.log/var/log/nginx/*.access.log
filter = nginx-scan
maxretry = 5
bantime = -1
findtime = 600[nginx-404-scan]
enabled = true
port = http,https,6033
logpath = /var/log/nginx/access.log/var/log/nginx/*.access.log
filter = nginx-404-scan
maxretry = 20
bantime = -1
findtime = 600[nginx-login-bruteforce]
enabled = true
port = http,https,6033
logpath = /var/log/nginx/access.log
filter = nginx-login-bruteforce
maxretry = 5
bantime = -1
findtime = 600[nginx-bad-useragent]
enabled = true
port = http,https,6033
logpath = /var/log/nginx/access.log
filter = nginx-bad-useragent
maxretry = 5
bantime = -1
findtime = 600[nginx-ray-abuse]
enabled = true
port = http,https,6033
logpath = /var/log/nginx/access.log
filter = nginx-ray-abuse
maxretry = 12
bantime = -1
findtime = 60[nginx-status-abuse]
enabled = true
port = http,https,6033
logpath = /var/log/nginx/access.log/var/log/nginx/*.access.log
filter = nginx-status-abuse
maxretry = 30
bantime = -1
findtime = 600[nginx-attacks]
enabled = true
port = http,https
filter = nginx-attack
logpath = /var/log/nginx/access.log
maxretry = 3
bantime = -1
findtime = 60[nginx-web-attacks]
enabled = true
port = http,https
filter = nginx-web-attacks
logpath = /var/log/nginx/*.daven.us.access.log
maxretry = 12
bantime = -1
findtime = 600
EOFlog_success "Created /etc/fail2ban/jail.local configuration file"# fail2ban for sshd and ssh-scanner local filterlog_success "Fail2Ban jails configured successfully"mkdir -p /etc/fail2ban/filter.dlog_info "Creating Fail2Ban filter for sshd and ssh-scanner..."# Create sshd local filter configurationlog_info "Creating sshd.local filter configuration..."cat > /etc/fail2ban/filter.d/sshd.local << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> .* "[^"]*" .*" "(?:Go-http-client|wget|curl|python-requests|masscan|zgrab|sqlmap|nikto|dirb|gobuster)" .*$^<HOST> .* "[^"]*" .*" ".*(?:Mozi\.m|scamanje|stresserit)" .*$^<HOST> .* "[^"]*" .*" "xfa1" .*$
ignoreregex = ^<HOST> .* "[^"]*" .*" ".*(?:Googlebot|Bingbot|YandexBot|DuckDuckBot)" .*$
EOFlog_success "sshd.local filter configuration created"# Create ssh-scanner filterlog_info "Creating Fail2Ban filter for SSH scanner..."cat > /etc/fail2ban/filter.d/ssh-scanner.conf << 'EOF'
[INCLUDES]
before = common.conf
[Definition]
_daemon = sshd
allowipv6 = auto
failregex = ^%(__prefix_line)sConnection from <HOST> port \d+ on \S+ port \d+$^%(__prefix_line)sConnection closed by <HOST> port \d+ \[preauth\]$^%(__prefix_line)sConnection closed by authenticating user .* <HOST> port \d+ \[preauth\]$^%(__prefix_line)sDisconnected from .* <HOST> port \d+ \[preauth\]$^%(__prefix_line)sReceived disconnect from <HOST> port \d+:\d+: .* \[preauth\]$^%(__prefix_line)sbanner exchange: Connection from <HOST> port \d+: invalid format$
ignoreregex =
EOFlog_success "ssh-scanner filter created"# Create Fail2Ban jail for nginx scanners# Create nginx-scan filterlog_info "Creating Fail2Ban filter for Nginx scanner..."cat > /etc/fail2ban/filter.d/nginx-scan.conf << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> .* "(GET|POST|PUT) [^"]*(?:\.env|\.git|phpinfo|eval-stdin\.php|shell\?|admin/login\.asp|boaform/admin|login\.cgi)" .*$^<HOST> .* "(GET|POST) [^"]*(?:wget\+|chmod\+|/tmp/|Mozi\.m|scamanje\.stresserit\.pro)" .*$^<HOST> .* "(GET|POST) [^"]*(?:/SDK/webLanguage|/actuator|/console|/geoserver|/solr/admin)" .*$^<HOST> .* "(GET|POST) [^"]*(?:phpunit|vendor/phpunit|_ignition)" .*$^<HOST> .* "[^"]*(?:27;wget|mstshash=)" .*$
ignoreregex = ^<HOST> .* "GET /(?:favicon\.ico|robots\.txt|sitemap\.xml) .*$
EOFlog_success "nginx-scan filter created"# Create nginx-404-scan filterlog_info "Creating Fail2Ban filter for Nginx 404 scanner..."cat > /etc/fail2ban/filter.d/nginx-404-scan.conf << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> - - \[.*\] "(?:GET|POST) /(?:admin|wp-admin|\.php|\.asp|phpinfo|\.env|config|backup|uploads|test|debug|system_info|diagnostics)" .*" 404 .*$^<HOST> - - \[.*\] "(?:GET|POST) [^"]*/(?:partymgr|jasperserver|solr|owncloud|geoserver|WebInterface)" .*" 404 .*$^<HOST> - - \[.*\] "(?:GET|POST|HEAD|OPTIONS|PUT|DELETE|CONNECT|TRACE|PATCH) /[a-zA-Z0-9]{4,8}(?:\s|/|$)" .*" 404 .*$
ignoreregex = ^<HOST> - - \[.*\] "GET /(?:favicon\.ico|robots\.txt|sitemap\.xml|apple-touch-icon|\.well-known/)" .*" 404 .*$
EOFlog_success "nginx-404-scan filter created"# Create login scanner filterlog_info "Creating Fail2Ban filter for nginx login bruteforce scanner..."cat > /etc/fail2ban/filter.d/nginx-login-bruteforce.conf << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> .* "(?:GET|POST) /login\.cgi\?username=.*&psd=" .*$^<HOST> .* "POST /boaform/admin/formLogin" .*$^<HOST> .* "GET /boaform/admin/formLogin\?username=.*&psd=" .*$^<HOST> .* "POST /admin/login\." .*$
ignoreregex =
EOFlog_success "nginx-login-bruteforce filter created"# Create nginx bad User-Agent filterlog_info "Creating Fail2Ban filter for Nginx bad User-Agent..."cat > /etc/fail2ban/filter.d/nginx-bad-useragent.conf << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> .* "[^"]*" .*" "(?:Go-http-client|wget|curl|python-requests|masscan|zgrab|sqlmap|nikto|dirb|gobuster)" .*$^<HOST> .* "[^"]*" .*" ".*(?:Mozi\.m|scamanje|stresserit)" .*$^<HOST> .* "[^"]*" .*" "xfa1" .*$
ignoreregex = ^<HOST> .* "[^"]*" .*" ".*(?:Googlebot|Bingbot|YandexBot|DuckDuckBot)" .*$
EOFlog_success "nginx-bad-useragent filter created"# Create nginx ray abuse filterlog_info "Creating Fail2Ban filter for Nginx Ray abuse..."cat > /etc/fail2ban/filter.d/nginx-ray-abuse.conf << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> .* "GET /api HTTP/1\.1" 101 .*$
ignoreregex =
EOFlog_success "nginx-ray-abuse filter created"# Create nginx status abuse filterlog_info "Creating Fail2Ban filter for Nginx status abuse..."cat > /etc/fail2ban/filter.d/nginx-status-abuse.conf << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> - - \[.*\] "(?:GET|POST|HEAD) .* HTTP/1\.[01]" 40[0-3] \d+ ".*" ".*"$
ignoreregex = ^<HOST> .* "GET /(?:favicon\.ico|robots\.txt)" 40[0-3] .*$
EOFlog_success "nginx-status-abuse filter created"# Create nginx status abuse filterlog_info "Creating Fail2Ban filter for Nginx attack abuse..."cat > /etc/fail2ban/filter.d/nginx-attack.conf << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> -.*"GET .*(.env|.git|.well-known/security\.txt|\/phpmyadmin|\/wp-login\.php|\/wp-admin|\/cgi-bin|\/phpinfo|\.sql|\.log|\.ini|\.conf|\.bak).* HTTP.*" (40[0-4]|444|499|500)^<HOST> -.*"GET .*(?:\x09hink|\x09pp).*shell_exec.*wget.*" 400.*
ignoreregex =
EOFlog_success "nginx-attack filter created"# Create nginx status abuse filterlog_info "Creating Fail2Ban filter for Nginx web attacks abuse..."cat > /etc/fail2ban/filter.d/nginx-web-attacks.conf << 'EOF'
[Definition]
allowipv6 = auto
failregex = ^<HOST> -.*"GET .*(?:\.env|\.git|\.well-known/security\.txt|\/phpmyadmin|\/wp-admin|\/cgi-bin|\/webui|\/geoserver|\/remote/login|onvif|PSIA|boaform|owa/auth/logon\.aspx|PROPFIND|\+CSCOE\+).*HTTP.*" (40[0-4]|444|499|500)^<HOST> -.*"GET .*(?:\x09hink|\x07pp).*shell_exec.*wget.*" 400.*^<HOST> -.*"POST .*cgi-bin/(\.\./).*bin/sh HTTP.*" (400|404|301|302)^<HOST> -.*"GET .*HTTP.*" (40[0-4]|444|499).*"(?:zgrab|CMS-Checker|libredtail-http|WanScannerBot|Odin|GenomeCrawlerd)"
ignoreregex =
EOFlog_success "nginx-web-attacks filter created"# Disable conflicting default configuration fileslog_info "Disabling conflicting default configuration files..."if [ -f /etc/fail2ban/jail.d/defaults-debian.conf ]; thenif mv /etc/fail2ban/jail.d/defaults-debian.conf /etc/fail2ban/jail.d/defaults-debian.disabled; thenlog_success "Conflicting default configuration files disabled"elselog_warning "Failed to disable conflicting default configuration files"fifi# Restart services in proper orderlog_info "Starting services in proper order..."# Start IPSet persistence firstsystemctl start ipset-persistent.service# Reload UFW to pick up the new before.ruleslog_info "Reloading UFW to apply IPSet integration..."ufw --force reloadlog_success "UFW reloaded with IPSet integration"# Enable and start Fail2Bansystemctl enable fail2bansystemctl start fail2banlog_info "Fail2Ban service started"# Wait for fail2ban to initializesleep 3# Verify IPSet integrationlog_info "Verifying IPSet integration..."if ipset list fail2ban-banned >/dev/null 2>&1; thenlog_success "IPSet 'fail2ban-banned' is active"elselog_error "IPSet 'fail2ban-banned' is not active"fi# Verify UFW integrationif iptables -L ufw-before-input -n | grep -q fail2ban-banned; thenlog_success "UFW is integrated with IPSet"elselog_warning "UFW integration with IPSet may not be working"fi# Create management toolscreate_ipset_management_tools# Create fail2ban-tester.shlog_info "Creating /usr/bin/fail2ban-tester.sh..."cat > /usr/bin/fail2ban-tester.sh << 'EOF'
#!/bin/bash
# A script to automatically test all enabled Fail2ban jails using fail2ban-regex.
# It fetches the filter and logpath for each active jail and runs the test.
# By Dave Nian on 25Aug25# --- Configuration ---
# Colors for better readability
C_RESET='\033[0m'
C_RED='\033[0;31m'
C_GREEN='\033[0;32m'
C_YELLOW='\033[0;33m'
C_BLUE='\033[0;34m'
C_CYAN='\033[0;36m'
C_BOLD='\033[1m'# --- Pre-flight Checks ---
# Check if running as root
if [[ $EUID -ne 0 ]]; thenecho -e "${C_RED}This script must be run as root.${C_RESET}"exit 1
fi# Check if fail2ban-client is available
if ! command -v fail2ban-client &> /dev/null; thenecho -e "${C_RED}Error: 'fail2ban-client' command not found.${C_RESET}"echo "Please ensure Fail2ban is installed and you are running this on the correct server."exit 1
fi# --- Main Logic ---
echo -e "${C_BOLD}${C_BLUE}--- Starting Fail2ban Filter Test ---${C_RESET}"# Get the list of enabled jails from fail2ban-client
# The sed command cleans up the output to get just the jail names
ENABLED_JAILS=$(fail2ban-client status | grep "Jail list:" | sed -e 's/.*Jail list:\s*//' -e 's/,//g')if [ -z "$ENABLED_JAILS" ]; thenecho -e "${C_RED}Could not find any enabled jails. Exiting.${C_RESET}"exit 1
fiecho -e "Found enabled jails: ${C_YELLOW}${ENABLED_JAILS}${C_RESET}\n"# Loop through each enabled jail
for jail in $ENABLED_JAILS; doecho -e "${C_BOLD}${C_CYAN}========================================${C_RESET}"echo -e "${C_BOLD}${C_CYAN} Testing Jail: [${jail}] ${C_RESET}"echo -e "${C_BOLD}${C_CYAN}========================================${C_RESET}"# Get the filter name for the current jail.# Redirect stderr to /dev/null to keep the output clean.filter_name=$(fail2ban-client get "$jail" filter 2>/dev/null)# ** FIX **: Check if the command failed or returned a known error message.# Some versions of fail2ban-client output this error to stdout.# If it fails, we assume the filter name is the same as the jail name.if [ $? -ne 0 ] || [[ "$filter_name" == *"Invalid command"* ]] || [ -z "$filter_name" ]; thenecho -e " ${C_YELLOW}Warning: Could not dynamically get filter for [${jail}].${C_RESET}"echo -e " ${C_YELLOW}Assuming filter name matches jail name.${C_RESET}"filter_name=$jailfifilter_file="/etc/fail2ban/filter.d/${filter_name}.conf"# ** FIX **: Get the log path(s) and parse the multi-line output correctly.# This command now specifically extracts lines that are file paths.log_paths_output=$(fail2ban-client get "$jail" logpath)log_paths=$(echo "$log_paths_output" | grep -E '^[|`]-' | sed -E 's/^[|`]- //')# If the above fails (for single-line outputs), use the raw output but clean it.if [ -z "$log_paths" ]; thenlog_paths=$(echo "$log_paths_output" | sed 's/Current monitored log file(s)://g' | tr -d '`-' | tr -d '|')fi# Check if the filter file actually existsif [ ! -f "$filter_file" ]; thenecho -e " ${C_RED}Filter file not found:${C_RESET} ${filter_file}"# Try to find it with a .local extension as a fallbackfilter_file_local="/etc/fail2ban/filter.d/${filter_name}.local"if [ -f "$filter_file_local" ]; thenecho -e " ${C_YELLOW}Found fallback filter:${C_RESET} ${filter_file_local}"filter_file=$filter_file_localelseecho -e " ${C_RED}Skipping jail [${jail}] as no valid filter was found.${C_RESET}\n"continuefifiecho -e " ${C_BOLD}Filter:${C_RESET} ${filter_file}"echo -e " ${C_BOLD}Log(s):${C_RESET}\n${log_paths}" # Print the cleaned listecho ""# Loop through each log path (a jail can have multiple)for log_path in $log_paths; do# Expand wildcards in log pathsexpanded_logs=$(eval echo $log_path)for expanded_log_path in $expanded_logs; do# Check if the log file exists and is readableif [ ! -r "$expanded_log_path" ]; thenecho -e " -> Testing log: ${C_YELLOW}${expanded_log_path}${C_RESET}"echo -e " ${C_RED}Result: Log file not found or not readable. Skipping.${C_RESET}\n"continuefiecho -e " -> Testing log: ${C_GREEN}${expanded_log_path}${C_RESET}"# Run the fail2ban-regex command and capture the outputtest_output=$(fail2ban-regex "$expanded_log_path" "$filter_file")# ** FIX **: More robustly extract the number of matched lines.# This specifically finds the number preceding "matched".matches=0 # Default to 0matches_line=$(echo "$test_output" | grep "Lines:")if [[ -n "$matches_line" ]]; then# Use grep with Perl-compatible regex to extract the numbermatches=$(echo "$matches_line" | grep -oP '\d+(?=\s+matched)')fi# Print the summaryif [ "$matches" -gt 0 ]; thenecho -e " ${C_GREEN}${C_BOLD}Result: Found ${matches} matching lines!${C_RESET}"elseecho -e " ${C_YELLOW}Result: Found 0 matching lines.${C_RESET}"fiecho ""donedone
doneecho -e "${C_BOLD}${C_BLUE}--- Test Complete ---${C_RESET}"EOFif [ -f "/usr/bin/fail2ban-tester.sh" ];then log_info "fail2ban-tester.sh created in /usr/bin/"elselog_warning "fail2ban-tester.sh didn't exist in /usr/bin/"return 1fiif chmod 777 /usr/bin/fail2ban-tester.sh;thenlog_info "/usr/bin/fail2ban-tester.sh has been given execute permission"elselog_warning "Ran chmode on /usr/bin/fail2ban-tester.sh failed"return 1fiif ln -s /usr/bin/fail2ban-tester.sh /usr/bin/fail2ban-tester;thenlog_info "Linked /usr/bin/fail2ban-tester.sh to /usr/bin/fail2ban-tester"elselog_warning "Failed to link /usr/bin/fail2ban-tester"return 1fi# Fixing the WARNING 'allowipv6' not defined in 'Definition'if fail2ban_sshd_local_allowipv6; thenlog_success "'allowipv6' configuration fixed in sshd.local"elselog_warning "'allowipv6' configuration not fixed in sshd.local"fi# Final restart to apply all changeslog_info "Performing final restart of services..."systemctl daemon-reloadsystemctl restart fail2banif systemctl is-active --quiet fail2ban; thenlog_success "Fail2Ban restarted successfully with IPSet integration"elselog_error "Failed to restart Fail2Ban"systemctl status fail2ban --no-pager -l || truelog_function_end "Installing Fail2Ban with IPSet" 1return 1fi# Create Fail2Ban management toolslog_info "Creating Fail2Ban management tools..." create_fail2ban_tools# Verify installationif verify_fail2ban_installation; thenlog_success "🎉 Fail2Ban with IPSet integration completed successfully!"show_fail2ban_summarylog_function_end "Installing Fail2Ban with IPSet" 0return 0elselog_warning "⚠️ Fail2Ban with IPSet installed but some issues detected"show_fail2ban_troubleshootinglog_function_end "Installing Fail2Ban with IPSet" 1return 1fi
}# Function to create IPSet management tools
create_ipset_management_tools() {log_info "Creating IPSet management tools..."# Create IPSet status scriptcat > /usr/local/bin/ipset-status << 'EOF'
#!/bin/bash
# IPSet Status Display Scriptecho "=== IPSet Status ==="
echo "Active IPSets:"
ipset list -nameecho ""
echo "=== Fail2Ban IPSets ==="
for set in $(ipset list -name | grep fail2ban); doecho "--- $set ---"echo "Members: $(ipset list $set | grep -c '^[0-9]')"ipset list $set | head -20echo ""
doneecho "=== IPSet Rules in iptables ==="
iptables -L ufw-before-input -n | grep set
EOFchmod +x /usr/local/bin/ipset-statuslog_success "Created IPSet status script"# Create IPSet backup scriptcat > /usr/local/bin/ipset-backup << 'EOF'
#!/bin/bash
# IPSet Backup ScriptBACKUP_DIR="/etc/ipset-backups"
mkdir -p "$BACKUP_DIR"BACKUP_FILE="$BACKUP_DIR/ipset-backup-$(date +%Y%m%d-%H%M%S).rules"
ipset save > "$BACKUP_FILE"echo "IPSet rules backed up to: $BACKUP_FILE"# Keep only last 10 backups
ls -t "$BACKUP_DIR"/ipset-backup-*.rules | tail -n +11 | xargs rm -f 2>/dev/null
EOFchmod +x /usr/local/bin/ipset-backuplog_success "Created IPSet backup script"
}fail2ban_sshd_local_allowipv6() {local source_file="/etc/fail2ban/filter.d/sshd.conf"local local_file="/etc/fail2ban/filter.d/sshd.local"if [ -f "$source_file" ]; thenlog_info "Setting up sshd.local filter configuration..."# Backup existing sshd.local if it existsif [ -f "$local_file" ]; thenlog_info "Backing up existing sshd.local..."cp "$local_file" "${local_file}.backup.$(date +%s)"fi# Copy and set permissionsif cp -f "$source_file" "$local_file"; thenchmod 644 "$local_file"# Verify copy resultif [ -s "$local_file" ]; thenlog_success "sshd.conf copied to sshd.local successfully"elselog_error "Copied file is empty"return 1fielselog_error "Failed to copy sshd.conf to sshd.local"return 1fi# Fix 'allowipv6' configurationlog_info "Fixing 'allowipv6' warning in sshd.local..."if sed -n '/^\[Definition\]/,/^\[/p' "$local_file" | grep -q "^[[:space:]]*allowipv6[[:space:]]*=[[:space:]]*\(auto\|yes\)"; thenlog_success "'allowipv6' already correctly configured in sshd.local"elseif sed -n '/^\[Definition\]/,/^\[/p' "$local_file" | grep -q "^[[:space:]]*allowipv6[[:space:]]*="; thenlog_info "Updating existing 'allowipv6' configuration to 'auto'"sed -i '/^\[Definition\]/,/^\[/{ s/^[[:space:]]*allowipv6[[:space:]]*=.*/allowipv6 = auto/; }' "$local_file"elselog_info "Adding 'allowipv6 = auto' to sshd.local"sed -i '/^\[Definition\]/a allowipv6 = auto' "$local_file"fi# Verify modification resultif grep -A 5 "^\[Definition\]" "$local_file" | grep -q "^allowipv6[[:space:]]*=[[:space:]]*auto"; thenlog_success "✅ allowipv6 successfully configured in sshd.local"elselog_warning "⚠️ allowipv6 configuration may not be correct"fifielselog_error "Source file does not exist: $source_file"return 1fi
}# Unified verification function that handles both standard and IPSet installations
verify_fail2ban_installation() {log_info "Verifying Fail2Ban installation and configuration..."local verification_passed=truelocal tools_dir="/usr/local/bin/fail2ban-tools"local has_ipset=false# Detect if IPSet integration is being usedif command -v ipset >/dev/null 2>&1 && ipset list fail2ban-banned >/dev/null 2>&1; thenhas_ipset=truelog_info "IPSet integration detected, performing enhanced verification..."elselog_info "Standard Fail2Ban installation detected..."fi# 1. Check Fail2Ban service statusif systemctl is-active --quiet fail2ban; thenlog_success "✅ Fail2Ban service is running"elselog_error "❌ Fail2Ban service is not running"verification_passed=falsefi# 2. IPSet-specific checks (only if IPSet integration detected)if [ "$has_ipset" = true ]; then# Check IPSet persistence serviceif systemctl is-active --quiet ipset-persistent; thenlog_success "✅ IPSet persistence service is active"elselog_warning "⚠️ IPSet persistence service may not be active"fi# Check IPSets existif ipset list fail2ban-banned >/dev/null 2>&1; thenlog_success "✅ IPSet 'fail2ban-banned' exists"elselog_error "❌ IPSet 'fail2ban-banned' does not exist"verification_passed=falsefi# Check for recidive IPSet (optional)if ipset list fail2ban-recidive >/dev/null 2>&1; thenlog_success "✅ IPSet 'fail2ban-recidive' exists"elselog_info "ℹ️ IPSet 'fail2ban-recidive' not found (optional)"fi# Check UFW integration with IPSetif iptables -L ufw-before-input -n | grep -q fail2ban-banned; thenlog_success "✅ UFW is integrated with IPSet"elselog_error "❌ UFW is not integrated with IPSet"verification_passed=falsefi# Check IPSet action fileslocal ipset_actions=("/etc/fail2ban/action.d/ipset-ban.conf""/etc/fail2ban/action.d/ipset-persistent.conf""/etc/fail2ban/action.d/ipset-recidive.conf")for action_file in "${ipset_actions[@]}"; doif [ -f "$action_file" ]; thenlog_success "✅ IPSet action file exists: $(basename "$action_file")"elselog_warning "⚠️ IPSet action file missing: $action_file"fidone# Check IPSet rules fileif [ -f "/etc/ipset.rules" ]; thenlog_success "✅ IPSet rules file exists: /etc/ipset.rules"elselog_warning "⚠️ IPSet rules file missing: /etc/ipset.rules"fifi# 3. Check jail statuslocal active_jails=$(fail2ban-client status 2>/dev/null | grep "Jail list:" | cut -d: -f2 | tr ',' '\n' | wc -w)if [ "$active_jails" -gt 0 ]; thenlog_success "✅ $active_jails jail(s) active"# Check specific jailsfor jail in sshd ssh-scanner; doif fail2ban-client status "$jail" >/dev/null 2>&1; thenlog_success "✅ $jail jail is active"elselog_warning "⚠️ $jail jail is not active"fidoneelselog_error "❌ No active jails found"verification_passed=falsefi# 4. Check configuration fileslocal config_files=("/etc/fail2ban/jail.local""/etc/fail2ban/filter.d/sshd.local""/etc/fail2ban/filter.d/ssh-scanner.conf""/etc/fail2ban/filter.d/nginx-scan.conf""/etc/fail2ban/filter.d/nginx-404-scan.conf""/etc/fail2ban/filter.d/nginx-login-bruteforce.conf""/etc/fail2ban/filter.d/nginx-bad-useragent.conf""/etc/fail2ban/filter.d/nginx-ray-abuse.conf""/etc/fail2ban/filter.d/nginx-status-abuse.conf""/etc/fail2ban/filter.d/nginx-attack.conf""/etc/fail2ban/filter.d/nginx-web-attacks.conf")local missing_configs=0for config_file in "${config_files[@]}"; doif [ -f "$config_file" ]; thenlog_success "✅ Configuration file exists: $(basename "$config_file")"elselog_error "❌ Configuration file missing: $config_file"verification_passed=false((missing_configs++))fidoneif [ "$missing_configs" -eq 0 ]; thenlog_success "✅ All configuration files are present"elselog_error "❌ $missing_configs configuration file(s) missing"fi# 5. Check log fileif [ -f "/var/log/fail2ban.log" ]; thenlocal recent_errors=$(tail -50 /var/log/fail2ban.log 2>/dev/null | grep -c "ERROR" || echo "0")if [ "$recent_errors" -eq 0 ]; thenlog_success "✅ No recent errors in Fail2Ban log"elselog_warning "⚠️ Found $recent_errors recent errors in Fail2Ban log"fielselog_warning "⚠️ Fail2Ban log file not found at /var/log/fail2ban.log"fi# 6. Check tool filesif [ -d "$tools_dir" ]; thenlog_success "✅ Fail2Ban tools directory exists ($tools_dir)"if [ -f "$tools_dir/fail2ban-checking.sh" ]; thenlog_success "✅ Fail2Ban check script exists"if [ -f "$tools_dir/fail2ban-status.sh" ]; thenlog_success "✅ Fail2Ban status script exists"elselog_error "❌ Fail2Ban status script missing"verification_passed=falsefielselog_error "❌ Fail2Ban check script missing"verification_passed=falsefielselog_error "❌ Fail2Ban tools directory missing"verification_passed=falsefi# 7. Check tool symlinksif [ -f "/usr/local/bin/fail2ban-checking" ]; thenlog_success "✅ Fail2Ban checking symlink exists"if [ -f "/usr/local/bin/fail2ban-status" ]; thenlog_success "✅ Fail2Ban status symlink exists"elselog_error "❌ Fail2Ban status symlink missing"verification_passed=falsefielselog_error "❌ Fail2Ban checking symlink missing"verification_passed=falsefi# 8. Check fail2ban-tester scriptif [ -f "/usr/bin/fail2ban-tester.sh" ] && [ -f "/usr/bin/fail2ban-tester" ]; thenlog_success "✅ Fail2Ban tester script exists"elselog_warning "⚠️ Fail2Ban tester script missing"fi# 9. Additional IPSet management tools check (if IPSet detected)if [ "$has_ipset" = true ]; thenif [ -f "/usr/local/bin/ipset-status" ]; thenlog_success "✅ IPSet status script exists"elselog_warning "⚠️ IPSet status script missing"fiif [ -f "/usr/local/bin/ipset-backup" ]; thenlog_success "✅ IPSet backup script exists"elselog_warning "⚠️ IPSet backup script missing"fifi# Summaryif [ "$has_ipset" = true ]; thenlog_info "Verification completed for Fail2Ban with IPSet integration"elselog_info "Verification completed for standard Fail2Ban installation"fireturn $([ "$verification_passed" = true ] && echo 0 || echo 1)
}show_fail2ban_summary() {local has_ipset=false# Detect if IPSet integration is being usedif command -v ipset >/dev/null 2>&1 && ipset list fail2ban-banned >/dev/null 2>&1; thenhas_ipset=truefiechoif [ "$has_ipset" = true ]; thenlog_info "📋 Fail2Ban with IPSet Integration Summary"echo "============================================="echo "🔐 SSH Port: $SSH_PORT"echo "🚫 Ban Policy: Permanent bans via IPSet"echo "🕒 SSH Attempts: 3 failures in 30 minutes"echo "🕒 Scanner Detection: 2 connections in 5 minutes"echo "🔧 Firewall Integration: UFW + IPSet"echo "💾 IPSet Persistence: Enabled"echoecho "📁 Key Configuration Files:"echo " • Main config: /etc/fail2ban/jail.local"echo " • IPSet actions: /etc/fail2ban/action.d/ipset-*.conf"echo " • IPSet rules: /etc/ipset.rules"echo " • UFW before rules: /etc/ufw/before.rules"echo " • SSH filter: /etc/fail2ban/filter.d/sshd.local"echo " • Scanner filter: /etc/fail2ban/filter.d/ssh-scanner.conf"echo " • Nginx filters: /etc/fail2ban/filter.d/nginx-*.conf"echoecho "🔧 Management Commands:"echo " • Fail2Ban status: fail2ban-client status"echo " • IPSet status: ipset-status"echo " • IPSet backup: ipset-backup"echo " • View banned IPs: ipset list fail2ban-banned"echo " • Unban IP: fail2ban-client set <jail> unbanip <IP>"echo " • Manual IPSet ban: ipset add fail2ban-banned <IP>"echo " • Manual IPSet unban: ipset del fail2ban-banned <IP>"echoecho "🔧 Service Management:"echo " • Restart Fail2Ban: systemctl restart fail2ban"echo " • Reload UFW: ufw reload"echo " • Save IPSet: ipset save > /etc/ipset.rules"echo "============================================="elselog_info "📋 Fail2Ban Configuration Summary"echo "=================================="echo "🔐 SSH Port: $SSH_PORT"echo "🚫 Ban Policy: Permanent bans"echo "🕒 SSH Attempts: 3 failures in 30 minutes"echo "🕒 Scanner Detection: 2 connections in 5 minutes" echo "🔧 Firewall Integration: UFW"echoecho "📁 Configuration Files:"echo " • Main config: /etc/fail2ban/jail.local"echo " • SSH filter: /etc/fail2ban/filter.d/sshd.local"echo " • Scanner filter: /etc/fail2ban/filter.d/ssh-scanner.conf"echo " • Nginx filters: /etc/fail2ban/filter.d/nginx-*.conf"echoecho "🔧 Management Commands:"echo " • Check status: fail2ban-client status"echo " • Check SSH jail: fail2ban-client status sshd"echo " • View logs: tail -f /var/log/fail2ban.log"echo " • Unban IP: fail2ban-client set sshd unbanip <IP>"echo " • Restart service: systemctl restart fail2ban"echo "=================================="fiechoecho "🔧 Additional Tools:"echo " • Security check: fail2ban-checking"echo " • Status display: fail2ban-status" echo " • Filter tester: fail2ban-tester"echoecho "🔧 Troubleshooting:"echo " • Check service status: systemctl status fail2ban"echo " • View logs: journalctl -u fail2ban -f"echo " • Test configuration: fail2ban-client -d"echo " • Check log files: tail -f /var/log/fail2ban.log"echo " • Manual restart: systemctl restart fail2ban"echo " • Check UFW integration: ufw status"if [ "$has_ipset" = true ]; thenecho " • Check IPSet rules: ipset list"echo " • Check firewall rules: iptables -L ufw-before-input -n"elseecho " • Check firewall rules: iptables -L -n"fiecho "=================================="
}
总结:
因为我使用的 -1 永久封,造成内存中有几百条 iptables 记录,严重影响性能。 现在使用 IPset 做为一个池来集中管理。 也在修改之前的策略改为封20小时,这个脚本中可改。
这三个工具构成了一套层次化的网络安全防护体系,它们的协同工作过程如下:
角色分工
UFW (Uncomplicated Firewall):作为 iptables 的简化前端,负责基础防火墙规则管理和流量过滤的最终执行。
IPset:高效的 IP 地址集合管理工具,使用哈希表存储大量 IP 地址,提供 O(1) 时间复杂度的查找效率。
Fail2ban:智能入侵检测系统,监控日志文件、分析攻击模式、触发自动封禁动作。
核心工作流程
检测阶段:
- 攻击者尝试连接服务(如SSH)
- 服务记录认证失败日志到
/var/log/auth.log
- Fail2ban 实时监控日志文件,使用正则表达式匹配攻击模式
- 计数器跟踪每个IP的失败次数
封禁阶段:
- 当失败次数达到阈值(如 maxretry=3),Fail2ban 触发封禁动作
- 执行 IPset 命令:
ipset add fail2ban-ssh 攻击者IP
- IP被添加到预先创建的IPset集合中
- UFW/iptables 规则立即生效:
iptables -I INPUT -m set --match-set fail2ban-ssh src -j DROP
防护阶段:
- 后续来自该IP的所有请求在内核层面被直接丢弃
- 不再消耗应用服务资源
- 根据配置的 bantime 自动解封或永久封禁
关键性能优势
传统方式的问题是每个被封IP需要单独的 iptables 规则,当封禁IP数量达到数千个时,规则匹配效率急剧下降。
IPset 优化方式只需要一条 iptables 规则引用整个IP集合,无论集合中有多少IP,查找效率都保持恒定。这种方式可以高效处理数万个封禁IP而不影响网络性能。
实际数据流
正常流量:外部请求 → UFW检查 → IPset查询(未命中) → 通过基础规则 → 到达服务
恶意流量:攻击请求 → UFW检查 → IPset查询(命中) → 直接丢弃数据包
这种架构的核心优势是将被动防护(UFW/iptables)与主动检测(Fail2ban)相结合,同时通过IPset解决了大规模IP封禁的性能瓶颈问题。整个系统实现了从攻击检测到自动响应的完全自动化,大大降低了管理员的工作负担。
收到 CSDN 消息: