mr-addams/nginx-sentinel

GitHub: mr-addams/nginx-sentinel

nginx-sentinel 是一个实时分析nginx日志并自动封禁恶意IP的安全工具,提升Web服务器防护能力。

Stars: 0 | Forks: 0

# nginx-sentinel [![版本发布](https://img.shields.io/github/v/release/mr-addams/nginx-sentinel?include_prereleases&label=release)](https://github.com/mr-addams/nginx-sentinel/releases) [![构建状态](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/98c237a920015539.svg)](https://github.com/mr-addams/nginx-sentinel/actions/workflows/release.yml) [![许可证](https://img.shields.io/badge/license-GPL--3.0-blue)](LICENSE) [![Go](https://img.shields.io/badge/Go-1.26-00ADD8?logo=go)](go.mod) [![平台支持](https://img.shields.io/badge/linux-amd64%20%7C%20arm64-lightgrey?logo=linux)](https://github.com/mr-addams/nginx-sentinel/releases) [![安装包](https://img.shields.io/badge/packages-deb%20%7C%20rpm%20%7C%20pacman-blue)](https://github.com/mr-addams/nginx-sentinel/releases) 实时分析 nginx access.log 的守护进程。它跟踪每个 IP 的行为,通过 7 个检测器累计分数,并将可疑 IP 写入威胁日志 —— Fail2Ban 读取该日志并封禁攻击者。 ``` nginx access.log → TailReader → whitelist → tracker → scorer → threats.log → Fail2Ban → iptables ``` ## 功能特性 - **7 个检测器:** 探针扫描、频率异常、可疑 User-Agent、暴力破解(404 比例)、顺序爬虫、无资源请求的机器人、URL 溢出 / WAF 绕过 - **机器人 DNS 验证:** 通过 rDNS/fDNS 验证 Googlebot、Bingbot、Yandex、DuckDuckGo 等 —— 合法的爬虫永远不会被封禁 - **白名单:** IP、CIDR、UA 子串 —— 可配置的排除列表 - **线性分数衰减:** 分数在 `observation_window` 窗口期内衰减,不会因为旧流量导致误封 - **SIGHUP 热重载:** 无需重启守护进程即可重建配置、评分器和白名单 - **优雅关闭:** 收到 SIGTERM 信号时,行缓冲区会被清空 - **Systemd + logrotate + Fail2Ban:** 包含开箱即用的部署配置 ## 系统要求 - 支持 systemd 的 Linux x86_64 或 arm64 系统 - Fail2Ban - nginx 日志格式中包含 `$real_ip`(或使用 `$remote_addr` 的标准组合格式) ## 安装说明 ### Debian / Ubuntu — 推荐 从[发布页面](https://github.com/mr-addams/nginx-sentinel/releases)下载适合您架构的 `.deb` 包并进行安装: ``` # amd64 sudo apt install ./nginx-sentinel__linux_amd64.deb # arm64 sudo apt install ./nginx-sentinel__linux_arm64.deb ``` `apt install` 会自动解决依赖关系(`fail2ban`),安装 systemd 单元文件、Fail2Ban 过滤器/监狱、logrotate 配置,并创建 `nginx-sentinel` 系统用户。 安装完成后,编辑配置并启动服务: ``` sudo nano /etc/nginx-sentinel/config.yaml sudo systemctl enable --now nginx-sentinel ``` ### Fedora / RHEL / AlmaLinux / Rocky Linux 从[发布页面](https://github.com/mr-addams/nginx-sentinel/releases)下载适合您架构的 `.rpm` 包并进行安装: ``` # amd64 sudo dnf install ./nginx-sentinel__linux_amd64.rpm # arm64 sudo dnf install ./nginx-sentinel__linux_arm64.rpm ``` `dnf install` 会解决依赖关系,将 systemd 单元文件安装到 `/usr/lib/systemd/system/`,安装 Fail2Ban 过滤器/监狱、logrotate 配置,并创建 `nginx-sentinel` 系统用户。 安装完成后,编辑配置并启动服务: ``` sudo nano /etc/nginx-sentinel/config.yaml sudo systemctl enable --now nginx-sentinel ``` ### Arch Linux / Manjaro 从[发布页面](https://github.com/mr-addams/nginx-sentinel/releases)下载适合您架构的 `.pkg.tar.zst` 包并进行安装: ``` # amd64 sudo pacman -U nginx-sentinel__linux_amd64.pkg.tar.zst # arm64 sudo pacman -U nginx-sentinel__linux_arm64.pkg.tar.zst ``` 该包会将 systemd 单元文件安装到 `/usr/lib/systemd/system/`,安装 Fail2Ban 配置文件、logrotate 配置,并创建 `nginx-sentinel` 系统用户。 安装完成后,编辑配置并启动服务: ``` sudo nano /etc/nginx-sentinel/config.yaml sudo systemctl enable --now nginx-sentinel ``` ### 从源代码构建 需要 Go 1.19+: ``` git clone https://github.com/mr-addams/nginx-sentinel cd nginx-sentinel sudo ./scripts/install.sh sudo systemctl enable --now nginx-sentinel ``` ## 配置说明 配置文件:`/etc/nginx-sentinel/config.yaml`(安装时从 `config.yaml` 创建)。 覆盖路径:`NGINX_SENTINEL_CONFIG=/path/to/config.yaml`。 主要参数: ``` general: log_file: /var/log/nginx/access.log # nginx access.log to watch stats_interval: 300s # STATS output interval to operational log scoring: alert_threshold: 50 # score → WARN in threat log ban_threshold: 80 # score → THREAT + Fail2Ban ban observation_window: 300s # score accumulation / decay window detectors: probe: enabled: true score: 25 paths: [/.env, /.git/config, /wp-config.php, ...] # probe path list rate: enabled: true threshold: 100 # requests per window window: 60s score: 25 useragent: enabled: true scanner_score: 40 # Nuclei, sqlmap, Nikto grabber_score: 20 # wget, HTTrack automation_score: 15 # python-requests, aiohttp empty_ua_score: 30 bruteforce: enabled: true min_requests: 10 ratio_threshold: 0.6 # >60% of responses are 404 score: 30 crawler: enabled: true min_sequential: 5 # /page/1, /page/2, ... N in a row score: 20 noasset: enabled: true min_page_requests: 3 asset_ratio_threshold: 0.1 # <10% of requests go to static assets score: 20 overflow: enabled: true max_url_length: 2048 suspicious_params: [bypass, shell, cmd, exec, eval] score: 30 whitelist: fake_bot_score: 35 # penalty for a bot UA that fails DNS verification dns_verify_timeout: 2s # DNS verification timeout per pipeline request custom: ips: [127.0.0.1] cidrs: [10.0.0.0/8] ua_substrings: [internal-monitor] output: threat_log: /var/log/nginx-sentinel/threats.log operational_log: /var/log/nginx-sentinel/sentinel.log ``` ## 检测器 | 检测器 | 触发条件 | 默认分数 | |----------|---------|---------------| | **probe** | 访问 .env, .git, wp-config.php 等路径 | 每次请求 25 分 | | **rate** | 60 秒内请求数 > 100 | 25 分 | | **useragent** | 扫描器 / 抓取器 / 自动化工具 / 空 UA | 15–40 分 | | **bruteforce** | 请求 ≥10 次且 >60% 的响应为 404 | 30 分 | | **crawler** | ≥5 个顺序数字 URL(如 /page/1..N) | 20 分 | | **noasset** | 请求 ≥3 个页面且访问静态资源的比例 <10% | 20 分 | | **overflow** | URL 长度 >2048 字符或包含 WAF 绕过关键词 | 30 分 | 分数在 `observation_window` 窗口期内累计并线性衰减。达到 `alert_threshold` 将写入 WARN;达到 `ban_threshold` 将写入 THREAT 并触发 Fail2Ban。 ## 白名单 白名单告诉 nginx-sentinel:“这些是友好的 —— 完全跳过它们。” 有两种独立的机制:**自动机器人验证**(搜索引擎)和**自定义排除**(您自己的 IP、子网、工具)。 ### 自动机器人验证(Googlebot、Bingbot、Yandex 等) nginx-sentinel 知道所有主要搜索引擎机器人的 User-Agent 字符串。当机器人到达时,它会执行 DNS 检查以确认机器人是真实的: 1. 对 IP 进行反向 DNS 查询 → 获得主机名(例如 `crawl-66-249-66-1.googlebot.com`) 2. 对该主机名进行正向 DNS 查询 → 必须解析回同一个 IP 3. 主机名必须以 Google 的已知域名之一结尾(`.googlebot.com`、`.google.com`) 如果两项检查都通过 → 机器人是合法的 → 跳过,不加分。 如果检查失败 → UA 声称是 Googlebot 但 IP 不是 Google 的 → 将添加 `fake_bot_score` 惩罚(默认 35 分)。 内置已验证的机器人:Google、Bing、Yandex、DuckDuckBot、百度、Apple、GPTBot、ClaudeBot 等 —— 详见 `config.yaml` 中的 `whitelist.bots` 部分。 **无需配置** —— 验证是自动进行的。DNS 结果被缓存(`dns_cache.positive_ttl: 24h`),因此不会拖慢处理速度。 ### 自定义白名单 将您自己的 IP、子网和工具添加到 `whitelist.custom` 部分: ``` whitelist: custom: ips: [] cidrs: [] ua_substrings: [] ``` 与任何条目匹配的请求都会在**任何检测器运行之前**被跳过 —— 永远不会添加任何分数。 #### `ips` — 特定 IP 地址 列出应该永远不被检查的单个 IP 地址。 ``` whitelist: custom: ips: - "192.168.1.50" # office workstation - "10.99.99.1" # internal monitoring server - "203.0.113.42" # your home IP ``` 用于:您自己的服务器、已知的合作伙伴、办公机器、开发人员的笔记本电脑。 #### `cidrs` — IP 范围(子网) CIDR 是一种简洁的 IP 地址范围表示法。与其列出数百个 IP,不如写一行。 如何解读:`192.168.1.0/24` 表示“从 `192.168.1.0` 到 `192.168.1.255` 的所有地址” —— 一个包含 256 个地址的完整块。`/` 后面的数字表示该块中有多少个地址: | 表示法 | 范围 | 地址数量 | |----------|-------|-----------| | `10.0.0.1/32` | 恰好 `10.0.0.1` | 1(单个 IP) | | `192.168.1.0/24` | `192.168.1.0` – `192.168.1.255` | 256 | | `10.0.0.0/8` | `10.0.0.0` – `10.255.255.255` | ~1600 万 | ``` whitelist: custom: cidrs: - "192.168.0.0/16" # entire office/VPN network - "10.0.0.0/8" # all private 10.x.x.x addresses - "172.16.0.0/12" # Docker / internal network ``` 用于:办公网络、VPN 子网、Cloudflare IP 范围、CDN 边缘节点、您的托管集群。 #### `ua_substrings` — User-Agent 子串 如果请求的 User-Agent 标头中任何位置包含此字符串,则该请求被加入白名单。匹配是**不区分大小写**的。 ``` whitelist: custom: ua_substrings: - "UptimeRobot" # uptime monitoring service - "internal-healthcheck" # your own monitoring script - "MySEOCrawler/" # your SEO tool - "Screaming Frog SEO" # Screaming Frog crawler - "Ahrefs" # Ahrefs bot - "Semrush" # SEMrush crawler ``` **子串就足够了** —— 您不需要完整的字符串。`"Ahrefs"` 将匹配 `"AhrefsBot/7.0"` 以及任何未来的版本。 ### 应用更改 所有白名单更改都**无需重启守护进程**即可应用 —— 发送重载信号: ``` # 重新加载配置(白名单、探测器、阈值 — 除日志文件路径外的所有内容) systemctl kill -s HUP nginx-sentinel # 或通过 PID 文件 kill -HUP $(cat /var/run/nginx-sentinel.pid) ``` 更改将在几秒钟内生效。操作日志将显示: ``` [CONFIG] reloaded: whitelist updated ``` ### 完整示例 ``` whitelist: fake_bot_score: 35 # penalty for fake Googlebot/Bingbot impersonation dns_verify_timeout: "2s" # DNS verification timeout custom: ips: - "203.0.113.42" # developer's home IP - "10.99.99.1" # Zabbix monitoring cidrs: - "192.168.0.0/16" # office and VPN - "10.0.0.0/8" # private network ua_substrings: - "UptimeRobot" - "Screaming Frog SEO Spider" - "AhrefsBot" - "SemrushBot" - "MJ12bot" # Majestic crawler ``` ## 架构 ``` nginx access.log │ TailReader (inotify, logrotate-aware) │ lines chan (buffered, size LinesBufSize) │ whitelist.Matcher ──→ custom IP/CIDR/UA? → skip │ whitelist.Verifier ──→ bot UA? → rDNS/fDNS → verified? → skip │ → fake bot? → +FakeBotScore tracker.Update(*IPState) ├── TotalRequests, Requests404 ├── pathBuf (ring buffer, last 64 paths) └── sliding window rate counters │ scorer.Evaluate(ipState, entry) ├── decay accumulated score ├── run 7 detectors └── determine verdict (score → level) │ output.ThreatLogger ──→ threats.log ──→ Fail2Ban ──→ iptables ban └──→ sentinel.log (operational) ``` 后台 goroutine: - **TailReader** — 通过 fsnotify 监视文件,处理 mv/copytruncate 日志轮转 - **GC** — 每隔 `gc_interval`(默认 60 秒)移除不活跃的 IP - **Stats** — 每隔 `stats_interval` 打印 `STATS processed/tracked/threats/suspicious` - **SIGHUP 监听器** — 将信号转换为通道事件供主循环使用 ## 日志 **操作日志**(`/var/log/nginx-sentinel/sentinel.log`)—— 守护进程的工作日志: ``` 2026-04-02 14:33:10 [STARTUP] nginx-sentinel v0.1 started 2026-04-02 14:33:12 [THREAT] 45.134.26.8 score=85 modules=probe,rate reason="..." 2026-04-02 14:38:10 [STATS] processed=14320 tracked=87 threats=3 suspicious=12 ``` 标签:`STARTUP`, `SHUTDOWN`, `CONFIG`, `THREAT`, `WHITELIST`, `STATS`, `GC`, `ERROR`, `WARN`。 调试标签(`PARSER`, `TAIL`, `DETECTOR`, `SCORER`)仅在 `logging.debug: true` 时可见。 **威胁日志**(`/var/log/nginx-sentinel/threats.log`)—— 由 Fail2Ban 读取: ``` 2026-04-02T14:33:12Z THREAT 45.134.26.8 score=85 modules=probe,rate reason="probe:/.env,rate:142rps" 2026-04-02T14:35:01Z WARN 92.63.104.12 score=55 modules=useragent reason="ua:Nuclei/3.1.0" ``` Fail2Ban 的 failregex:`THREAT score=\d+`(文件 `deploy/fail2ban/filter.d/nginx-sentinel.conf`)。 ## 管理 ``` # 状态和日志 systemctl status nginx-sentinel journalctl -u nginx-sentinel -f # 无需重启重新加载配置(SIGHUP) kill -HUP $(cat /var/run/nginx-sentinel.pid) # 或 systemctl kill -s HUP nginx-sentinel # 停止(优雅 — 清空行缓冲区) systemctl stop nginx-sentinel # 通过 Fail2Ban 手动封禁/解封 fail2ban-client status nginx-sentinel fail2ban-client set nginx-sentinel unbanip 1.2.3.4 ``` **SIGHUP 更新的内容:** 评分器(检测器 + 阈值)、白名单匹配器、调试/颜色标志、日志文件路径。 **SIGHUP 不会更新的内容:** 跟踪器(IP 状态)、DNS 缓存、TailReader(access.log 路径需要重启)。 ## 位于反向代理(如 Cloudflare)之后 如果 nginx 位于 Cloudflare 之后,日志中的 `$remote_addr` 将是 Cloudflare 的 IP,而不是真实客户端的 IP。这样 nginx-sentinel 会对 Cloudflare 的地址进行评分 → Fail2Ban 会封禁 Cloudflare → 所有人的网站都会宕机。 **解决方案:`ngx_http_realip_module`** 生成包含 Cloudflare IP 范围的 nginx 配置并包含它: ``` # 生成并保存配置 sudo scripts/update-cloudflare-ips.sh /etc/nginx/cloudflare-real-ip.conf ``` 添加到 `nginx.conf`: ``` http { include /etc/nginx/cloudflare-real-ip.conf; ... } ``` 然后 nginx 会在写入日志之前,用真实客户端 IP(来自 `CF-Connecting-IP` 标头)替换 `$remote_addr` —— nginx-sentinel 无需任何更改即可工作。 **自动更新 IP 范围**(Cloudflare 会定期更新它们): ``` # 添加到 cron — 每周一 03:00 0 3 * * 1 /path/to/update-cloudflare-ips.sh /etc/nginx/cloudflare-real-ip.conf && nginx -t && nginx -s reload ``` ## 故障排除 **守护进程启动失败 — 威胁日志错误:** 检查 `/var/log/nginx-sentinel/` 的权限 —— 该目录必须由 `nginx-sentinel` 用户拥有。 **Fail2Ban 未封禁 — 检查日志格式:** ``` fail2ban-regex /var/log/nginx-sentinel/threats.log /etc/fail2ban/filter.d/nginx-sentinel.conf ``` **误报过多 — 降低灵敏度:** 降低配置中的 `score` 或提高阈值(`threshold`、`ratio_threshold`),然后执行 `kill -HUP`。 **调试管道 — 启用调试模式:** ``` logging: debug: true ``` 重启或执行 `kill -HUP`。操作日志将为每个请求显示 `[PARSER]`、`[DETECTOR]`、`[SCORER]` 行。 **内存使用过高:** 减少 `state.max_tracked_ips`(默认 100000;每个 IP ≈ 2.5 KB → 10 万 ≈ 250 MB)。 [俄语文档 → README.ru.md](README.ru.md)
标签:AppImage, Clean Code, EVTX分析, Fail2Ban集成, Go语言, IP行为分析, Linux系统安全, nginx安全, WAF绕过检测, Web安全, Web应用防火墙, 威胁日志, 守护进程, 实时分析, 恶意流量检测, 日志审计, 暴力破解防护, 服务器日志分析, 爬虫检测, 程序破解, 系统集成, 网络安全, 网络攻击检测, 自动化封禁, 蓝队分析, 隐私保护