fabriziosalmi/secure-proxy-manager
GitHub: fabriziosalmi/secure-proxy-manager
一个自托管的 Secure Web Gateway 方案,整合了正向代理、WAF、DNS sinkhole 和 Web UI,为家庭和小型办公网络提供可控的网络出口安全管控。
Stars: 37 | Forks: 4
# Secure Proxy Manager
[](https://github.com/fabriziosalmi/secure-proxy-manager/releases)
[](https://github.com/fabriziosalmi/secure-proxy-manager/actions/workflows/ci.yml)
[](LICENSE)
[](https://github.com/fabriziosalmi/secure-proxy-manager/pkgs/container/secure-proxy-manager-proxy)
[](https://fabriziosalmi.github.io/secure-proxy-manager/)
[](#像攻击者一样测试)
📖 **文档:**
一个 **自托管的 Secure Web Gateway (SWG)** — 为家庭、实验室或小型办公网络提供一个可控的出口点。它将 Squid 正向代理、自定义 **WAF**(通过 ICAP 进行请求/响应检查)、**DNS sinkhole** 以及现代化的 Web UI + API 结合在一个 Docker Compose 技术栈中:它是 Zscaler / Cloudflare Gateway 等云 SWG 的自托管替代品 — **流量绝对不会离开您的网络**。
拦截域名和 IP,执行默认拒绝的出口允许列表,根据 WAF 规则检查请求,在 DNS 层对恶意软件/广告域名进行 sinkhole 处理,并监控每个客户端正在访问的内容。

恶意请求会被 WAF 在代理处以 **403** 拦截;正常流量直接放行。每个类别都经过对抗性测试套件的验证 — 参见 [像攻击者一样测试](#tested-like-an-attacker)。

## 对比
大多数自托管网络工具只覆盖单层。SPM 将正向代理出口控制、请求检查和 DNS sinkholing 结合在一个技术栈中:
| 功能 | Pi-hole / AdGuard | Nginx Proxy Manager | 云 SWG (Zscaler…) | **Secure Proxy Manager** |
|---|:---:|:---:|:---:|:---:|
| DNS sinkhole(在解析时拦截) | ✅ | — | ✅ | ✅ |
| 正向代理(出站出口控制) | — | — | ✅ | ✅ |
| HTTP 请求/请求体检查 (WAF) | — | — | ✅ | ✅ |
| 默认拒绝的出口允许列表 | — | — | ✅ | ✅ |
| 反向代理 / 入口 | — | ✅ | — | — |
| **自托管 — 流量保留在您的网络中** | ✅ | ✅ | — | ✅ |
| **免费 / 无按席位收费** | ✅ | ✅ | — | ✅ |
Pi-hole/AdGuard 仅在 DNS 层进行拦截;Nginx Proxy Manager 是反向代理*入口*;云 SWG 可以完成所有这些工作,但它是作为付费 SaaS 来传输您的流量。SPM 是**自托管的出站**替代品 — 在您自己的硬件上结合了 WAF 检查和 DNS sinkholing 的单一可控出口点。
## 它是什么
该技术栈包含五个容器(外加一个可选的第六个):
| 服务 | 技术 | 角色 |
|---------|------|------|
| `web` | React 19 + Vite,由 nginx 提供服务 | 管理 UI 和 HTTPS 入口点。终结 TLS,代理 API 和 WebSocket。 |
| `backend` | Go (chi, modernc/sqlite, JWT) | REST API、日志摄取、设置、身份验证、后台工作进程。约 20 MB 内存。 |
| `waf` | Go ICAP 服务 | 检查 Squid 通过 ICAP 移交的被代理请求/响应。 |
| `proxy` | Squid (Ubuntu 22.04, `squid-openssl`) | 端口 3128 上的实际正向代理,ICAP 已连接到 WAF。 |
| `dns` | dnsmasq | 在网络层对黑名单域名进行 sinkhole 处理的 DNS 解析器。 |
| `tailscale` | Tailscale(可选) | 用于通过私有网络进行远程访问的 Sidecar。通过 compose profile 启用。 |
后端通过 Docker Engine API 与 Squid 和 dnsmasq 通信,以应用黑名单和配置更改,而无需重启主机。
## 快速开始
要求:Docker 20.10+ 及 Compose v2,约 512 MB 内存,约 2 GB 磁盘空间。支持在 x86_64 和 ARM64 架构上运行。
**一条命令(全新的 VPS 或服务器):**
```
curl -fsSL https://raw.githubusercontent.com/fabriziosalmi/secure-proxy-manager/main/deploy/install.sh | sudo bash
```
安装程序会检查 Docker,生成随机管理员凭据,从 GHCR 拉取预构建的签名镜像,并启动技术栈。它会在最后打印出凭据(它们也会被保存到 `.env` 中)。
**手动方式:**
```
git clone https://github.com/fabriziosalmi/secure-proxy-manager.git
cd secure-proxy-manager
cp .env.example .env
# Set a strong BASIC_AUTH_PASSWORD in .env (the backend refuses to start with an
# empty, common, or <8-char password). Leave SECRET_KEY empty to auto-generate.
docker compose up -d --build
```
然后打开 并接受自签名证书。使用 `.env` 中的凭据(或安装程序打印的凭据)登录。首次登录时,会有一个简短的向导帮助您选择初始配置。
要通过代理发送流量,请将客户端指向 `http://:3128`(“设置” > “客户端设置”会生成针对不同操作系统的说明和 PAC 文件)。
## 端口
| 端口 | 服务 | 说明 |
|------|---------|-------|
| 443, 8443 | Web UI (HTTPS) | 主界面。8443 是备用端口上的相同 UI。 |
| 80, 8011 | HTTP | 重定向到 HTTPS;同时也为 Let's Encrypt 提供 ACME 挑战服务。 |
| 3128 | 代理 | 将客户端指向这里。默认情况下仅允许 RFC1918/localhost 源。 |
| 5001 | 后端 API | 绑定到主机上的 `127.0.0.1`;由 UI 在内部进行代理。 |
## 配置
所有配置均通过 `.env` 进行环境驱动(完整列表及注释请参见 `.env.example`)。核心要素:
| 变量 | 默认值 | 用途 |
|----------|---------|---------|
| `BASIC_AUTH_USERNAME` / `BASIC_AUTH_PASSWORD` | 必填 | 管理员登录。如果密码为空、是常见的默认密码或短于 8 个字符,后端将拒绝启动。 |
| `SECRET_KEY` | 自动生成 | JWT 签名密钥。留空则自动生成并持久化一个密钥。如果手动设置,它必须是唯一、随机的,且长度超过 32 个字符;启动时会拒绝已知/示例值。 |
| `LETSENCRYPT_DOMAIN` / `LETSENCRYPT_EMAIL` | 空 | 设置这两项可通过 certbot 获取真实证书,以替代自签名证书。 |
| `CORS_ALLOWED_ORIGINS` | `https://localhost:8443` | 允许的 UI 源。 |
| `DNS_UPSTREAM_1..3` | 拦截恶意软件的解析器 | dnsmasq 的上游 DNS。可覆盖为您自己的(如 Pi-hole)。 |
| `PROXY_IP` | 空 | 您的局域网 IP,用于为浏览器启用 WPAD 自动发现 (`wpad.dat`)。 |
| `WAF_BLOCK_THRESHOLD` | `10` | 触发请求拦截的异常评分阈值(越低越严格)。 |
| `WAF_FAIL_OPEN` | `0` | 如果 WAF 处理程序出错,是拦截(`0`,fail-closed)还是放行(`1`)该请求。 |
大多数运行时行为(WAF 类别、黑名单、过滤开关、通知)均通过 UI 管理,并存储在数据库中,而非 `.env` 中。
## 功能
**过滤**
- 域名和 IP 黑白名单,可在 UI 中或通过 API 管理,支持通过 URL 或粘贴内容导入(IP 支持 CIDR)。
- 通过 dnsmasq 对黑名单域名进行 DNS sinkholing,因此被拦截的查找请求根本不会到达代理。
- Squid 中的直接 IP 访问拦截和方法白名单。
- 可选的默认拒绝出口(默认关闭):不使用拒绝列表式的黑名单,而是仅允许出站流量访问明确的 CIDR/域名允许列表,并拒绝其他所有访问。在“设置”中切换;目标在“出口允许列表”页面上管理。
**WAF**
- 涵盖 21 个可切换类别(SQLi、XSS、遍历、C2、数据丢失等)的 170 条 regex 规则,每条均可在运行时单独启用/禁用。
- 7 种行为启发式算法(熵、信标、PII、分片、变形、幽灵、序列)以及 DGA 检测(二元语法 + 熵)、域名抢注检测(编辑距离 + 同形字)和安全 URL 快速路径缓存。
- 反规避输入规范化和异常评分;默认情况下遇到内部错误时 fail closed。
**HTTPS 检查(可选)**
- 默认情况下,HTTPS 通过 `CONNECT` 建立隧道 - WAF 只能看到连接元数据(主机、端口),看不到请求内容。
- 在“设置”中启用 SSL-bump 会让 Squid 使用本地生成的 CA 拦截 TLS,以便 WAF 可以检查解密后的请求。这需要在每个应信任它的客户端上安装生成的 CA(“设置” > 下载 CA)。该 CA 会在首次启动时针对每个部署单独生成。
**运维**
- 实时仪表盘、按客户端深入查看、可搜索的访问日志,以及配置更改审计日志。
- 内部网络上的 Prometheus 指标:后端位于 `:5000/metrics`(按路由划分的 RED 指标、数据库连接池指标、工作进程心跳、Go runtime),WAF 位于 `:8080/metrics`(计数器外加 REQMOD 延迟直方图)。可选的 `observability` Compose profile 提供了 Prometheus + Grafana:`docker compose --profile observability up -d`。
- 健康探针:`/livez`(进程运行中)和 `/readyz`(数据库可连接 — 容器健康检查使用此项,因此死锁的数据库将显示为不健康状态)。后端提供结构化的按请求访问日志。
- 向自定义 webhook、Gotify、Telegram 或 Microsoft Teams 发送通知,支持重试和退避机制。
- WebSocket 日志流,带有持久撤销列表的 JWT 身份验证,按 IP 限速,以及静态敏感设置的 AES-256-GCM 加密。
- 可选的 Let's Encrypt、WPAD/PAC 客户端自动配置以及 Tailscale Sidecar。
## 使用方法
**将客户端指向代理。** 在设备上将 HTTP/HTTPS 代理设置为 `http://:3128`,或者使用“设置” > “客户端设置”中的 PAC 文件 / WPAD 自动发现。默认情况下仅允许 RFC1918 和 localhost 源。
**拦截域名。** 黑名单 > 域名 > 添加 `example.com`。更改将在几秒钟内导出到 Squid 和 dnsmasq。白名单优先级更高。
**检查 HTTPS。** 设置 > 启用 SSL 检查,下载生成的 CA,并在您的客户端上将其安装为受信任的证书。如果不这样做,HTTPS 请求将仅根据主机/IP 和 DNS 进行过滤,而不根据 WAF 正文规则进行过滤。
**将出口锁定为允许列表。** 设置 > 启用默认拒绝出口,然后在“出口允许列表”页面上添加批准的 IP/CIDR 和域名。本地客户端随后只能访问这些目标;其他所有访问都将被拒绝。
**导入黑名单。** 黑名单 > 导入支持通过 URL 或粘贴内容导入热门的公共列表。导入有大小限制,并由防 SSRF 客户端抓取,拒绝私有/内部目标。
## 更新
```
cd secure-proxy-manager
git pull
docker compose up -d --build
```
您的 `.env`、数据库和黑名单位于 bind-mounted 数据卷中,在更新期间会予以保留。后端会检查 GitHub 是否有更新的版本,并在有新版本时在 UI 中显示徽章。
## 备份和恢复
数据库 (`data/`)、配置 (`config/`) 和 `.env` 保存了所有状态。
```
docker compose down
cp data/proxy_manager.db proxy_manager.db.bak
tar czf config.bak.tgz config/ .env
docker compose up -d
```
UI 还在“设置”下提供了配置导出/导入功能,并且可以通过 API 导出数据库。
## 健康检查与测试
```
# Service health
curl -skI https://localhost:8443/ # UI
curl -I http://127.0.0.1:5001/health # backend API (localhost only)
# Proxy a request
curl -x http://localhost:3128 -I http://example.com
# End-to-end suite (service health, proxy egress, blocking, log pipeline)
bash tests/ci-e2e.sh
```
Go 单元测试:`cd backend-go && go test ./...` 和 `cd waf-go && go test ./...`。
UI 测试:`cd ui && npm test`。位于 `scripts/pre-commit-validate.sh` 的本地 pre-commit 检查脚本会运行 TypeScript、ESLint、Go vet/test 以及 UI 构建。
## 故障排除
- **后端容器无法启动** - 检查 `docker compose logs backend`。最常见的原因是 `BASIC_AUTH_PASSWORD` 为空/太或 `SECRET_KEY` 是已知/太短;出于设计安全考虑,这两种情况都会被拒绝。
- **无法访问 UI** - 它是在 8443 端口上使用自签名证书的 HTTPS;请接受该证书,或使用 `https://localhost:8443`(而不是 `http://...:8011`)。
- **客户端未被过滤** - 确认设备的代理设置为 `:3128`,并且源 IP 属于 RFC1918/localhost(其他来源将被拒绝)。
- **HTTPS 未被检查** - 这是默认行为;启用 SSL-bump 并安装 CA 以检查请求内容。
## API
后端公开了供 UI 使用的 REST API(77 个路由)。可通过 `GET /api/docs` 获取机器可读的列表。使用 HTTP Basic auth 进行身份验证,或者在 `POST /api/auth/login` 处交换凭据以获取 JWT,并将其作为 Bearer token 发送。
## 架构和部署文档
- [DEPLOYMENT.md](DEPLOYMENT.md) - 分步部署、反向代理和 TLS 设置、资源调优。
- [SECURITY.md](SECURITY.md) - 安全模型以及如何报告问题。
- [BENCHMARKS.md](BENCHMARKS.md) - 可重现的性能和检测基准。
- [CHANGELOG.md](CHANGELOG.md) - 发布历史。
## 像攻击者一样测试
大多数代理和 WAF 只是发布了规则并祈求平安。SPM 拥有一个**对抗性 e2e 测试套件**(`make adversarial`,在 CI 中门控),它会在隔离的沙箱中通过*正在运行的*代理 + WAF 驱动真实的攻击流量,并且**在任何回归时使构建失败**。五个测试维度:
- **block-matrix** — 涵盖 **21 个 WAF 类别**的 SQLi / XSS / RCE / SSRF / 遍历 / 秘密窃取 / 等:恶意 → **403**,正常 → 放行。门控标准:**假阴性 = 0,假阳性 = 0**。
- **API 攻击者** — 身份验证绕过、伪造/篡改的 JWT(包括 `alg=none`)、登录 SQL 注入、速率限制探测 → **零绕过**(按预期返回 401/403/429)。
- **基准测试 / 延迟** — p50/p95 + 代理 + ICAP 开销,带有回归门控。
- **config-matrix** — 通过真实的 API 切换设置,并证明数据平面的变化(禁用某个规则类别 → 该攻击现在可以通过;重新启用 → 再次被拦截,选择性生效)。
- **弹性** — 杀死 WAF,代理将 **fail closed**(没有 fail open 漏洞);重启它,技术栈会**自我修复**。
亲自运行它 — 它会启动沙箱、发起攻击、打印 block-matrix + FP/FN 报告,然后将其拆除:
```
make adversarial
```
参见 [`tests/adversarial/`](tests/adversarial/)。
## 安全说明
- SSL-bump CA 在首次启动时按每个部署单独生成,且从不提交到代码库;请将 `config/` 下的私钥视为敏感信息,切勿共享。
- 设置强有力的管理员凭据。默认情况下,代理仅接受 RFC1918/localhost 客户端 — 如果您公开暴露 3128 端口,请将其置于身份验证或私有网络(如 Tailscale sidecar)之后。
- 敏感设置(webhook URL、token)在静态下进行加密;JWT 密钥和加密密钥生成并持久化保存在 `data/` 下。
- 通过代理镜像中的 fail-closed 版本底线和禁用的 FTP 网关,使 Squid 针对上游 CVE(例如 Squidbleed / CVE-2026-47729)保持修补状态 — 参见 [安全公告](docs/guide/security-advisories.md)。
## 支持
SPM 采用 MIT 许可证,可免费自托管。如果它对您有帮助:
- ⭐ **Star** 该代码库并 [**赞助**](https://github.com/sponsors/fabriziosalmi) 以支持开发。
- 🛠️ **想要为您的网络或组织安装、加固或管理它?** 请发送电子邮件至 **fabrizio.salmi@gmail.com**。
## 许可证
[MIT](LICENSE)。
标签:DNS防污染, Docker, EVTX分析, WAF, 代理服务, 安全网关, 安全防御评估, 日志审计, 版权保护, 网络过滤, 自定义请求头