Zonora
正在加载
Zonora
正在加载
Zonora
正在加载
面向个人安全学习的独立隔离靶场首页,聚焦漏洞挖掘、考核进度和沉浸式练习入口。

面向多用户并发漏洞挖掘实战考核的动态靶场平台,支持 10 人同时在线,每人独立容器 + 独立 PostgreSQL 数据库,自动重置、心跳保活、超时释放。
┌──────────────────────────────────────────────────────────────────┐
│ Nginx (80/443, SSL) │
│ 反向代理 → Portal :4000 │
└───────────────────────────┬──────────────────────────────────────┘
│
┌──────▼──────┐
│ Portal │ Slot 管理 + Cookie 路由
│ :4000 │ WebSocket 支持
└──┬──────┬───┘
│ Cookie: range_slot / range_token
┌────────────▼────────────────────────────────┐
│ Slot 反代层 │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │App-1 │ │App-2 │ ... │App-10│ │
│ │:3000 │ │:3000 │ │:3000 │ │
│ └──┬───┘ └──┬───┘ └──┬───┘ │
│ │ │ │ │
│ ┌──▼───┐ ┌──▼───┐ ┌──▼───┐ │
│ │slot_1│ │slot_2│ ... │slot_10│ │
│ └──────┘ └──────┘ └──────┘ │
└──────────────────────────────────────────────┘
▲
┌─────────────┴─────────────┐
│ PostgreSQL + Redis │
│ (cqupt_template 模板库) │
└───────────────────────────┘靶场 = Portal 服务 + 10 个 App Slot + 模板数据库机制
漏洞平台(漏洞提交/审核/积分/证书)运行在每个 Slot 容器内,随 Slot 重置而恢复初始状态。
用户点击"进入靶场"时的完整流程:
enter 请求 → 查空闲 Slot → 从模板重建 DB → 重启容器 → 就绪检测 → 返回 Cookie → 反代| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 查找空闲 Slot | 遍历 slots 数组,找 status=free 的 Slot |
| 2 | 断开旧连接 | pg_terminate_backend() 终止目标 DB 的活跃连接 |
| 3 | 重建数据库 | DROP DATABASE + CREATE DATABASE ... TEMPLATE cqupt_template |
| 4 | 重启容器 | Docker API 调用 containers/{name}/restart |
| 5 | 就绪检测 | 轮询 app-N:3000/api/system/status,超时 35 秒 |
| 6 | 返回 Cookie | 设置 range_slot、range_token、range_info 三个 Cookie |
| 7 | 反代请求 | 后续所有非 /_range/ 请求根据 Cookie 路由到对应 App |
IP 限速:/_range/enter 接口,同一 IP 5 分钟最多 3 次,防刷。
Cookie 机制:
| Cookie | 内容 | 属性 | 用途 |
|---|---|---|---|
range_slot | Slot ID(1-10) | HttpOnly, SameSite=Lax | 标识用户分配到哪个 Slot |
range_token | 随机 Token | HttpOnly, SameSite=Lax | 会话认证 |
range_info | 访客序号 + 并发数 | 可读(无 HttpOnly) | 前端水印展示 |
超时策略:
| 机制 | 阈值 | 行为 |
|---|---|---|
| 会话最大时长 | 120 分钟 | Slot 释放,容器重启 |
| 心跳超时 | 300 秒(无心跳) | Slot 释放,容器重启 |
心跳由前端每 60 秒发送一次 /_range/heartbeat。Portal 每 15 秒扫描一次释放超时 Slot。每个 Slot = 独立 Docker 容器 + 独立 PostgreSQL 数据库:
cqupt-app-1 到 cqupt-app-10 容器,互不干扰cqupt_slot_1 到 cqupt_slot_10 库,从 cqupt_template 模板克隆DROP + CREATE DATABASE TEMPLATE 重建,确保环境干净pg_terminate_backend() 断开旧连接,防止数据交叉| 状态 | 说明 | 转换 |
|---|---|---|
free | 空闲,可分配 | 初始化 / 释放后 / 重置失败后 |
resetting | 正在重置 DB + 重启容器 | 进入时 |
occupied | 已被用户占用 | 重置完成后 |
free(释放) | 用户退出 / 心跳超时 / 会话超时 | occupied → 自动或手动触发 |
释放流程:清除 Cookie → 异步重启容器 → Slot 回到 free 状态。容器重启不阻塞响应。
Portal 提供独立的前端入口页面(/),包含:
range_info Cookie 中的序号用于页面水印展示cquptsrc.asia).env 配置文件bash init-range.sh your@email.com初始化流程(6 步):
| 步骤 | 内容 | 说明 |
|---|---|---|
| 1 | 构建镜像 | 编译 app-1(所有 Slot 共享镜像)和 Portal |
| 2 | 启动基础服务 | PostgreSQL + Redis |
| 3 | 创建模板库 | 创建 cqupt_template,执行 Prisma 迁移 + Seed |
| 4 | 克隆 Slot 库 | 从模板创建 10 个 cqupt_slot_N |
| 5 | 申请 SSL | Certbot Webroot 方式申请证书 |
| 6 | 启动全部 | 含 10 个 App Slot、Nginx、Certbot 续期 |
./deploy.sh start # 启动所有服务
./deploy.sh stop # 停止所有服务
./deploy.sh restart # 重启所有服务
./deploy.sh logs # 实时查看日志
./deploy.sh status # 查看服务状态 + 资源使用
./deploy.sh init # 重新执行迁移 + Seed(模板库)
./deploy.sh backup # 备份 PostgreSQL + Redis
./deploy.sh clean # 清理数据卷(删除所有数据库和 Redis 数据)
./deploy.sh rebuild # 重建镜像
./deploy.sh shell # 进入应用容器
./deploy.sh db-shell # 进入数据库容器| 变量 | 默认值 | 说明 |
|---|---|---|
MAX_SLOTS | 10 | 最大并发 Slot 数量 |
SESSION_TIMEOUT_MIN | 120 | 会话最大时长(分钟) |
HEARTBEAT_TIMEOUT_SEC | 300 | 心跳超时时间(秒) |
DB_HOST | postgres | PostgreSQL 主机名 |
DB_PORT | 5432 | PostgreSQL 端口 |
DB_USER | postgres | PostgreSQL 用户名 |
DB_PASSWORD | postgres123 | PostgreSQL 密码 |
REDIS_PASSWORD | redis123 | Redis 密码 |
JWT_SECRET | — | JWT 签名密钥 |
REDIS_URL | — | Redis 连接串 |
SMTP_HOST/PORT/USER/PASS/FROM | — | 邮件发送配置(用于漏洞平台) |
GEMINI_API_KEY | — | Google Gemini API 密钥(用于漏洞平台) |
NODE_ENV | production | 运行环境 |
PORT | 3000 | App 服务端口 |
| 服务 | 镜像 | 端口 | 说明 |
|---|---|---|---|
postgres | postgres:16-alpine | 5432 | 数据库(含 cqupt_template 模板库) |
redis | redis:7-alpine | 6379 | 缓存 + 邮件验证码存储 |
portal | 自定义 | 4000 | Slot 管理 + 反向代理 + WebSocket |
app-1 ~ app-10 | 自定义 | 3000 | 漏洞平台实例(10 个,共享镜像) |
nginx | nginx:alpine | 80/443 | SSL + 反向代理入口 |
certbot | certbot/certbot | — | SSL 证书自动续期 |
/_range/*)#| 方法 | 路径 | 说明 | 鉴权 |
|---|---|---|---|
| GET | /_range/status | Slot 状态统计(空闲/占用/重置数、总访客数) | 无 |
| POST | /_range/enter | 进入靶场(分配 Slot + 重建 DB + 重启容器) | 无 |
| POST | /_range/heartbeat | 心跳保活(更新最后活跃时间) | Cookie |
| POST | /_range/exit | 退出靶场(释放 Slot) | Cookie |
`/_range/enter` 响应:
{
"ok": true,
"visitorNumber": 1,
"occupied": 1
}同时 Set-Cookie 包含 range_slot、range_token、range_info 三个 Cookie。
`/_range/heartbeat` 响应:
{
"ok": true,
"remainingSec": 7200
}通过 Portal 反代后的所有 /api/* 请求路由到对应 Slot 的 App。漏洞平台所有 API 端点(认证、漏洞 CRUD、商城、证书、排行榜等)在 Slot 内独立运行,与 Slot 绑定。
# 查看当前 Slot 占用情况
curl http://<host>:4000/_range/status
# 返回示例:
{
"occupied": 3,
"resetting": 0,
"total": 10,
"available": 7,
"totalVisitors": 42
}./deploy.sh logs # 所有服务日志
docker logs cqupt-range-portal # Portal 日志(含 Slot 重置记录)
docker logs cqupt-app-1 # Slot 1 日志| 机制 | 说明 |
|---|---|
| 容器隔离 | 10 个独立容器,网络通过 cqupt-network 互通 |
| 数据库隔离 | 每个 Slot 独立数据库,克隆自模板 |
| Cookie 安全 | HttpOnly + SameSite=Lax,防 XSS/CSRF |
| IP 限速 | /_range/enter 5 分钟 3 次 |
| SSL/TLS | Certbot 自动申请 + 续期,TLSv1.2/1.3 仅允许 |
| 连接回收 | 克隆前终止旧连接,防止数据交叉 |
| 会话超时 | 心跳 + 最大时长双重超时控制 |
| 异步重置 | 用户退出后异步重启容器,不阻塞响应 |
├── src/ # 漏洞平台(所有 Slot 共享镜像)
│ ├── server/ # 后端入口 + 业务模块
│ └── views/ # 前端页面(React)
├── portal/ # 靶场 Portal
│ ├── server.mjs # Slot 管理 + 反向代理 + WebSocket
│ └── public/ # Portal 前端(Slot 指示灯 + 进入页面)
├── prisma/ # 数据模型 + 迁移 + Seed
├── docker-compose.yml # 服务编排(16 个服务)
├── nginx.conf # Nginx 反向代理配置
├── init-range.sh # 一键初始化脚本
├── deploy.sh # 日常运维脚本
├── Dockerfile # App 容器镜像
├── portal/Dockerfile # Portal 容器镜像
└── .env.example # 环境变量模板MIT