不能直接在Workerman中exec('iptables -A...'),因需root权限、规则性能差、reload丢包且多进程易冲突;推荐用ipset+iptables组合,Workerman仅操作ipset实现毫秒级封禁。

iptables 和 firewalld 本身不支持“动态”黑名单——它们是静态规则引擎,每次加/删 IP 都要调用命令、触发内核重载。而 Workerman 是 PHP 进程模型的长连接框架,它自己能实时感知恶意请求(如高频登录、异常包),但不能直接操作内核防火墙规则。真正在生产中跑通「Workerman 触发封禁 → 防火墙立即生效」,必须靠中间层衔接。
为什么不能直接在 Workerman 里 exec('iptables -A ...')
看似可行,实则埋雷:
• exec() 调用需要 root 权限,PHP 进程通常以普通用户(如 www-data 或 workerman)运行,sudo 不配密码会暴露权限,配密码又难管理;
• 每次封一个 IP 就追加一条 iptables 规则,1000 个恶意 IP = 1000 条 INPUT 链规则,匹配性能线性下降,iptables -L 查看都卡顿;
• firewall-cmd --direct 同样有优先级、持久化、reload 冲突问题,且 reload 会短暂丢包;
• Workerman 多进程下并发调用同一条 shell 命令,可能规则写乱或漏加。
推荐方案:用 ipset + iptables 组合,Workerman 只操作 ipset
ipset 是专为海量 IP 匹配设计的内核模块,支持哈希查找 O(1),增删 IP 不影响规则链长度,且 iptables 可以一条规则匹配整个集合。
Workerman 只需执行轻量命令(如 ipset add bl_ip 192.168.1.100),完全规避上述问题。
实操要点:
- 先创建集合:
sudo ipset create bl_ip hash:ip maxelem 65536(名字可自定,bl_ip是集合名) - 让 iptables 关联它:
sudo iptables -I INPUT -m set --match-set bl_ip src -j DROP(这条必须在所有 ACCEPT 规则之前) - Workerman 封禁时只需:
shell_exec("ipset add bl_ip {$ip} 2>/dev/null"),无需 sudo(前提是给www-data用户授权) - 授权方式(推荐):
sudo visudo加一行:www-data ALL=(root) NOPASSWD: /usr/sbin/ipset - 解封同理:
ipset del bl_ip {$ip},不需 reload,秒生效
firewalld 下怎么对接 Workerman?别硬套 direct 规则
firewalld 的设计哲学是「区域+服务」,不是为逐 IP 管理优化的。强行用 firewall-cmd --direct 插入上百条 REJECT 规则,等于放弃 firewalld 的优势,还比不过裸 iptables + ipset。
正确做法是:停用 firewalld,改用 iptables + ipset 组合,并确保 iptables-services 已启用:
• sudo systemctl stop firewalld && sudo systemctl disable firewalld
• sudo yum install iptables-services(CentOS/RHEL)或 sudo apt install iptables-persistent(Debian/Ubuntu)
• 启动 iptables 服务并保存当前规则:sudo systemctl enable iptables && sudo service iptables save
这样 Workerman 操作 ipset,底层仍是 firewalld 兼容的 netfilter,但控制权回到你手上。
Workerman 封禁逻辑里最容易漏的三件事
• 没做 IP 标准化:客户端可能是代理,$_SERVER['REMOTE_ADDR'] 是 CDN 或 Nginx 的地址,得从 X-Forwarded-For 提取,还要防伪造(需 Nginx 用 real_ip_header 配置可信源);
• 没设过期时间:恶意 IP 封 10 分钟就够了,ipset 支持 timeout:ipset create bl_ip hash:ip timeout 600,之后 ipset add bl_ip 1.2.3.4 timeout 600;
• 没做并发去重:同一 IP 在多 Worker 进程里被同时检测到,可能重复 ipset add 报错("Element is already added"),加个 @ 抑制或捕获 warning 即可,不影响结果。


















