封禁
按 IP 地址或 CIDR 临时拦截客户端。封禁经节点通道直接下发,数秒内生效,不产生配置版本。
概念
| 术语 | 定义 |
|---|---|
| 封禁 | 一个 IP 地址或 CIDR,到期前对应请求被拦截。 |
| 范围 | 网站:只作用于一个网站。全局:作用于所有集群的全部网站,并由节点在内核丢包(见内核封禁)。API 中分别为 site 与 platform。 |
| 来源 | 手动:在 封禁 页或经 API 创建。自动:节点按触发条件自行产生并上报。 |
| 到期 | 1 分钟到 7 天。长期拦截用 IP 名单。 |
封禁地址
-
打开 封禁,点击「新建封禁」。
-
选择「范围」:「网站」或「全局」。
-
范围为「网站」时,在「网站」中选择网站;网站较多时先在「搜索网站」中输入名称或域名。
-
填写「IP 或 CIDR」,例如
203.0.113.7或198.51.100.0/24。 -
选择「原因」与「时长」(1 小时、6 小时、1 天、3 天、7 天),点击「封禁」。
-
验证:从被封禁的地址请求该网站:
curl -sI -H 'Host: www.example.com' http://<节点 IP>/返回 403,响应头含
X-Edgeweir-Error: ip-banned。网站封禁不影响同一地址访问其他网站。
解封:点击该行的「解封」并确认。节点在数秒内移除该封禁。手动与自动封禁都可解封。
不必重新输入:访问日志、统计中的热门 IP、网站「安全」页签的 Top IP 与自动封禁事件,在行右侧的「⋯」中提供「封禁 IP」,对话框已填好网站与地址(「范围」可改为「全局」),按 ⌘K / Ctrl+K 选择「封禁 IP…」打开同一对话框(在网站页面时预填该网站)。封禁后提示中的「查看封禁」打开列表。链接 /bans?site=<网站 ID>&ip=<地址> 打开同样预填的对话框,列表只显示与该地址重叠的封禁(地址旁的 × 清除)。
列表只显示有效的封禁(未到期、未解封),按创建时间倒序,可按范围、网站与来源筛选:
| 列 | 内容 |
|---|---|
| 地址 | 规范化的 CIDR;有节点未能保存该封禁时显示「N 个节点未生效」 |
| 网站 | 网站名称;全局封禁显示「全局」 |
| 原因 | 封禁原因 |
| 来源 | 「手动」及操作者;「自动」及来源节点与触发条件(指标、观测值 / 阈值、窗口秒数);未下发的自动封禁另标「未共享」 |
| 到期 | 剩余时间;悬停显示到期时间 |
规则
| 项目 | 行为 |
|---|---|
| 地址 | IPv4 / IPv6 地址或 CIDR;单个地址视为 /32 或 /128;清除主机位,IPv6 小写并压缩;::ffff:a.b.c.d 按对应的 IPv4 处理;拒绝前导零写法和 zone ID |
| 前缀下限 | IPv4 /16,IPv6 /48 |
| 到期 | 1 分钟到 7 天(界面提供 1 小时到 7 天的选项);节点按到期时间自动失效,控制台在到期一小时后删除记录 |
| 原因 | 手动:滥用、攻击、扫描、垃圾请求、其他;自动:单 IP 请求过快 |
| 再次封禁 | 同一地址的全局封禁、或同一网站与地址的网站封禁,只有一条有效的手动封禁;再次封禁更新原因与到期时间,不增加数量 |
| 受保护地址 | 网站封禁不能包含该网站所在集群的节点地址,全局封禁不能包含任何节点地址;封禁也不能包含回环地址(127.0.0.0/8、::1)与未指定地址(0.0.0.0/8、::),不能与放行名单重叠 |
| 执行位置 | 边缘层确定网站之后、规则之前:先查全局封禁,再查网站封禁;放行名单中的地址不受封禁 |
| 下发 | 不产生配置版本、不经配置金丝雀、节点不重载 |
| 审计 | ban.create、ban.update(再次封禁)、ban.delete;自动封禁不写审计 |
数量上限
| 上限 | 计数 | 设置 |
|---|---|---|
| 手动封禁上限 | 有效的手动封禁,全局与网站封禁合计 | 防护设置 → 封禁 → 手动封禁上限;默认 10000,范围 100–100000 |
| 自动封禁 | 每个集群的有效自动封禁 | 固定 10000 条;超出时最早的自动封禁失效 |
自动封禁不计入手动封禁上限。达到上限时新建封禁返回 BAN_PLATFORM_LIMIT(「手动封禁已达上限(N 条)」);再次封禁仍有效的地址不受上限限制。
自动封禁
节点按触发条件(CC 防护的「单 IP QPS」,见挑战与 CC 防护)自行封禁:IPv4 封单个地址,IPv6 封所在的 /64(一个客户端通常持有整个 /64);回环地址从不封禁。封禁立即在该节点生效,并每 5 秒批量上报控制台。
| 项目 | 行为 |
|---|---|
| 共享 | 防护设置 → 封禁 → 集群内共享自动封禁,默认开启:开启时下发到同集群的全部节点;关闭时只保存供查看,列表标记「未共享」。修改只影响之后新增的自动封禁 |
| 合并 | 同一节点、网站与地址只保存一条;再次上报时延长到期时间 |
| 校验 | 网站须属于该节点所在的集群;只接受单个地址;到期最长为创建后 7 天;受保护地址不保存 |
| 解封 | 与手动封禁相同,点击该行的「解封」;未共享的自动封禁由产生它的节点在数秒内删除(不支持的旧版节点要到期才失效),该节点之后再次自行封禁同一地址不受影响 |
节点
| 项目 | 行为 |
|---|---|
| 节点能力 | 执行封禁需要 bans-v1;未上报该能力的旧版节点照常服务,不执行封禁 |
| 同步 | 节点只取得本集群的封禁与全局封禁。节点保存已应用的序号,只拉取之后的变化;首次连接或控制台数据库恢复后取得完整快照。节点把封禁落盘,重启后先装入再连接控制台 |
| 容量 | 节点参数 --ban-capacity(默认 100000 条)与 --ban-dict-mb(默认 32 MiB)。空间不足时先淘汰最早的自动封禁 |
| 未生效 | 手动封禁写不下时不会静默丢弃:节点上报未生效的封禁,列表显示「N 个节点未生效」(只计在线节点),节点每分钟重试 |
| 状态 | 节点每次心跳上报封禁状态:已应用序号、条目数、容量、未生效的封禁、内核条目数与淘汰的自动封禁数。经 GET /api/v1/nodes/{id} 的 banStatus 读取 |
内核封禁
全局封禁同时由节点 agent 写入 nftables,在内核丢弃被封地址的入站数据包,被封客户端连 TCP 与 TLS 握手都无法完成。网站封禁只在 HTTP 层执行。L4 应用 只受内核封禁约束:网站封禁与 HTTP 层的封禁不作用于四层流量。
| 项目 | 行为 |
|---|---|
| 条件 | agent 具备 CAP_NET_ADMIN,主机有 nft。agent 启动时试建表,成功才上报能力 kernel-ban-v1;失败时只做 HTTP 层封禁并记录日志 |
| 表 | agent 只管理自己的表 inet edgeweir(集合 ban4、ban6、allow4、allow6,input 链),启动时清掉残留,退出时删除 |
| 永不封禁 | 控制台地址、节点自身地址、回环地址、放行名单 |
| 出站连接 | 被封地址的入站包全部丢弃,节点也无法与该地址建立连接(例如该地址恰好是源站) |
默认的 systemd 单元与节点镜像不授予 CAP_NET_ADMIN,需要时按下列步骤启用。
systemd 节点
-
安装 nftables:
节点 sudo apt-get install -y nftables # Debian、Ubuntu sudo dnf install -y nftables # RHEL 系 -
创建 drop-in,在已有的
CAP_NET_BIND_SERVICE之外增加CAP_NET_ADMIN:/etc/systemd/system/edgeweir-node.service.d/kernel-ban.conf [Service] AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN -
重新加载并重启:
节点 sudo systemctl daemon-reload sudo systemctl restart edgeweir-node -
验证:
sudo nft list table inet edgeweir列出集合ban4与ban6;GET /api/v1/nodes/{id}的supportedFeatures含kernel-ban-v1。
环境能力同样被 agent 启动的 OpenResty 进程继承。
容器节点
-
以构建参数
NFT_CAPABILITY=true构建节点镜像。镜像为nft设置文件能力,agent 仍以非 root 用户运行:edgeweir-node 源码目录 docker build --build-arg NFT_CAPABILITY=true -t edgeweir-node:nft . -
运行容器时加
--cap-add NET_ADMIN(Compose:cap_add: [NET_ADMIN])。 -
验证:
docker exec <容器> nft list table inet edgeweir列出集合ban4与ban6;GET /api/v1/nodes/{id}的supportedFeatures含kernel-ban-v1。
节点前的负载均衡
集群的访客 IP 设置决定 HTTP / HTTPS 监听的访客地址(ip.src):
| 访客 IP | HTTP 层封禁、CC、规则、日志 | 内核封禁 |
|---|---|---|
| 直连(默认) | TCP 对端;节点前有负载均衡时为负载均衡器的地址,自动封禁会把它整个封掉 | TCP 对端 |
| PROXY protocol | PROXY 头中的访客地址 | 仍只匹配 TCP 对端(负载均衡器),对访客地址的封禁不在内核生效 |
| 可信代理报头 | 可信代理报头中的访客地址;可信 CIDR 内的地址永不封禁,也不计入 CC 单 IP 计数 | 同上,只匹配 TCP 对端 |
后两种模式下,「封禁」页顶部显示「集群 {clusters} 的访客 IP 来自代理:内核封禁只匹配 TCP 对端」。直连模式下节点前仍有负载均衡时,把负载均衡器的地址加入放行名单:它们不会被封禁,也不会在内核被丢包。
L4 应用在自己的监听上接受 PROXY 协议,与 HTTP / HTTPS 监听的访客 IP 设置无关。
API
路径位于 /api/v1 下。
| 过程 | 端点 | 说明 |
|---|---|---|
bans.list | GET /bans | 有效的封禁;查询参数 scope(site / platform)、siteId、source(manual / auto)、address(IP 或 CIDR:覆盖它或在它之内的封禁)、page、pageSize |
bans.create | POST /bans | scope 为 site 时必须带 siteId,为 platform 时不能带;另需 cidr、reason、durationSeconds |
bans.delete | DELETE /bans/{id} | 解封手动或自动封禁 |
只读 AccessKey 只能调用 bans.list;服务账号不能调用封禁接口(403 SERVICE_ACCOUNT_FORBIDDEN)。字段与示例见 API 与端点。
故障排查
| 现象 | 原因 | 处理 |
|---|---|---|
| 「IP 地址或 CIDR 无效」 | 不是 IP 地址或 CIDR,或含前导零、zone ID | 改为标准写法 |
「前缀太短,最短为 /16」(或 /48) | 前缀短于下限 | 拆成更长的前缀,或改用 IP 名单 |
| 「封禁时长须在 1 分钟到 7 天之间」 | 到期时间超出范围 | 长期拦截改用 IP 名单 |
| 「封禁范围包含受保护的地址 …」 | 封禁包含节点地址、回环或未指定地址,或与放行名单重叠 | 缩小范围 |
| 「手动封禁已达上限(N 条)」 | 达到手动封禁上限 | 解封不再需要的地址,或调大 防护设置 → 封禁 → 手动封禁上限 |
| 「请选择网站」 | 范围为「网站」但未选择网站 | 选择网站,或把范围改为「全局」 |
| 「封禁不存在或已失效」 | 封禁已到期或已解封 | 刷新列表 |
| 列表显示「N 个节点未生效」 | 节点封禁容量或内存不足 | 调大节点的 --ban-capacity、--ban-dict-mb,或减少封禁 |
| 封禁后仍能访问 | 节点缺少 bans-v1;地址在放行名单中 | 升级节点;检查放行名单 |
| 全局封禁没有在内核生效 | 节点缺少 kernel-ban-v1 | 按内核封禁授予 CAP_NET_ADMIN 并安装 nftables |