<div align="center">
# UltraViolet
**自托管网络发现与搜索——你自己的 Shodan,运行在你自己的硬件上。**
TCP/UDP 扫描 · 约 100 种协议探测 · TLS/JARM 指纹 · CVE 匹配 · 全文搜索 · 增量跟踪 · 告警
<br/>
[](service-api/)
[](service-frontend/)
[](service-env/)
[](service-env/)
[](.github/workflows/ci.yml)
[](LICENSE)
[快速开始](#-快速开始) ·
[功能特性](#-功能特性) ·
[架构](#-架构) ·
[文档](#-文档) ·
[生产环境](#-生产环境检查清单)
</div>
---
> **重要提示。** 仅扫描你拥有或已获得**书面授权**的网络。UltraViolet 执行被动服务侦察——它不会利用漏洞。
## 为什么选择 UltraViolet
| 使用场景 | 你能获得什么 |
|----------|--------------|
| 边界与资产盘点 | 跨 CIDR 范围的开放端口与服务持续发现 |
| 基础设施搜索 | 对 HTTP 响应体、横幅、TLS、DNS 和 CVE 进行全文搜索 |
| 风险与合规 | 本地 NVD 匹配,外加 CISA KEV 和 EPSS——无云端依赖 |
| 隔离网络部署 | 离线归档,包含 Docker 镜像、CVE 种子数据和磁盘上的 GeoIP MMDB |
| 变更跟踪 | 扫描之间的增量、WebSocket 事件、保存搜索的告警 |
单租户、一套 Docker Compose 栈、完全掌控你的数据。
## ✨ 功能特性
**发现**
- 以 **masscan** 或 **zmap** 作为发现引擎的 TCP connect 扫描器
- 可配置端口上的 UDP 探测
- 通过 `SCAN_ALLOWED_CIDRS` 限定范围,并带有主机与端口限制
**深度探测(约 100 种协议)**
- Web:HTTP/HTTPS、HTTP/3、GraphQL、favicon 哈希、`robots.txt`、`security.txt`、技术栈
- TLS:证书链、JARM、JA3S/JA4S、配置评级
- 邮件与目录服务:SMTP、POP3/IMAP、LDAP、IPMI
- 数据库与消息队列:MySQL、PostgreSQL、MongoDB、Redis、Kafka、MQTT、NATS、AMQP…
- ICS/SCADA:Modbus、BACnet、DNP3、IEC 104、S7Comm、ENIP、OPC UA…
- IoT 与媒体:ONVIF、RTSP、Chromecast、AirPlay、UPnP…
- 完整列表见[协议文档](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend/docs/probes/service-protocols.md)
**数据丰富**
- 反向 DNS、GeoIP(MMDB)、ASN
- 可选的正向 DNS 与 CT 日志发现
**CVE 与风险**
- 本地 NVD 镜像,支持后台同步与基于指纹的匹配
- CISA KEV 与 FIRST EPSS
**运维**
- RBAC(`viewer` / `operator` / `admin`)、JWT + 刷新令牌
- 扫描计划、暂停/恢复、工作进程重启后的孤儿任务回收
- Prometheus `/metrics`、可选的 Grafana 配置
- 审计日志、速率限制、保留策略
## 🏗 架构
```mermaid
flowchart TB
Browser["Browser"]
FE["service-frontend<br/>React + nginx"]
API["uv-api<br/>REST · WS · metrics"]
PG[("PostgreSQL 16")]
SCAN["uv-scanner<br/>probe pipeline"]
Browser --> FE
FE -->|"/api"| API
FE -->|"/realtime"| API
API <--> PG
SCAN <--> PG
API -.->|LISTEN/NOTIFY| API
```
发布版 UI 镜像将 `/api/` 和 `/realtime` 代理到 `uv-api`——**单一来源**,无需针对每个 API URL 重新构建前端。
| 目录 | 用途 |
|-----------|---------|
| [`service-api/`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-api) | Go:`uv-api`(HTTP API、WebSocket、工作进程)+ `uv-scanner`(流水线) |
| [`service-frontend/`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-frontend) | React 19 + Vite + RTK——扫描、主机、搜索、仪表盘 |
| [`service-documentation-frontend/`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend) | VitePress——用户与运维人员文档 |
| [`service-env/`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-env) | docker-compose、密钥、`install.sh` / `upgrade.sh` / 备份 |
后端开发规则:[`CLAUDE.md`](https://github.com/yakushstanislav/ultraviolet/blob/master/CLAUDE.md)。
## 🚀 快速开始
**环境要求:** Go 1.25+、Docker Engine ≥ 24、约 4 GB 内存。生产镜像面向 Linux **amd64**。
### 从 Docker Hub 运行(无需源码构建)
新手路径——使用
[`service-env/docker-compose.registry.yml`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-env/docker-compose.registry.yml) 拉取已发布镜像:
```bash
cd service-env
cp env.registry.example .env
# 设置 POSTGRES_PASSWORD、AUTH_JWT_SECRET、AUTH_BOOTSTRAP_PASSWORD
mkdir -p geoip catalog-seed
docker compose -f docker-compose.registry.yml pull
docker compose -f docker-compose.registry.yml up -d
# UI → http://localhost:3000
```
参见文档站点中的 [Docker Registry](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend/docs/deployment/docker-registry.md)。
### 从源码开发
API/扫描器镜像会复制 `service-api/bin/` 中预构建的二进制文件(它们**不会**在 Docker 内部编译)。`make dev` 会在 `docker compose … --build` 之前运行 `make -C service-api build-linux`。
```bash
git clone https://github.com/yakushstanislav/UltraViolet.git
cd UltraViolet/service-env
cp .env.example .env
mkdir -p secrets
openssl rand -hex 32 > secrets/postgres_password
openssl rand -hex 32 > secrets/auth_jwt_secret
cd ..
make dev
```
| URL | 用途 |
|-----|---------|
| http://localhost:3000 | UI(通过 nginx `/api/` 访问 API) |
| http://localhost:8080 | 直接访问 API |
| http://localhost:9090/metrics | Prometheus |
**开发引导账号:** `admin` / `admin`(仅在 `APP_ENV≠production` 时有效)。
## 📖 文档
完整指南——VitePress 位于 [`service-documentation-frontend/docs/`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend/docs):
```bash
make docs-dev # → http://localhost:5173
```
在生产环境中,启用 `docs` 配置:
```bash
cd service-env && docker compose --profile docs up -d
# → http://localhost:${UV_DOCUMENTATION_PORT:-3002}
```
关键章节:[安装](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend/docs/getting-started/installation.md) · [扫描](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend/docs/scanning) · [API](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend/docs/api/overview.md) · [部署](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend/docs/deployment/docker-compose.md) · [离线安装](https://github.com/yakushstanislav/ultraviolet/blob/master/service-documentation-frontend/docs/deployment/offline-install.md)。
## 🛠 开发
### 完整 Docker
```bash
make dev # build-linux → compose prod + dev override --build
```
后端代码变更后重新构建 Go 二进制文件(`make -C service-api build-linux`),然后重启或重新运行 `make dev`。
### 混合模式(更快的后端迭代)
```bash
make dev-db # 仅在 :5432 上运行 PostgreSQL
cd service-api && make build
export LOGGER_NAME=uv-api LOGGER_DEBUG=true
export SERVER_ADDR=0.0.0.0 SERVER_PORT=8080
export METRICS_ADDR=0.0.0.0 METRICS_PORT=9090
export REALTIME_ADDR=0.0.0.0 REALTIME_PORT=8081
export POSTGRES_ADDR=localhost POSTGRES_PORT=5432
export POSTGRES_USERNAME=ultraviolet
export POSTGRES_PASSWORD="$(cat ../service-env/secrets/postgres_password)"
export POSTGRES_DATABASE=ultraviolet
export POSTGRES_SCHEMA_PATH="$(pwd)/deploy/migrations"
export AUTH_JWT_SECRET="$(cat ../service-env/secrets/auth_jwt_secret)"
./bin/uv-api
```
前端:`cd service-frontend && npm run dev`(Vite 将 `/api` 代理到 `:8080`,将 `/realtime` 代理到 `:8081`)。
### GeoIP 与 CVE
```bash
cd service-env && make geoip-download # IPLocate MMDB → geoip/
make -C service-env cve-catalog-dump # CVE 数据导出(需要正在运行的 postgres)
```
### 不使用 Docker 构建
```bash
make build && make lint && make frontend-build && make docs-build
```
## 📦 发布与安装
**目标平台:** `linux/amd64`。在 Apple Silicon 上,为离线归档设置 `DOCKER_PLATFORM=linux/amd64`。
```bash
git tag v0.1.0
make release VERSION=v0.1.0
make release-promote VERSION=v0.1.0 # :latest——本地发布必需;GitHub Actions 会自动执行此操作
```
| 归档文件 | 内容 |
|---------|----------|
| `ultraviolet-vX.Y.Z.tar.gz` | compose、脚本、`.env.example`(约 50 KB,在线) |
| `ultraviolet-vX.Y.Z-offline.tar.gz` | + amd64 Docker 镜像 |
| `ultraviolet-vX.Y.Z-offline-full.tar.gz` | + CVE 种子数据 + GeoIP MMDB |
**在线安装:** 解压 → `cp .env.example .env` → `./install.sh`(从 registry 拉取)。
**离线安装:** `./install.sh`(`docker load`,不拉取)。升级:保留 `.env`、`secrets/` 和 Postgres 卷 → `./upgrade.sh`。
变量:`UV_REGISTRY`(默认 `docker.io/styakush`)、`DRY_RUN=1`——本地冒烟构建,不推送。
<details>
<summary><strong>离线安装——分步指南</strong></summary>
```bash
# 在构建机器上
scp dist/ultraviolet-v0.1.0-offline.tar.gz user@host:~/
# 在服务器上(Docker ≥ 24,amd64)
tar xzf ultraviolet-v0.1.0-offline.tar.gz && cd ultraviolet-v0.1.0
cp .env.example .env
./install.sh
```
升级(保留数据):
```bash
docker compose -f docker-compose.yml down
# 备份 .env + secrets/,解压新归档,运行 ./upgrade.sh
```
保持目录名称(`ultraviolet-v0.1.0`)不变——`postgres-data` Docker 卷与其绑定。
</details>
## ✅ 生产环境检查清单
1. 将密钥存储在 `secrets/` 中,切勿提交到 git。
2. 将 `CORS_ALLOWED_ORIGINS` 仅设置为真实的 UI 来源。
3. 配置 `SCAN_ALLOWED_CIDRS` 以及主机/端口限制。
4. 更改引导密码和 `AUTH_JWT_SECRET`(`install.sh` 会拒绝占位符)。
5. 设置 `APP_ENV=production`——uv-api 会拒绝 `admin/admin` 以及少于 8 个字符的密码。
6. 在反向代理处终止 TLS——示例:[`service-env/examples/nginx-tls.conf`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-env/examples/nginx-tls.conf)。
7. 位于代理之后时:设置 `AUDIT_TRUST_PROXY_HEADERS=true`。
8. 备份 PostgreSQL:[`service-env/scripts/backup.sh`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-env/scripts/backup.sh) + cron;`upgrade.sh` 也会创建转储。
9. 指标位于 `:9090/metrics`;通过 `observability` 配置使用 Grafana。
10. 刷新 GeoIP:[`service-env/scripts/geoip-refresh.sh`](https://github.com/yakushstanislav/ultraviolet/blob/master/service-env/scripts/geoip-refresh.sh),每月 cron 执行。
<details>
<summary><strong>HTTPS、子路径部署、备份 cron</strong></summary>
**在外部 nginx 上终止 TLS**——将 UI 仅绑定到回环地址:
```yaml
# docker-compose.override.yml
services:
service-frontend:
ports:
- "127.0.0.1:3000:8080"
```
```bash
CORS_ALLOWED_ORIGINS=https://ultraviolet.example.com
AUDIT_TRUST_PROXY_HEADERS=true
```
**子路径**(`/ultraviolet/`):
```bash
VITE_BASE_PATH=/ultraviolet/ make frontend-build
# compose 中设置 UV_BASE_PATH=/ultraviolet/——必须与 VITE_BASE_PATH 一致
```
**备份 cron**(每日,14 天保留):
```cron
0 3 * * * cd /opt/ultraviolet/service-env && ./scripts/backup.sh && find backups -name 'uv-*.dump' -mtime +14 -delete
```
**GeoIP 刷新**(每月 1 日):
```cron
0 4 1 * * cd /opt/ultraviolet/service-env && ./scripts/geoip-refresh.sh >> /var/log/uv-geoip.log 2>&1
```
</details>
## 🔐 API 与 RBAC
执行 `POST /v1/auth/login` 后,你将获得一个 `access_token`(Bearer)和 `refresh_token`(`/v1/auth/refresh`)。
| 角色 | 权限 |
|------|--------------|
| `viewer` | 读取主机、搜索、CVE、仪表盘 |
| `operator` | + 启动和管理扫描 |
| `admin` | + 用户、审计、设置 |
`GET /v1/me` · `GET /v1/version` · `GET /readyz`(健康检查 + 版本 + 提交)。
## 🤝 贡献
参见 [CONTRIBUTING.md](https://github.com/yakushstanislav/ultraviolet/blob/master/CONTRIBUTING.md)。安全报告:[SECURITY.md](https://github.com/yakushstanislav/ultraviolet/blob/master/SECURITY.md)。
社区规范:[CODE_OF_CONDUCT.md](https://github.com/yakushstanislav/ultraviolet/blob/master/CODE_OF_CONDUCT.md)。
## 📄 许可证
UltraViolet 以 [MIT License](https://github.com/yakushstanislav/ultraviolet/blob/master/LICENSE) 发布。
---
<p align="center">
<sub>UltraViolet——发现你所拥有的,了解发生了什么变化,将其保留在你的网络中。</sub>
</p>