公网服务器只要开放 SSH,认证日志里通常很快就会出现针对 root、admin、test 等用户名的自动化尝试。单次失败不一定危险,真正需要处理的是同一来源在短时间内持续试探密码、用户名或 Web 登录接口。Fail2ban 的作用,就是从可信日志中识别这类重复失败,并调用本机防火墙动作临时封禁来源地址。
它适合做主机侧的第二道防线,但不能替代 SSH 密钥认证、MFA、来源网络限制、漏洞修复和集中式边界防护。攻击者可以轮换 IP,日志也可能缺失或被代理层改写;如果登录接口本身存在绕过或凭证已经泄露,单靠封禁失败次数并不能解决问题。
本文以常见的 Fail2ban 0.11/1.x 包为参考。不同 Linux 发行版、Fail2ban 打包方式和防火墙后端可能提供不同 action 文件,实际部署前必须以本机 fail2ban-client -V、配置测试结果和 /etc/fail2ban/action.d/ 内容为准。
文中占位符说明:
<管理网段>
:允许运维登录的可信 CIDR,例如企业 VPN 出口网段,不能照抄示例地址。<测试IP>
:专门用于封禁验证、且不会中断当前管理会话的地址。
:sshd 实际监听端口,例如 22;修改端口不能替代认证加固。
:系统中的 systemd 单元,常见为 ssh.service 或 sshd.service。<日志文件>
:目标服务真实写入的日志路径。<备份目录>
:受权限保护、空间充足的配置备份目录。
:启用的 jail 名称,例如 sshd。Fail2ban 的处理链路可以拆成五个部分:
failregex 从失败日志中提取来源地址,使用 作为 IP/主机匹配标记。最常见的三个时间参数是:
findtime
:统计失败次数的时间窗口。maxretry
:窗口内达到多少次匹配后触发封禁。bantime
:封禁持续时间。
例如 findtime = 10m、maxretry = 5、bantime = 1h,表示同一来源在 10 分钟统计窗口内达到 5 次匹配后,封禁 1 小时。这里的“失败次数”不是 Fail2ban 主动尝试登录得出的,而是 filter 对日志行的匹配结果。如果日志格式变了、日志没写来源 IP,或者正则过宽,封禁判断就会失效或误伤。
因此 Fail2ban 部署的核心并不是“把服务启动起来”,而是证明四件事:日志确实存在、filter 只匹配真实失败、action 与本机防火墙兼容、运维入口不会被误封。
检查 sshd 实际监听地址和端口:
bash
sudo ss -lntp | grep -E 'sshd|:22([[]]|$)' sudo sshd -T | grep -E '^(port|listenaddress|passwordauthentication|permitrootlogin|pubkeyauthentication) '
sshd -T 输出的是展开后的有效配置,通常比只搜索 /etc/ssh/sshd_config 更可靠,因为后者可能包含 Include 和 Match 段。若命令提示权限或主机密钥问题,应在 root 权限和正确环境中执行,不要据此猜测生效配置。
示例输出:
text
port 22 passwordauthentication no permitrootlogin prohibit-password pubkeyauthentication yes 判断时重点关注:
即使 PasswordAuthentication no,日志里仍可能出现用户名枚举、密钥尝试或握手滥用。Fail2ban 可以减少重复来源,但更优先的措施仍是限制来源网段和关闭不需要的认证方式。
Debian/Ubuntu 常把 SSH 认证写入 /var/log/auth.log,RHEL 系常见 /var/log/secure;启用 journald 后也可以直接读取服务单元。不能只凭发行版名称假定路径,应现场确认:
bash
sudo journalctl -u --since '30 minutes ago' --no-pager sudo test -r /var/log/auth.log && sudo tail -n 50 /var/log/auth.log sudo test -r /var/log/secure && sudo tail -n 50 /var/log/secure 命令输出可能含用户名、来源 IP 和主机信息,提交工单或共享截图前应脱敏。需要区分真正的认证失败与正常噪声,例如连接在认证前断开、健康检查探测、管理员输错一次密码。没有日志证据时,不应先写自定义正则。
Fail2ban 的 action 必须与主机实际防火墙一致:
bash
sudo nft list ruleset sudo iptables-save sudo firewall-cmd --state 某些命令可能因组件未安装而失败,这本身就是判断依据。不要同时启用多个相互不清楚的防火墙管理层。比如系统由 firewalld 管理 nftables 时,直接手写 nft 规则和再让 Fail2ban 管理链,可能导致重载后规则丢失或顺序变化。
将当前规则保存为只读基线:
bash
#!/usr/bin/env bash set -euo pipefail BACKUP_DIR="<备份目录>" STAMP="$(date +%Y%m%d-%H%M%S)" sudo install -d -m 0700 "${BACKUP_DIR}" if command -v nft >/dev/null 2>&1; then sudo nft list ruleset | sudo tee "${BACKUP_DIR}/nft-rules-${STAMP}.txt" >/dev/null fi if command -v iptables-save >/dev/null 2>&1; then sudo iptables-save | sudo tee "${BACKUP_DIR}/iptables-rules-${STAMP}.txt" >/dev/null fi 这些文件用于差异核对,不应不加分析地直接回灌整个生产防火墙。全量恢复规则可能覆盖同时发生的合法变更;真正恢复前必须由防火墙责任人审核。
bash
sudo apt-get update sudo apt-get install fail2ban Fail2ban 常来自 EPEL 等额外仓库。先按照企业镜像源规范启用适配当前系统版本的仓库,再安装:
bash
sudo dnf install fail2ban 不要为了安装单个包临时接入未经审核的公网软件源。安装后先确认版本和文件,不急着启用服务:
bash
fail2ban-client -V rpm -ql fail2ban 2>/dev/null | head -n 30 || true dpkg -L fail2ban 2>/dev/null | head -n 30 || true 再检查系统是否已经存在旧配置:
bash
sudo find /etc/fail2ban -maxdepth 2 -type f -print | sort sudo systemctl status fail2ban --no-pager 如果服务此前已运行,当前任务就不是“首次安装”,而是变更已有安全策略。此时必须先导出有效配置和现有 ban 状态,不能覆盖原文件。
jail.conf 和 filter.d/*.conf 可能在升级软件包时变化。生产配置应写入 jail.local、jail.d/*.local 或对应的本地 filter 文件,并通过版本管理保留变更记录。
先备份现有配置:
bash
#!/usr/bin/env bash set -euo pipefail BACKUP_DIR="<备份目录>" STAMP="$(date +%Y%m%d-%H%M%S)" sudo install -d -m 0700 "${BACKUP_DIR}" sudo tar -C /etc -czf "${BACKUP_DIR}/fail2ban-${STAMP}.tar.gz" fail2ban sudo sha256sum "${BACKUP_DIR}/fail2ban-${STAMP}.tar.gz" | sudo tee "${BACKUP_DIR}/fail2ban-${STAMP}.tar.gz.sha256" >/dev/null 备份需要确认能读取、校验和匹配,并按企业要求复制到独立位置。把备份留在同一台可能被入侵的主机上,不构成完整灾备。
systemd 环境建议优先使用 journal 后端,避免日志路径在不同发行版间不一致。创建 /etc/fail2ban/jail.d/sshd.local:
ini
[sshd] enabled = true backend = systemd port = filter = sshd findtime = 10m maxretry = 5 bantime = 1h ignoreip = 127.0.0.1/8 ::1 <管理网段> 关键点如下:
backend = systemd 时通常不再填写 logpath,日志匹配由 filter 的 journal 条件和系统日志决定。port
必须与 sshd 实际监听端口一致。也可以使用服务名 ssh,但自定义端口应直接写数字。ignoreip
只加入经过确认的固定运维出口、堡垒机和本机地址。不要把整个办公网、云 VPC 或所有私网段无条件放行。maxretry
过小会误伤人工操作,过大则削弱效果。需要根据 MFA、密码策略、用户规模和历史日志基线调整。bantime
不是越长越好。动态公网 IP 可能很快重新分配给正常用户,永久封禁会积累误伤。如果系统使用文本认证日志,可改为:
ini
[sshd] enabled = true backend = auto port = filter = sshd logpath = <日志文件> findtime = 10m maxretry = 5 bantime = 1h ignoreip = 127.0.0.1/8 ::1 <管理网段>
<日志文件> 应替换为已经确认存在且由 root 控制的真实路径,例如某些系统上的 /var/log/auth.log。不要让非特权业务用户能够写入 Fail2ban 信任的日志,否则攻击者可能伪造日志触发对任意 IP 的封禁。
bash
sudo fail2ban-client -t sudo fail2ban-client -d > /tmp/fail2ban-effective-config.txt
-t 用于测试配置;-d 会输出解析后的配置,便于确认最终采用的 backend、port、logpath 和 action。输出可能很长,应重点搜索目标 jail:
bash
grep -nE "['(add|set)', ''" /tmp/fail2ban-effective-config.txt
如果版本不支持某个选项,必须根据本机 fail2ban-client --help 和包内示例调整,不要带着语法错误强行重启服务。
文本日志可直接测试:
bash
sudo fail2ban-regex <日志文件> /etc/fail2ban/filter.d/sshd.conf --print-all-matched systemd journal 可以先导出一个受控时间窗口,再通过标准输入测试:
bash
sudo journalctl -u --since '24 hours ago' --no-pager | sudo fail2ban-regex - /etc/fail2ban/filter.d/sshd.conf 示例输出:
text
Failregex: 87 total Ignoreregex: 0 total Lines: 1250 lines, 0 ignored, 87 matched, 1163 missed 这只是示例输出,实际数量必须来自目标主机。判断不能只看 matched 大于 0,还要抽查匹配行:
提取到的是实际来源地址。
不要通过真实爆破生产 SSH 来“测试效果”。用历史日志和 fail2ban-regex 足以验证 filter;action 另用隔离测试 IP 验证。
启动 Fail2ban 会创建或修改防火墙规则,可能影响远程连接。执行前必须满足:
ignoreip。fail2ban-client -t
通过。先启动但不要断开当前 SSH 会话:
bash
sudo systemctl enable --now fail2ban sudo systemctl status fail2ban --no-pager sudo journalctl -u fail2ban --since '10 minutes ago' --no-pager 从第二个已验证的管理终端重新登录,确认认证正常后,再查看 jail:
bash
sudo fail2ban-client status sudo fail2ban-client status sshd 示例输出:
text
Status for the jail: sshd |- Filter | |- Currently failed: 1 | `- Total failed: 12 `- Actions |- Currently banned: 2 |- Total banned: 3 `- Banned IP list: 198.51.100.20 2001:20 示例地址来自文档保留网段,不能作为真实封禁列表。需要确认 jail 已启动、失败计数会更新、action 没有报错,同时运维来源没有进入 banned 列表。
Fail2ban 状态显示 banned,不一定代表数据包确实被防火墙丢弃。应检查对应后端规则:
bash
sudo nft list ruleset | grep -i -A 20 -B 5 f2b sudo iptables-save | grep -i f2b sudo firewall-cmd --direct --get-all-rules 2>/dev/null | grep -i f2b || true 具体链名和 set 名取决于 action,不要依赖固定名称写清理脚本。
在具备隔离测试源且不会影响管理连接时,可以由授权人员执行一次人工 ban/unban:
bash
sudo fail2ban-client set sshd banip <测试IP> sudo fail2ban-client status sshd sudo fail2ban-client set sshd unbanip <测试IP>
<测试IP> 绝不能是当前 SSH 客户端、堡垒机、负载均衡健康检查或共享 NAT 出口。执行前先记录测试 IP 的责任人和验证时间,执行后必须确认已解除封禁。
如果通过控制台确认某个合法来源被封禁,优先做最小范围处理:
bash
sudo fail2ban-client set unbanip <被误封IP> sudo fail2ban-client status 随后从日志确认它为什么达到阈值,再决定是否调整认证客户端、阈值或可信网段。不要一误封就把整个 jail 永久关闭。
紧急情况下可以停止目标 jail:
bash
sudo fail2ban-client stop sudo fail2ban-client status 影响范围是该 jail 不再继续检测和封禁,对应 action 通常会清理自身规则,但必须实际检查防火墙。恢复前先修正配置并测试:
bash
sudo fail2ban-client -t sudo fail2ban-client start sudo fail2ban-client status 若配置或 action 导致范围性故障,先通过带外通道保留证据,然后:
bash
sudo systemctl stop fail2ban sudo systemctl status fail2ban --no-pager sudo nft list ruleset | grep -i f2b || true sudo iptables-save | grep -i f2b || true
服务正常停止时通常会执行 actionstop 清理规则,但不能假定一定成功。确认网络恢复后,从变更前备份恢复 /etc/fail2ban,再执行配置测试。恢复备份会覆盖现有配置,操作前应先把故障版本另存以便复盘:
bash
#!/usr/bin/env bash set -euo pipefail BACKUP_ARCHIVE="<备份目录>/fail2ban-<时间戳>.tar.gz" FAILED_COPY="<备份目录>/fail2ban-failed-$(date +%Y%m%d-%H%M%S).tar.gz" sudo tar -C /etc -czf "${FAILED_COPY}" fail2ban sudo tar -C /etc -xzf "${BACKUP_ARCHIVE}" sudo fail2ban-client -t
通过测试后再启动服务,并从第二管理链路验证。不要直接执行 nft flush ruleset 或清空全部 iptables 规则来恢复访问,这会同时移除其他安全策略,扩大事故范围。
maxretry = 3 看似更严格,但共享 NAT、自动化任务使用旧密钥、运维工具并发连接时很容易误封整个出口。建议先统计历史窗口内同一来源的失败分布,再决定阈值。
可以从 SSH journal 中抽取近期失败日志做人工分析,具体正则需根据本机日志格式调整:
bash
sudo journalctl -u --since '7 days ago' --no-pager | grep -E 'Failed password|Invalid user|authentication failure' > /tmp/sshd-failures-sample.log sudo fail2ban-regex /tmp/sshd-failures-sample.log /etc/fail2ban/filter.d/sshd.conf --print-all-matched
不要用未经验证的 awk 字段位置直接统计 IP,因为 OpenSSH 日志格式会随认证方式和消息类型变化。应先让 Fail2ban 自身 filter 验证提取结果,或使用结构化日志解析工具。
窗口太短只拦截高速爆破,无法发现低速尝试;窗口太长则更容易把零散的正常失败累积在一起。SSH 公网入口通常可以从 10 分钟到 1 小时的小范围灰度开始,但最终值必须由业务登录行为和安全策略决定。
较短 bantime 能降低共享地址误封影响,较长 bantime 能减少重复来源消耗。Fail2ban 的部分版本支持递增封禁,例如:
ini
[DEFAULT] bantime.increment = true bantime.factor = 2 bantime.maxtime = 1d 这些选项必须先用本机版本测试。启用后要监控长期封禁数量和误伤,不能把递增封禁等同于永久黑名单。
ignoreip 能避免堡垒机、VPN 出口和自动化节点被封禁,但它同时绕过该 jail 的检测处置。白名单过宽,攻击者一旦进入可信网络就不会被 Fail2ban 阻断;白名单长期不清理,旧办公出口或已回收云地址也可能被新租户继承。
先查看 jail 当前生效的忽略列表:
bash
sudo fail2ban-client get sshd ignoreip sudo fail2ban-client status sshd 配置审查应逐项记录地址归属、用途、负责人、批准单和到期时间。建议遵循:
SSH_CONNECTION 的第一列通常是 sshd 看到的客户端地址,可以作为核对线索:
bash
printf '%s ' "${SSH_CONNECTION:-not-an-ssh-session}"
这条值可能是堡垒机、NAT 或代理地址,不能把脚本输出自动追加到 ignoreip。必须与网络拓扑和认证日志交叉确认。
白名单变更会减少防护范围,也属于安全策略变更。执行前备份配置、确认第二管理通道并保存有效配置;修改后先测试:
bash
sudo fail2ban-client -t sudo fail2ban-client -d > /tmp/fail2ban-effective-config.after-ignoreip 审核差异后再重新加载:
bash
sudo fail2ban-client reload sudo fail2ban-client get sshd ignoreip sudo fail2ban-client status sshd
执行后从白名单内和非白名单的受控测试源分别验证。前者应保持可管理,后者触发失败时仍应被 jail 统计。回滚时恢复上一版 local 配置、运行 fail2ban-client -t,再 reload,并确认有效列表回到变更前状态。
不要因为一个合法用户被封禁,就把其整个运营商网段加入白名单。正确处理是先 unban 单个 IP,确认失败原因;如果是客户端保存了旧密码或自动化重复认证,应修复客户端。白名单只能基于明确的网络信任关系,不是误封的快捷补丁。
Web jail 比 SSH 更容易配置错误,因为客户端地址可能经过反向代理,日志字段也由应用决定。前提是访问日志中的来源 IP 可信:代理必须覆盖而不是盲目信任客户端提交的 X-Forwarded-For,应用和 Web 服务器只能接受来自受信代理的转发头。
假设应用日志经过验证后具有以下固定格式:
text
2026-07-16T1001Z login_failed remote_addr=192.0.2.10 user=alice reason=bad_password
可以创建 /etc/fail2ban/filter.d/app-login.local:
ini
[Definition] failregex = ^S+s+login_faileds+remote_addr=s+user=S+s+reason=bad_passwords*$ ignoreregex = 再创建 jail:
ini
[app-login] enabled = true filter = app-login backend = auto logpath = <日志文件> port = http,https findtime = 10m maxretry = 8 bantime = 30m ignoreip = 127.0.0.1/8 ::1 <管理网段> 上线前同时准备匹配样本和不应匹配样本:
bash
sudo fail2ban-regex <日志样本文件> /etc/fail2ban/filter.d/app-login.local --print-all-matched --print-all-missed 需要重点防止三类误匹配:
remote_addr。如果 Fail2ban 位于应用节点,而所有请求都来自负载均衡器,那么本机 L3/L4 防火墙通常只能看到负载均衡器地址。即便日志中有真实客户端 IP,封禁该 IP 的本机防火墙规则也可能拦不到它,因为数据包源地址已经变成代理地址。此时应在 WAF、反向代理或统一边界层实施限速与封禁,而不是在应用节点假装规则有效。
Fail2ban 包通常带有多种 action,例如 nftables、iptables 和 firewalld 相关文件。可先列出本机实际可用项:
bash
sudo find /etc/fail2ban/action.d -maxdepth 1 -type f ( -name '*nft*' -o -name '*iptables*' -o -name '*firewall*' ) -printf '%f ' | sort
不要从其他版本文档复制一个本机不存在的 banaction 名称。若确实需要覆盖默认 action,应在测试机验证,并通过 fail2ban-client -d 确认最终命令。示例:
ini
[DEFAULT] banaction = nftables-multiport
这个名称仅适用于本机存在对应 action 文件且 nftables 为批准后端的环境。使用 firewalld 时,应选择与其生命周期兼容的 action,并验证 firewall-cmd --reload 后 Fail2ban 规则是否仍然存在或能正确恢复。
IPv6 也需要验证。公网 SSH 同时监听 0.0.0.0 和 [::] 时,只封 IPv4 会留下直接绕过路径。检查 jail 是否能从日志提取 IPv6、action 是否创建了相应规则,并用受控 IPv6 测试源完成灰度。
使用 backend = auto、polling 等文本日志后端时,要验证 logrotate 后 Fail2ban 能继续跟踪新文件。先查看轮转配置:
bash
sudo grep -R "<日志文件>" /etc/logrotate.conf /etc/logrotate.d 2>/dev/null sudo fail2ban-client get logpath 不要为了测试直接强制轮转生产大日志,除非已经评估服务的 reopen 行为、磁盘空间和回滚。可以在测试环境执行同配置轮转,再观察 jail failure counter 是否继续更新。
journal 后端避免了路径和 inode 轮转问题,但仍依赖 journald 保留足够时间的日志。检查当前占用和持久化方式:
bash
sudo journalctl --disk-usage sudo systemd-analyze cat-config systemd/journald.conf 如果日志因空间策略或速率限制过早丢失,Fail2ban 无法从不存在的事件中判断攻击。修改 journald 参数会影响整机日志,必须单独评估磁盘和合规要求,本文不建议为 Fail2ban 盲目调大。
常见包会使用 /var/lib/fail2ban/fail2ban.sqlite3 保存持久状态。路径和保留时间以有效配置为准:
bash
sudo fail2ban-client -d | grep -E 'dbfile|dbpurgeage' sudo ls -lh /var/lib/fail2ban 2>/dev/null 不要在服务运行中直接删除或手工编辑 SQLite 文件。数据库异常时先停服务、备份文件、检查日志,并按当前版本维护方式处理。删除数据库会丢失封禁历史,属于有影响操作。
bash
sudo systemctl is-active fail2ban sudo fail2ban-client ping sudo fail2ban-client status sudo fail2ban-client status sshd sudo journalctl -u fail2ban --since '1 hour ago' --no-pager | grep -E 'ERROR|WARNING|Ban |Unban ' 关注指标不应只有“封了多少 IP”,还包括:
如果接入第三方 Fail2ban exporter,Prometheus 指标名会随实现不同而变化,必须以实际 exporter 暴露的指标为准,不能照抄不存在的指标。部署 exporter 时还要限制监听地址和访问权限,避免暴露封禁列表等安全信息。
下面脚本只读取状态和日志,不修改防火墙:
bash
#!/usr/bin/env bash set -euo pipefail JAIL_NAME="" command -v fail2ban-client >/dev/null 2>&1 || { echo "fail2ban-client not found" >&2 exit 1 } sudo systemctl status fail2ban --no-pager sudo fail2ban-client ping sudo fail2ban-client status sudo fail2ban-client status "${JAIL_NAME}" echo "Recent Fail2ban warnings and errors:" sudo journalctl -u fail2ban --since '24 hours ago' --no-pager | grep -E 'ERROR|WARNING' || true echo "Fail2ban-managed firewall objects:" if command -v nft >/dev/null 2>&1; then sudo nft list ruleset | grep -i -A 10 -B 3 f2b || true fi if command -v iptables-save >/dev/null 2>&1; then sudo iptables-save | grep -i f2b || true fi 该脚本不会判断某个 IP 是否恶意。封禁列表涉及安全处置,仍需关联认证日志、资产归属、威胁情报和变更记录。
普通 jail 只处理目标服务在当前时间窗口内的失败。某个来源可能每隔几小时重复被不同 jail 短暂封禁,却在解封后继续尝试。Fail2ban 随包提供的 recidive filter 可以读取 Fail2ban 自身的 Ban 日志,对反复触发者施加更长封禁。
启用前先确认 Fail2ban 确实写文件日志:
bash
sudo fail2ban-client -d | grep -E 'logtarget|dbfile' sudo test -r /var/log/fail2ban.log && sudo tail -n 30 /var/log/fail2ban.log sudo test -r /etc/fail2ban/filter.d/recidive.conf && sudo sed -n '1,160p' /etc/fail2ban/filter.d/recidive.conf
如果 logtarget 仅指向 systemd journal,/var/log/fail2ban.log 不存在,就不能照抄文件型 recidive 配置。应先根据本机版本确定受支持的日志来源;不要为了一个 jail 随意改变全局日志路径。
文件日志条件满足后,可在测试环境评估:
ini
[recidive] enabled = true filter = recidive backend = auto logpath = /var/log/fail2ban.log findtime = 1d maxretry = 5 bantime = 1w ignoreip = 127.0.0.1/8 ::1 <管理网段> 这些时间值只是便于理解配置关系,不是所有企业都适用的推荐默认值。recidive 的误封影响更大:移动网络、家庭宽带和云代理的公网地址会重新分配,长时间封禁可能落到无关用户;共享 NAT 下,一个用户的失败也会影响整个出口。
上线前要用已有 Fail2ban 日志测试:
bash
sudo fail2ban-regex /var/log/fail2ban.log /etc/fail2ban/filter.d/recidive.conf --print-all-matched
抽查匹配是否来自真实 Ban 记录,并确保 recidive 自己产生的日志不会形成错误递归。完成配置测试后,只在单台非关键节点灰度,观察长期封禁列表和误伤。
recidive 不是威胁情报系统。是否进行跨主机、跨业务的长期阻断,应由集中安全平台结合资产、ASN、代理、地理位置和攻击行为判断。主机上的一个 jail 不应直接把地址同步为企业全网永久黑名单。
Fail2ban 默认是单机状态:A 节点看到的失败不会自动让 B 节点封禁。如果同一服务后面有多台主机,攻击请求经负载均衡分散后,单机阈值可能永远达不到。可选方案包括在统一入口限速、集中日志检测后调用边界防火墙、WAF 或安全平台策略。
把封禁 IP 从一台主机自动同步到全网,是影响范围很大的安全自动化。必须处理 NAT 共享地址、IPv6、过期时间、冲突恢复、审计和回滚,不能用一个未经认证的消息队列脚本直接广播。
在容器和 Kubernetes 环境中还要确认网络路径:
如果目标是保护 Ingress 登录接口,通常优先使用 Ingress Controller 原生限速、WAF、身份平台锁定策略和集中安全控制;Fail2ban 更适合能同时看到可信日志和真实数据包来源的节点。
Fail2ban 只能对日志中观察到的失败做反应。攻击者如果已经获得有效密钥、复用被盗会话或通过漏洞进入系统,可能只产生一次成功登录,完全不会触发 maxretry。因此发现暴力破解时,不能把“已封禁攻击 IP”当作事件结束。
先记录告警时间、主机时区、jail、来源地址、失败总数和当前封禁状态。导出目标窗口的原始日志,文件放入受保护的取证目录:
bash
#!/usr/bin/env bash set -euo pipefail EVIDENCE_DIR="<取证目录>" START_TIME="<开始时间>" END_TIME="<结束时间>" sudo install -d -m 0700 "${EVIDENCE_DIR}" sudo journalctl -u --since "${START_TIME}" --until "${END_TIME}" --output=short-iso-precise --no-pager | sudo tee "${EVIDENCE_DIR}/ssh-journal.log" >/dev/null sudo journalctl -u fail2ban --since "${START_TIME}" --until "${END_TIME}" --output=short-iso-precise --no-pager | sudo tee "${EVIDENCE_DIR}/fail2ban-journal.log" >/dev/null sudo sha256sum "${EVIDENCE_DIR}/ssh-journal.log" "${EVIDENCE_DIR}/fail2ban-journal.log" | sudo tee "${EVIDENCE_DIR}/SHA256SUMS" >/dev/null
<开始时间> 和 <结束时间> 应使用 systemd 能解析、且包含明确时区的时间格式。日志中含账号和 IP,应限制访问并按取证流程保存。不要为了节省空间先截断或清理原日志。
bash
sudo journalctl -u --since '<开始时间>' --until '<结束时间>' --no-pager | grep -E 'Accepted (password|publickey)|session opened' last -Fai | head -n 50 sudo lastb -Fai | head -n 50
last 和 lastb 依赖 wtmp/btmp,文件可能被轮转、损坏或未启用,不能单独作为无入侵证明。需要把成功登录来源、账号、密钥指纹、sudo 记录、堡垒机审计和云平台登录记录关联起来。
如果系统启用了 auditd,可补充查询:
bash
sudo ausearch -m USER_LOGIN,USER_AUTH -ts '<开始时间>' -te '<结束时间>' -i
不同 audit 版本接受的时间格式有限,应先看 ausearch --help。没有 audit 记录不能据此假定没有登录。
bash
getent passwd sudo stat /etc/passwd /etc/shadow /etc/group /etc/sudoers sudo find /root /home -xdev -type f ( -name authorized_keys -o -name authorized_keys2 ) -printf '%TY-%Tm-%Td %TH:%TM:%TS %u:%g %m %p ' sudo systemctl list-timers --all --no-pager sudo find /etc/cron.d /etc/cron.daily /var/spool/cron -xdev -type f -printf '%TY-%Tm-%Td %TH:%TM:%TS %u:%g %m %p ' 2>/dev/null 判断要与资产基线和配置管理记录比较。文件修改时间异常是线索,不是确定入侵的结论;正常补丁、账号开通和自动化也会改变这些文件。
继续采集进程和网络快照:
bash
ps -eo user,pid,ppid,lstart,etime,cmd --forest sudo ss -plant sudo lsof -nP -i 2>/dev/null 命令输出可能含敏感参数。不要直接把完整进程命令行贴到公开工单,先按安全流程脱敏。
如果发现未知成功登录、陌生密钥、异常提权、可疑外连或持久化项,应升级为安全事件。网络隔离、磁盘快照、密钥吊销和主机重建都属于高影响操作:
不要在已疑似失陷的主机上只改密码、安装 Fail2ban 后继续运行。攻击者可能拥有 root 权限或持久化后门,原机已经不能作为可信执行环境。凭证轮换应从干净终端完成。
以下证据更接近常见互联网扫描:大量不存在用户名、分散来源、没有成功认证、服务与文件基线无变化。以下证据需要立即升级:同一失败来源随后成功登录、非预期账号成功、登录后出现 sudo、密钥或定时任务变化、异常外连、日志缺口或清理痕迹。
结论必须由日志、审计、配置差异和进程网络证据共同支撑。Fail2ban 的 Total banned 只是处置计数,不能说明主机是否安全。
修改 sshd 是高风险操作,错误配置会造成远程失联。变更前保持当前会话、准备带外入口、备份配置,并用 sshd -t 做语法检查。示例流程:
bash
#!/usr/bin/env bash set -euo pipefail BACKUP_DIR="<备份目录>" STAMP="$(date +%Y%m%d-%H%M%S)" sudo install -d -m 0700 "${BACKUP_DIR}" sudo cp -a /etc/ssh/sshd_config "${BACKUP_DIR}/sshd_config.${STAMP}" sudo sshd -t sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin|pubkeyauthentication) '
实际修改建议写入受控的 sshd_config.d 文件,但要先确认主配置包含对应 Include,且选项没有被后续 Match 段改变。完成后运行:
bash
sudo sshd -t sudo systemctl reload sudo systemctl status --no-pager
reload 仍是有影响操作。保留原会话,从第二终端验证密钥登录成功后再退出。若失败,立即通过原会话恢复备份、执行 sshd -t,再 reload。不能在未验证新会话前关闭唯一连接。
能通过安全组、ACL、VPN 或堡垒机把 SSH 只开放给管理网段时,应优先缩小暴露面。防火墙变更必须先确认当前来源 IP、冗余管理通道、规则优先级和云平台回滚方式,再做精确灰度。不要在 SSH 会话中直接把默认策略改为 DROP,而没有先放行当前管理路径。
Fail2ban 主要处理失败行为;真正危险的往往是一次成功的异常登录,所以成功认证监控同样重要。
多台服务器不应靠手工编辑 jail.local。应把本地覆盖文件纳入配置管理,按操作系统、Fail2ban 版本、防火墙后端和服务角色生成配置。模板变量至少包括管理网段、SSH 端口、日志后端和阈值,敏感通知凭证必须从密钥系统注入。
每次发布前在同版本测试节点执行:
bash
sudo fail2ban-client -t sudo fail2ban-client -d > /tmp/fail2ban-effective-config.new sudo diff -u <变更前有效配置文件> /tmp/fail2ban-effective-config.new 有效配置输出可能因顺序或运行环境存在差异,需要人工关注 jail、filter、backend、ignoreip 和 action。批量发布建议按“测试节点 → 单台生产 → 单可用区小批 → 全量”进行,每批验证远程登录、jail 状态、防火墙规则和误封告警。
Fail2ban 支持通过客户端重新加载配置:
bash
sudo fail2ban-client -t sudo fail2ban-client reload sudo fail2ban-client status
reload 仍可能改变现有 jail 和防火墙规则,不能视为无影响操作。当前版本支持的 reload 选项以 fail2ban-client --help 为准,不要复制 --restart、--unban 等版本相关参数。批量变更必须保留暂停开关和上一版配置,任何一批出现登录异常都停止扩散。
Fail2ban 的 Ban 日志不应全部升级为同一优先级。可以根据资产暴露面和关联证据分层:
每条高优先级告警至少保留主机、jail、来源地址、首次/末次时间、失败次数、封禁时长、对应原始日志位置和后续成功认证检查。来源 IP 是线索,不等同于攻击者身份;VPN、代理、Tor、云主机和共享 NAT 都会影响归属判断。
处置完成后记录 unban、白名单、阈值调整或边界阻断的操作人和工单号。若调整 filter,必须附带修改前后的匹配样本与 fail2ban-regex 结果,证明没有把成功事件或代理地址纳入匹配。若只是临时 unban,还要说明导致合法失败的客户端何时修复,避免同一问题反复触发。
安全监控还应关注数据缺失:Fail2ban 进程存活但日志源断开、journal 保留不足、filter 因服务升级不再匹配,都可能让封禁数突然归零。把“零 Ban”直接当作环境安全,会形成盲区。
在生产启用前逐项确认:
fail2ban-regex
已在真实历史样本上验证匹配和漏匹配。ignoreip。
Fail2ban 的价值不是让日志里“Ban”越多越好,而是把重复恶意尝试变成可验证、可回滚的本机处置。可靠部署必须从真实日志出发,用 fail2ban-regex 证明 filter,用防火墙规则证明 action,再通过第二管理链路证明不会锁死运维入口。
当系统规模扩大到多节点、反向代理或 Kubernetes 后,日志中的客户端 IP 与防火墙实际看到的源地址可能已经不同。此时应把处置上移到能够看见真实流量的入口层。无论架构怎样变化,Fail2ban 都只能是分层防御的一环:缩小暴露面、强化身份、持续修补和监控异常成功登录,才是抵御暴力破解的主体措施。
全部0条评论
快来发表一下你的评论吧 !