公司动态

BunkerWeb开源WAF部署实战:基于NGINX和ModSecurity的WAAP防护方案

📅 2026/8/27 20:00:28
BunkerWeb开源WAF部署实战:基于NGINX和ModSecurity的WAAP防护方案
在实际生产系统里真正决定一个 Web 服务能否长期稳定运行的往往不是功能代码有多整洁而是入口层能不能挡住那些未授权探测、扫描器和异常流量。BunkerWeb 是一个基于 NGINX 的开源 Web 应用防火墙WAF同时覆盖了 WAAPWeb Application and API Protection中常见的 API 防护、Bot 识别、限速和规则检测能力。它面向自托管场景兼容 Docker、Docker Swarm 和 Kubernetes也支持通过环境变量、配置文件和 UI 管理多站点策略。这篇文章会围绕一条主线展开先理解 BunkerWeb 在整个访问链路中扮演什么角色再完成部署、接入一个被保护应用、验证规则是否真正生效最后落到日志留存、防御策略和生产落地清单。适合正在做自建 WAF 选型的开发者、负责 Web 应用安全加固的运维人员以及想了解开源 WAF 工作机制的初学者。学习过程中你会看到 Docker Compose、NGINX 配置、ModSecurity 规则、日志排查和合规要求但整篇文章不涉及任何攻击方法所有测试请求都以本地防护验证为目的。1. 为什么开源 WAF 和 WAAP 值得进入生产选型视野1.1 从 WAF 到 WAAP防护维度发生了什么变化传统 WAF 的核心工作是检测 HTTP 请求中是否有 SQL 注入、XSS、命令注入、文件包含等攻击特征并决定放行还是拦截。这个模型在早期 Web 应用中有效但随着 API 接口、移动端、前后端分离架构普及攻击面已经不只是 URL 参数和表单字段。请求体可能是 JSON、XML、Protobuf攻击目标可能是未授权调用、批量爬取、撞库、接口滥用、恶意自动化脚本这些都不能靠一份静态规则完全覆盖。WAAP 是在 WAF 基础上扩展出的安全能力集合通常包括 Web 防护、API 保护、Bot 管理、限速防滥用和应用层 DDoS 缓解。BunkerWeb 之所以被很多项目称为 WAF / WAAP是因为它不只是拦截已知攻击签名还提供了反向代理、自动 HTTPS、自定义安全响应头、IP 黑白名单、请求限速、异常行为封禁和基于 ModSecurity 的规则引擎。把这些能力组合起来才能对真实流量做多层判断而不是在收到请求后只查一次规则表。1.2 BunkerWeb 在开源 WAF 中的定位和适合场景BunkerWeb 的定位是自托管的 Web 应用安全网关。它基于 NGINX这意味着它天然具备高性能反向代理、负载均衡、TLS 终止能力而安全模块则围绕 NGINX 的请求处理阶段展开。适合使用 BunkerWeb 的场景主要有三类第一类是中小团队自建服务不想为每一个后端应用单独写安全逻辑希望在一层统一的网关上完成证书、HTTPS、访问控制和恶意请求拦截。第二类是容器化环境团队已经用 Docker Compose 管理服务希望 WAF 也以容器方式加入而不是额外维护一台配置复杂的物理设备。第三类是需要把防护规则和配置纳入版本管理的团队BunkerWeb 支持通过环境变量、配置文件和 API 管理配置便于把安全策略当作代码来维护。不太适合使用 BunkerWeb 的场景是对托管式安全服务有合规强需求、没有人力维护规则更新和安全事件、必须由云厂商提供 SLA 保障的严肃生产环境。开源 WAF 能减少软件授权成本但不可能替你把规则更新、日志分析和应急响应都做完。选型时需要清晰认识到这一点。2. 先理解 BunkerWeb 的核心工作机制2.1 基于 NGINX 的模块化架构BunkerWeb 的核心是 NGINX但它不是简单地在 NGINX 前面加一个容器而是把安全组件嵌入到 NGINX 的请求处理流程里。请求进入后会依次经过虚拟主机匹配、TLS 终止、日志记录、访问控制、规则检测、限速、Bot 判断最后转发到上游应用或静态文件。这种架构带来的直接好处是性能损耗可控。NGINX 本身处理高并发连接的能力已经很强安全逻辑尽量在 NGINX 模块和 Lua 脚本层完成避免为每个请求额外启动独立进程。另一个好处是配置表达能力更强基于 NGINX server 块可以做到每个域名、每个路径应用不同的安全策略。理解这条链路之后排查问题也会更快。如果测试请求返回的是 NGINX 错误页说明请求可能卡在连接、TLS 或上游路由阶段如果返回 403、406说明安全模块参与并拦截了如果请求已经到达后端但行为异常则需要检查后端应用自己的业务逻辑。2.2 请求处理链路和插件机制一个典型请求在 BunkerWeb 中的处理顺序可以概括为终止 TLS 连接解析 HTTPS 证书。根据 Host 匹配虚拟主机配置。记录访问日志。检查 IP 黑名单、用户代理黑名单、地域或 ASN 黑名单。执行 ModSecurity 规则检查。执行路径级限速和并发限制。执行机器人检测和自定义 Lua 逻辑。根据结果将请求转发给上游应用或返回内置页面。插件机制让 BunkerWeb 不只是固定规则集合。它支持接入自定义规则、Lua 脚本以及配置文件覆盖。实际项目中可以把业务特有的防护逻辑写成独立规则文件比如限制某个内部接口只允许指定网段访问或对上传接口单独设置 body 大小限制。2.3 核心功能模块速查功能类别主要作用典型配置方向自动 HTTPS申请和续期 TLS 证书支持 Lets Encrypt域名、邮箱、证书持久化目录反向代理将请求转发给后端应用代理 URL、上游地址、WebSocket 支持ModSecurity基于规则检测 Web 攻击特征规则集、日志级别、拦截与仅告警模式IP 黑名单/白名单控制来源地址访问权限允许网段、拒绝网段请求限速防止暴力破解和滥用路径、速率、突发大小Bot 检测识别恶意爬虫和自动化工具User-Agent、行为特征、自定义规则安全响应头降低浏览器侧攻击风险HSTS、CSP、X-Content-Type-Options 等日志和审计记录请求、拦截和运营日志日志格式、存储位置、集中采集需要注意表中每一类功能在不同版本里的配置变量名可能不同。正式落地前要以 BunkerWeb 官方文档的配置说明为准尤其是从旧版本升级时环境变量和配置文件格式可能发生变化。3. 部署环境准备和最小化安装3.1 环境要求和版本确认BunkerWeb 最常见的部署方式是基于 Docker 容器。学习环境只需要一台能运行 Docker 的 Linux 机器内存建议 1GB 以上磁盘预留 5GB 以上用于镜像、证书、规则和日志。生产环境则需要更多考虑固定版本镜像、证书持久化、日志集中存储、告警通知以及至少两个节点或可回滚的发布方式。开始之前先确认一下环境uname -a docker --version docker compose version如果 Docker 命令不存在需要先安装 Docker Engine 和 Docker Compose 插件。不同发行版安装方式有差异这里不展开。版本确认很关键。不要看到官网写最新版就直接在生产拉取 latest 标签。镜像标签会随版本演进变化建议访问 GitHub Releases 或 Docker Hub 查看当前稳定版本号并在 compose 文件里锁定具体版本。这样后续规则说明、配置项和部署步骤才有对照基础。3.2 使用 Docker Compose 快速启动为了快速跑通先用最简配置启动一个 BunkerWeb 容器。下面是一个适合学习环境的 compose 示例生产环境需要按实际域名、证书方式和持久化要求调整。version: 3.8 services: bunkerweb: image: bunkerity/bunkerweb:latest restart: unless-stopped ports: - 80:8080 - 443:8443 environment: SERVER_NAME: app.example.test MULTISITE: no AUTO_LETS_ENCRYPT: no USE_MODSECURITY: yes USE_BLACKLIST: yes USE_LIMIT_REQ: yes LOG_LEVEL: info volumes: - ./bw-data:/data - ./bw-config:/etc/bunkerweb/configs这个示例里有几个关键点。SERVER_NAME定义站点域名学习环境可以使用自定义域名或 localhostAUTO_LETS_ENCRYPT在本地测试时关闭因为本地没有公网域名和可达的 80 端口开启会导致证书申请失败USE_MODSECURITY开启规则检测USE_BLACKLIST开启黑名单能力USE_LIMIT_REQ开启限速。数据目录和配置目录通过 volume 挂到宿主机避免容器重建后丢失。把内容保存为docker-compose.yml后执行docker compose up -d docker compose ps如果看到bunkerweb状态为Up说明容器已经启动。但这只代表容器进程存在不代表安全策略已正确生效还需要继续查看日志和做请求验证。3.3 如何确认容器和服务状态查看启动日志是第一个检查动作。日志中通常能看到 NGINX 启动成功、BunkerWeb 加载配置、站点初始化等信息。docker compose logs -f bunkerweb如果日志持续输出错误比如端口被占用、配置文件语法错误、证书目录不可写需要先解决问题再继续。此时不要急着进入下一步否则后续所有验证结果都可能不可信。确认容器健康后用 curl 验证 HTTP 是否正常响应curl -I http://localhost如果看到HTTP/1.1 200 OK或重定向响应说明 BunkerWeb 已经接管了 80 端口。如果看到拒绝连接再检查端口映射ss -lntp | grep -E :80|:443这里特别要提醒不要只验证程序能启动还要验证输入、输出、异常分支和日志是否符合预期。容器是 Up 状态但请求无法到达的情况很常见端口映射、Host 匹配、证书配置都可能成为断点。4. 一个可复现的完整接入案例代理并保护一个 Web 应用4.1 场景设计本地 Nginx 应用如何被保护为了把 BunkerWeb 放进真实链路这里设计一个最小场景用一个 Nginx 容器模拟后端 Web 应用再让 BunkerWeb 作为前置网关接收请求把流量转发给后端应用。这个拓扑在很多团队里很常见上层是 WAF下层是业务服务。网络结构上两个容器放在同一个 Docker 网络里。BunkerWeb 将宿主机 80 端口收到的请求转发给webapp:80并在转发过程中执行安全规则。这样后端应用不需要改代码也不需要自己处理证书、黑名单和恶意检测。下面的配置用于说明接入思路实际项目要根据自己的域名、包名和版本调整。所有示例都基于学习环境线上部署需要额外考虑日志、权限、监控、回滚和异常处理。4.2 编写 docker-compose.yml 和配置文件创建项目目录并准备一个简单的后端页面mkdir -p bw-demo/demo-site echo h1Protected Demo Application/h1 bw-demo/demo-site/index.html在bw-demo目录下创建docker-compose.ymlversion: 3.8 services: webapp: image: nginx:alpine container_name: demo-webapp restart: unless-stopped volumes: - ./demo-site:/usr/share/nginx/html:ro networks: - bw-net bunkerweb: image: bunkerity/bunkerweb:latest restart: unless-stopped depends_on: - webapp ports: - 80:8080 - 443:8443 environment: SERVER_NAME: app.example.test MULTISITE: no AUTO_LETS_ENCRYPT: no USE_MODSECURITY: yes USE_BLACKLIST: yes USE_LIMIT_REQ: yes USE_REVERSE_PROXY: yes REVERSE_PROXY_URL: / REVERSE_PROXY_HOST: http://webapp:80 volumes: - ./bw-data:/data - ./bw-config:/etc/bunkerweb/configs networks: - bw-net networks: bw-net: driver: bridge启动服务docker compose up -d docker compose ps正常状态下两个容器应该都在运行。此时访问 BunkerWeb 的 80 端口请求会经过 WAF 被转发到webapp。4.3 关键配置项说明SERVER_NAME指定当前站点域名。如果你的浏览器访问的是http://localhost但SERVER_NAME配置成了app.example.test某些场景下 Host 不匹配可能导致请求无法正确路由。学习环境可以把SERVER_NAME改成localhost或者在本机 hosts 文件里把app.example.test映射到127.0.0.1。USE_REVERSE_PROXY开启反向代理功能REVERSE_PROXY_URL定义代理路径REVERSE_PROXY_HOST指定上游地址。这里http://webapp:80是 Docker 网络内的服务名不是宿主机localhost。USE_MODSECURITY与规则检测直接相关。仅在环境变量中开启还不够还需要确认规则集存在、日志能显示拦截结果。建议后续验证时先开启确认能拦截后再根据业务调优。environment: SERVER_NAME: localhost USE_REVERSE_PROXY: yes REVERSE_PROXY_URL: / REVERSE_PROXY_HOST: http://webapp:80如果你只需要保护部分路径比如只代理/api可以把REVERSE_PROXY_URL设为/api。也可以为不同路径设置不同的限速和访问策略这在实际项目里比单一全局配置更合理。5. 运行验证从访问日志到规则拦截测试5.1 验证 HTTPS 和反向代理是否生效先验证反向代理链路是否打通curl -I http://localhost/正常响应应该来自 Nginx 容器通常包含Server: nginx/...头并且页面内容是Protected Demo Application。如果返回 502 Bad Gateway说明 BunkerWeb 无法连接到上游webapp:80需要检查两个容器是否在同一网络、depends_on是否生效、上游服务是否启动。再验证 HTTPS。本地没有 Lets Encrypt 证书时可以关闭自动证书并容忍自签名证书警告或者用-k跳过证书校验curl -kI https://localhost/如果 HTTPS 正常响应说明 8443 端口的 TLS 终止已经工作。生产环境应当配置合法证书并启用 HSTS避免用户首次访问时遭遇中间人风险。5.2 用测试请求验证拦截效果WAF 是否真正工作核心测试是看恶意特征请求是否被拦截。下面的请求只在本地测试环境执行目的是确认 BunkerWeb 的规则检测链路已经生效。curl -k -s -o /dev/null -w %{http_code}\n \ -H User-Agent: sqlmap \ https://localhost/ curl -k -s -o /dev/null -w %{http_code}\n \ https://localhost/?id1 OR 11如果返回 403、406 或 400说明请求被安全模块拒绝。如果返回 200说明规则可能没有命中需要进一步检查 ModSecurity 配置、规则集是否加载、以及日志中是否有对应告警。这里要特别注意测试时不要用真实业务域名发起扫描也不要在未授权环境下做同样的验证。WAF 测试应当在自己控制的环境或被授权评估的系统中进行。5.3 查看日志与指标拦截结果最终会落到日志里。查看 BunkerWeb 容器日志docker compose logs -f bunkerweb如果需要看更详细的访问日志可以进入容器查看 NGINX 日志目录。不同镜版本日志路径可能不同常见的查找方式如下docker compose exec bunkerweb sh -c ls /var/log docker compose exec bunkerweb sh -c tail -n 50 /var/log/nginx/access.log实际使用时建议先查看容器内日志目录结构再根据文件路径调整命令。日志里如果出现大量403和规则编号说明 WAF 正在拦截请求。此时可以把这些日志接入集中日志系统如 Loki、ELK 或云平台日志服务便于后续审计和告警。6. 常见问题与排查链路6.1 容器启动失败和证书申请失败现象一docker compose up -d后容器反复重启。常见原因是宿主机端口被占用或配置目录没有写权限。检查方式ss -lntp | grep -E :80|:443 ls -ld bw-data bw-config如果端口被其他进程占用要么停掉占用进程要么修改左侧宿主机端口映射。如果 volume 目录权限不对为目录配置正确的 UID/GID 或用 Dockerfile 调整镜像内用户权限。现象二开启了AUTO_LETS_ENCRYPTyes但证书申请失败。常见原因是域名没有解析到当前机器、80 端口不可从公网访问、邮箱无效、以及 Lets Encrypt 请求频率限制。排查时先确认域名解析dig short app.example.com curl -I http://app.example.com证书申请必须要求域名的 80 端口能返回 200 验证响应所以不要只检查 443。如果解析和访问都正常再查看容器日志中的 certbot 输出定位具体失败原因。6.2 ModSecurity 规则不生效很多人的第一反应是“规则没加载”但实际上更常见的是上下文判断错了。ModSecurity 检测的是请求体和固定攻击模式如果请求还没进入规则阶段就被 IP 黑名单拦截那么返回 403 是因为黑名单而不是 ModSecurity。判断方式是看日志中的封锁原因和规则编号。如果确认是 ModSecurity 没有命中需要检查以下几点USE_MODSECURITY是否设置为yes。规则集文件是否挂载到正确目录。是否启用了只记录不拦截的模式。请求的 Content-Type 是否被正确解析。请求特征是否超出当前规则模式。一个常见的坑是只修改了环境变量但没有重新创建容器。Compose 更新环境变量后必须执行docker compose up -d --force-recreate bunkerweb不重建容器时环境变量不会自动生效。6.3 误拦真实用户和 API 请求WAF 上线后最常见的运维问题不是拦不住攻击而是误伤正常用户。误拦可能来自 IP 黑名单命中、User-Agent 命中、限速阈值过低或规则过于激进。排查顺序如下先从日志中找到被拦截请求的源 IP、User-Agent、请求路径和拦截原因。判断封锁来自哪个模块黑名单、ModSecurity、限速还是 Bot 检测。如果是 IP 被误封执行临时解封并检查该 IP 是否为共享出口 IP。如果是 UA 被误判比如内部监控脚本或企业代理需要加入白名单。如果是限速触发检查LIMIT_REQ_RATE是否适合业务峰值。这里建议把规则调整分为“观测模式”和“拦截模式”。新规则先记录日志、观察误报率再逐步切换为拦截。不要把未经验证的规则直接一刀切上线。问题现象常见原因检查方式处理建议容器反复重启端口占用、目录权限ss -lntp、查看容器日志调整端口映射或目录权限证书申请失败域名未解析、80 不可达、频率限制dig、curl -I http://domain修复解析和端口等待频率恢复ModSecurity 不拦截规则未加载、只记录模式查看日志规则编号开启规则并重新创建容器误封正常用户共享 IP、UA 白名单缺失、限速过低查看日志拦截原因增加白名单、调大阈值上游 502容器不在同一网络、上游未启动docker compose ps修正网络和依赖关系7. 防御绕过的思路、合规留存和自研对比7.1 从防御视角看待“登录框 WAF 绕过”在 CTF 靶场和日常安全测试中登录框常常是绕过重点关注点。原因在于登录页参数复杂、业务逻辑多、开发者经常为登录接口写自定义规则而自定义规则往往只覆盖了少数参数名。攻击者会通过路径大小写、参数污染、编码方式变化、Content-Type 不一致等方式避开简单签名。对于防御方来说更重要的是理解为什么会被绕过而不是收藏一两个漏洞利用字符串。要减少规则被绕过的可能建议做到以下几点开启 ModSecurity 的完整规则集并持续更新规则版本。在 WAF 层统一做请求解码确保 URL 编码、HTML 实体、JSON 字段和 Base64 内容都经过检测。对登录、注册、找回密码等接口单独设置更严格的限速和 body 限制。不要只依赖 WAF 规则拦截攻击应用层必须使用参数化查询和输入校验。使用“默认拒绝”策略保护敏感路径而不是只拦截已知特征。对异常但未命中规则的请求记录完整原始数据用于事后分析。注意不要把 WAF 当成 Web 应用防护的全部。正确顺序是先保证应用自身没有高危漏洞再用 WAF 补足统一策略、应急封禁和合规审计能力。7.2 等保 2.0 对 WAF 日志留存的要求在合规场景下WAF 不只是防护设备也是日志审计节点。等级保护 2.0 标准通常要求安全设备启用日志审计功能对网络访问、安全事件和管理员操作进行记录日志留存时间应满足监管和行业要求。常见要求是日志留存不少于六个月具体天数以当地主管部门要求和系统定级结果为准不能只凭一篇博客就当成最终标准。落地时建议至少覆盖四类日志日志类型记录内容留存建议访问日志客户端 IP、时间、请求方法、URL、状态码六个月以上攻击日志规则编号、命中原因、原始请求、拦截结果六个月以上管理员操作日志配置修改、规则变更、用户登录六个月以上系统运行日志容器启停、证书续期、异常重启六个月以上为了满足审计要求日志需要集中存储防止容器销毁后日志丢失。同时建议开启 NTP 时间同步保证日志时间可信并配置只追加或不可篡改的方式来避免审计失效。7.3 与雷池、云 WAF 和 Go 自研 WAF 的对比很多团队在选型时会同时考虑中文开源社区常见的 WAF 方案比如雷池 SafeLine也会对比阿里云 WAF 这类托管服务还有人会考虑用 Go 自研一个轻量 WAF。选择哪一种取决于团队运维能力、业务体量和规则维护成本。方案部署方式规则能力成本适合场景BunkerWeb自托管容器支持 Docker/K8sModSecurity、自定义规则、Lua软件免费运维自担有自建能力想统一容器环境防护雷池 SafeLine自托管中文社区生态较好内置规则、可视化配置软件免费运维自担中文团队希望降低配置门槛阿里云 WAF托管式云服务云原生规则、DDoS 联动按版本和流量计费云上业务需快速接入和服务保障Go 自研 WAF完全自研中间件高度自定义开发成本高业务协议特殊规则标准化困难如果团队没有安全规则运营经验自建开源 WAF 需要额外准备规则更新、误报调优和应急响应的能力。托管云 WAF 在接入便捷性上有明显优势但存在平台绑定和费用问题。Go 自研的思路可以用于特定业务比如在内网网关上实现一套轻量限流和协议过滤但不要轻易认为自研比成熟 WAF 更安全。用 Go 自研一个 WAF 类组件时通常从下面几个模块开始// 伪代码示例表达自研 WAF 的模块划分 type WAF struct { blacklist *IPBlacklist limiter *RateLimiter ruleEngine *RuleEngine upstream *httputil.ReverseProxy } func (w *WAF) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if w.blacklist.Contains(req.RemoteAddr) { http.Error(rw, forbidden, http.StatusForbidden) return } if !w.limiter.Allow(req.RemoteAddr) { http.Error(rw, too many requests, http.StatusTooManyRequests) return } if w.ruleEngine.Match(req) { // 记录日志并拦截 http.Error(rw, forbidden, http.StatusForbidden) return } w.upstream.ServeHTTP(rw, req) }这个示例展示了自研 WAF 的基本模块IP 黑名单、限流器、规则引擎和反向代理。实际开发还要考虑并发安全、正则性能、日志采集、配置热更新和自身安全加固。如果只是为了做一个简单的网关防护完全可以先使用 BunkerWeb 这类成熟方案把自研成本留到确实需要的时候。7.4 生产落地检查清单上线前按以下清单逐项确认能减少大部分安全事故和运维返工镜像使用固定版本号不使用latest无锁版本。证书和配置目录已经持久化容器重建不丢失。域名解析正确80 和 443 端口已对外开放且只受理预期流量。HTTPS 已启用并配置 HSTS关闭弱加密协议。ModSecurity 已开启规则集版本已确认并定期更新。访问日志、攻击日志、管理操作日志已集中存储并满足留存要求。对管理后台、API 内部接口等敏感路径配置了 IP 白名单或额外限速。规则上线先设为记录模式观察误报率后逐步切换为拦截。已确认出现误封时如何临时解封、如何批量调整规则。已确认 WAF 自身出现故障时是否有旁路或降级方案。已确认应用层仍保留参数化查询、输入校验、最小权限等基本安全措施。注意开源 WAF 可以显著降低审计成本和初始部署门槛但它不是一键安全方案。真正让防护有效果的是持续更新的规则、稳定可靠的日志体系以及团队处理误报和告警的操作流程。BunkerWeb 最大的价值在于把 NGINX、自动 HTTPS、ModSecurity、黑名单、限速和 Bot 检测整合到一套可以配置、可容器化部署的方案里。它适合作为 Web 服务入口的第一道安全关卡也适合让运维团队用较小的成本获得统一的安全策略。对新手来说最好的练习方式是在本地用两个容器把反向代理链路搭起来先跑通访问再做规则拦截验证最后把日志接入集中平台。真正上手之后你就能从“容器里跑着一个 WAF 镜像”进阶到“知道每个请求经过哪些检查、为什么被放行或被拦截”这也是安全网关运维里最有价值的能力。