概述 —— 这个功能做什么
自动封禁是 InfraGuard 的后台流水线,无需人工介入即可检测、甄别并封禁攻击者 IP。它不是一个页面,而是横跨采集器、调度器与内核 ipset 的一条流程。本文说明其整体结构。
该流水线包含两条相互独立的封禁路径,它们最终汇聚到同一个生效点(ipset)。
| 路径 | 运行形式 | 写入目标 | 粒度 | 来源 |
|---|---|---|---|---|
| ① 采集器自动封禁 | 各主机上的 Node 采集器(srct_tx · kts_tx · eve_ingest) |
blacklist_info |
/24(C 类) | Suricata L7 · Kitsune ML |
| ② 调度器自动封禁 | InfraGuard 应用(infr:auto-block → AutoBlockService) |
infr_block |
/32(单个)或 /24 | suricata_alert_ct · suricata_tls_ct |
| ③ 强制生效(共用) | infr_sync_black.mjs(主机 cron) |
内核 ipset blacklist |
hash:net | ① + ② 的并集 |
两条路径在封禁前都会立即执行白名单与无罪判定检查。 只有确认存在滥用行为的条目才会保留;很可能是误报的候选会被豁免。路径 ②(调度器)默认关闭,必须由管理员启用。
何时使用
- 当僵尸网络、扫描器和暴力破解探测持续涌入,手动封禁已经跟不上时,启用自动防御。
- 当检测 ▸ JA3 显示出轮换指纹的僵尸网络时,调整阈值使其能被自动捕获。
- 当某个来源在威胁中反复触发指定严重级别(红色/橙色)的 IDS 告警,而您希望自动封禁它时。
- 当您在封禁列表与信誉中审阅不断累积的封禁依据,并通过白名单豁免误封时。
页面构成
自动封禁没有专属页面。它的控制点与观测点如下:
- 配置 —— 运维策略 ▸ 安全设置标签页 ▸ 自动封禁卡片(总开关、模式、范围、触发条件、无罪判定)。
- 结果观测 —— 封禁列表与信誉(采集器的 /24 + 调度器的 /32 封禁记录、依据、解除)。
- 豁免 —— 白名单(手动/自动无罪判定的 IP 与 CIDR,带有效期)。
- 触发来源 —— 威胁(IDS 严重级别)、检测(JA3 轮换)。
使用方法
1. 路径 ② 调度器自动封禁 —— 启用与调优
默认为关闭(autoblock_on=0)。先在运维策略 ▸ 安全设置中拨动总开关,然后再调整策略。
模式(mode)
| 取值 | 含义 |
|---|---|
global(默认) |
在一台服务器上发现的入侵会应用到整个机群(server_index=0)。 |
per_server |
仅应用于被攻击的那台服务器 —— 目标 dest_ip 会通过 infr_server_index 解析为 server_index。 |
范围(scope)
| 取值 | 调度器写入 | 说明 |
|---|---|---|
balanced(推荐,默认) |
单个 /32 | IDS 程序使用 /24;InfraGuard 追加单个 /32 —— sync_black 会跳过任何已被某个 /24 覆盖的 /32。 |
broad |
/24 | 封禁整个 C 类网段 —— 覆盖面广,但有误伤无辜邻居 IP 的风险。 |
granular |
单个 /32 | 一次一个 IP —— 精确,但面对轮换时反应较慢。 |
触发条件(OR —— 候选只需满足其中一项)
① IDS 规则集严重级别 suricata_alert_ct
WHERE alert_ts >= NOW() - INTERVAL {window_min} MINUTE
AND severity <= {sev_max} -- crit=1, warn=2(Suricata:1=最严重 … 3=低)
GROUP BY src_ip HAVING COUNT(*) >= {sev_count}
② JA3 轮换僵尸网络 suricata_tls_ct
WHERE event_ts >= NOW() - INTERVAL {ja3_window_min} MINUTE
AND ja3_hash IS NOT NULL
GROUP BY src_ip HAVING COUNT(DISTINCT ja3_hash) >= {ja3_rotation}
严重级别触发条件使用的时间列是
alert_ts,而 JA3 触发条件使用的是event_ts(因表而异)。调整阈值时,请留意各自的时间窗是按哪一列过滤的。
策略默认值(OpsPolicy::autoBlock())
| 设置键 | 默认值 | 取值限制 | 含义 |
|---|---|---|---|
autoblock_on |
0 |
— | 总开关(1=开启)。默认关闭 |
autoblock_mode |
global |
global|per_server | 封禁模式 |
autoblock_scope |
balanced |
broad|granular|balanced | 封禁范围 |
autoblock_interval_min |
10 |
≥1 | 实际扫描间隔(分钟) |
autoblock_window_min |
10 |
≥1 | 严重级别评估时间窗(分钟) |
autoblock_sev_on |
1 |
— | 启用触发条件 ① |
autoblock_severity |
crit |
crit|warn | crit=sev≤1,warn=sev≤2 |
autoblock_sev_count |
1 |
≥1 | 时间窗内最少告警条数 |
autoblock_ja3_on |
1 |
— | 启用触发条件 ② |
autoblock_ja3_rotation |
50 |
≥2 | 不同的 JA3 指纹数 ≥ N |
autoblock_ja3_window_min |
360 |
≥1 | JA3 评估时间窗(分钟 · 6 小时) |
autoblock_innocent_on |
1 |
— | 启用无罪判定否决 |
autoblock_innocent_days |
30 |
≥1 | 自动加入白名单的时长(天) |
autoblock_reputation_cap |
25 |
0–200 | 每轮扫描的信誉查询次数 |
JA3 轮换调优(实测):现实中的 JA3 轮换攻击者通常在每个 /32 会话约 32 个不同 JA3 处达到峰值(数据中心/云端机器人,不使用 GREASE,因此稳定且可封禁)。因此默认的
ja3_rotation=50什么也捕获不到。 要让它真正触发,请降到 15–20 左右。先运行--dry查看候选数量。
2. 候选甄别顺序(AutoBlockService::run)
每个收集到的候选 IP 在真正被封禁之前,必须依次通过以下关卡。
- 内部/自有网段排除 —— 若
SecurityScope::isInternal($ip)成立则跳过(skipped_internal)。该判定把已登记实例的 IP(/24 前缀+精确 IP)与运维策略中的internal_networks白名单合并考虑。查询阶段还会通过NOT LIKE预先排除excludePrefixes()。 - 白名单豁免 —— 若
infr_whitelist中存在有效且未过期的 /32 或 /24 条目则跳过(skipped_wl)。 - 无罪判定否决 —— 仅适用于没有硬信号(严重级别触发条件)的纯 JA3 候选。在
reputation_cap的预算内查询 AbuseIPDB 信誉;如果该 IP 完全清白(滥用评分为 0 且 举报数为 0 且 非 Tor),则不予封禁,而是自动加入白名单innocent_days天(infr_whitelist(source=auto),即innocent)。严重级别候选会跳过此关卡,直接封禁。 - 解析 server_index ——
global→[0],per_server→通过ServerIndexResolver::byIp解析目标dest_ip(回退为[0])。 - 写入 —— 通过
InfrBlock::updateOrCreate([server_index, cidr], …)upsert 到infr_block,并带上source='schedule'、is_active=true、reason='[InfraGuard Auto] …'。
收集候选(OR) → 排除内部 → 白名单 → (纯 JA3)无罪判定否决 → server_index → 写入 infr_block
当信誉查询失败、未配置或无法判定时,
isSpotless()返回 false(即谨慎封禁)。无罪豁免是一道保守的关卡,只有在 IP 明显清白时才会生效。
3. 调度器与命令
infr:auto-block 是一条只评估一次就退出的单次命令,而不是常驻守护进程。调度器每分钟触发一次,但实际间隔由 autoblock_interval_min 决定。
# 检查门控(若策略为关闭则立即空操作),随后评估并写入一次
php artisan infr:auto-block
# 只统计候选数量,不写入(用于调优/验证)
php artisan infr:auto-block --dry
# 绕过策略门控(开关+间隔),强制运行一次
php artisan infr:auto-block --force
# 未产生封禁时抑制输出(用于定时调度)
php artisan infr:auto-block --quiet-if-ok
调度注册(routes/console.php):
Schedule::command('infr:auto-block --quiet-if-ok')
->everyMinute()
->withoutOverlapping(2)
->onOneServer()
->when($is_leader); // 主节点门控 —— 整个机群中只有一个节点执行
门控逻辑:
autoblock_on=0→ 命令立即报告“已跳过”(默认关闭,安全)。- 距上次运行尚未达到间隔 → 跳过。
--force与--dry会绕过门控。
输出示例:
infr:auto-block —— 候选 12(sev 3 · ja3 9) · 封禁 7 · wl-skip 1 · internal-skip 2 · innocent 2
4. 路径 ① 采集器自动封禁(Node)
各主机上的 Node 采集器消费 Suricata 事件,并按 /24 粒度直接写入 blacklist_info 完成封禁。该路径独立于调度器(路径 ②),且始终运行。
| 采集器 | 层次 | 检测内容 |
|---|---|---|
infr_eve_ingest.cjs |
摄取 | Suricata eve.json → 载入 suricata_http_ct · suricata_tls_ct · suricata_flow_ct · suricata_alert_ct |
infr_srct_tx.cjs |
特征/启发式 | 聚合 Suricata L7 → 5 类扫描器/机器人检测 → blacklist_info /24 |
infr_kts_tx.cjs |
Kitsune ML | conntrack + 自编码器异常检测(anomaly_score) → blacklist_info /24 |
srct_tx 的五类威胁:
(A) [HTTP] URL 扫描器 .env / .git / wp-login 等带扫描器特征的 URL
(B) [HTTP] 机器人 User-Agent curl / python-requests / sqlmap 等工具特征
(C) [HTTP] 暴力探测 短时间内大量 4xx 响应(路径暴力枚举)
(D) [TLS] JA3 异常 JA3 数量多、缺失 SNI 等僵尸网络指纹
(E) [FLOW] TCP 标志异常 大量短促的扫描会话(端口扫描)
srct_tx 同样会在封禁前执行白名单过滤+自动无罪豁免:只有 E-only(仅短扫描)候选才会做 AbuseIPDB 信誉查询,若明显清白(举报数 0 · 滥用评分 0 · 非 Tor)则登记为 30 天豁免(infr_whitelist(source=auto))并跳过封禁。只要存在任一硬信号(A/A2/B/C/D),就直接封禁。其常量与阈值在规则与阈值中管理。
5. 路径 ③ 强制生效 —— sync_black → ipset
infr_sync_black.mjs 在各主机上周期性运行,把两份名单同步进内核 ipset blacklist。
# 按主机运行,并传入其 server_index(需要 root)
node infr_sync_black.mjs sv=4
# crontab(每 5 分钟一次,用 flock 防止重叠执行)
*/5 * * * * /usr/bin/flock -n /var/run/sync_black.lock \
/usr/bin/node /root/server_monitor/infr_sync_black.mjs sv=4 \
>> /var/log/sync_black.log 2>&1
行为:
- 按
server_index IN (0, <sv>) AND is_active/is_use=1从blacklist_info(/24)+infr_block(/32)载入数据。 - balanced 去重 —— 跳过父级 /24 已经覆盖的任何 /32。
- 强制把内核 ipset 设为
hash:net(类型不符则重建),先载入临时集合,再原子交换(无中断)。 - 确保
iptables -I INPUT -m set --match-set blacklist src -j DROP存在(幂等)。 - 安全网 —— 私有地址(RFC1918)、回环地址与自有网段前缀(
HOME_NET_PREFIX)即使混入数据库,也绝不会被载入 ipset。
sync_black需要 root 权限以及内核的ipset/iptables工具,并且目标主机上必须已经存在blacklistipset:ipset create blacklist hash:net family inet hashsize 8192 maxelem 65536 iptables -I INPUT -m set --match-set blacklist src -j DROP
数据来源
所有来源表与记录表都位于 legacy_glory 连接上(采集器与强制生效脚本共用的数据库)。
| 表 | 连接 | 作用 | 粒度 |
|---|---|---|---|
suricata_alert_ct |
legacy_glory | 严重级别触发条件的来源(列 alert_ts · severity · src_ip · dest_ip · signature) |
— |
suricata_tls_ct |
legacy_glory | JA3 轮换触发条件的来源(列 event_ts · ja3_hash · src_ip) |
— |
blacklist_info |
legacy_glory | 采集器自动封禁名单(IDS 程序 · srct/kts) | /24 |
infr_block |
legacy_glory | 由 InfraGuard 调度器拥有的封禁名单(迁移 000751) | /32·/24 |
infr_whitelist |
legacy_glory | 封禁豁免(手动 · 自动无罪 · 种子数据) | /32·/24 |
infr_server_index |
default(MAIN) | 稀疏、非连续的 server_index ↔ 别名/实例对照表(000750) | — |
infr_block 表结构(摘要)
| 列 | 类型 | 含义 |
|---|---|---|
server_index |
int | 0=全局 · 其他=通过 infr_server_index 解析出的服务器 |
cidr |
varchar(43) | 单个 IP(/32)或归一化后的 /24 · UNIQUE(server_index,cidr) |
ip |
varchar(45) | 原始 IP(用于展示) |
reason |
varchar(500) | 触发依据([InfraGuard Auto] sev1 x3 …) |
source |
varchar(16) | auto · manual · schedule |
is_active |
bool | sync_black 是否载入该条目 |
expires_at |
datetime | NULL=永久 |
last_seen_at |
datetime | 最近一次重新检出(用于清理判定) |
访问权限
自动封禁没有面向用户的执行界面。相关的权限门控如下(级别:demo=1 · staff=5 · admin=10):
| 对象 | 所需权限 |
|---|---|
| 自动封禁策略配置(运维策略 ▸ 安全设置) | 管理员(管理,级别 ≥5 / admin) |
| 查看封禁结果(封禁列表) | 安全只读(staff,级别 ≥2) |
| 手动封禁/解除 · 写入白名单 | staff(级别 ≥2)+审计日志 |
调度器执行(infr:auto-block) |
系统(并非登录用户)—— 受主节点门控 |
调度器所用的凭据从不硬编码 —— 它们被外部化到
infr_collector/.env。sync_black的数据库凭据与自有网段前缀同样通过环境变量或“清单 ▸ 工具”的混搭替换注入(不做硬编码)。
故障排查与常见问题
已经开启,但什么都没被封禁。
- 用
--dry查看候选数量:php artisan infr:auto-block --dry。 - JA3 阈值很可能过高(默认 50)。真实攻击者大约在 32 上下,因此请降到 15–20。
- 严重级别触发条件:
autoblock_severity=crit只捕获 sev≤1(红色)。若要包含橙色,请设为warn(sev≤2)。 - 如果候选全部是
internal-skip/wl-skip,请检查运维策略中的内网白名单或白名单。
封禁没有落到真正的防火墙上。
infr_block中的条目在sync_black运行之前不会生效。请检查目标主机的 cron、sv=<server_index>参数,以及blacklistipset 是否存在。- 在
balanced范围下,已被父级 /24 覆盖的 /32 会被有意去重,不再单独载入(这是正常现象)。
无辜的 IP 被封禁了。
- 在白名单中以 /32 或 /24 登记,从下一轮扫描起即可豁免(现有的
infr_block记录请在封禁列表中解除)。 - 自动无罪豁免仅适用于纯 JA3 候选。被严重级别触发条件捕获的 IP 无论信誉如何都会被封禁,因此需要手动豁免。
明明是 per_server,记录却落成了全局(server_index=0)。
- 如果目标
dest_ip无法通过infr_server_index解析,就会安全地回退为[0]。请核对采集器/实例的 IP 映射。
调度器在多台服务器上同时运行。
->onOneServer()->when($is_leader)的主节点门控可确保只有一个节点执行。请在可观测性仪表板中查看主节点锁的状态。
相关条目
- 运维策略 —— 自动封禁总开关、触发条件、范围(安全设置标签页)
- 封禁列表与信誉 —— 自动封禁结果列表、依据、解除
- 白名单 —— 豁免的 IP/CIDR、自动无罪豁免
- 检测 ▸ JA3 —— JA3 轮换触发条件的来源
- 威胁 —— IDS 严重级别触发条件的来源
- IP 信誉(AbuseIPDB) —— 无罪判定否决所用的信誉查询

