mr-addams/nginx-sentinel
GitHub: mr-addams/nginx-sentinel
nginx-sentinel 是一个实时分析nginx日志并自动封禁恶意IP的安全工具,提升Web服务器防护能力。
Stars: 0 | Forks: 0
# nginx-sentinel
[](https://github.com/mr-addams/nginx-sentinel/releases)
[](https://github.com/mr-addams/nginx-sentinel/actions/workflows/release.yml)
[](LICENSE)
[](go.mod)
[](https://github.com/mr-addams/nginx-sentinel/releases)
[](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应用防火墙, 威胁日志, 守护进程, 实时分析, 恶意流量检测, 日志审计, 暴力破解防护, 服务器日志分析, 爬虫检测, 程序破解, 系统集成, 网络安全, 网络攻击检测, 自动化封禁, 蓝队分析, 隐私保护