Skip to content

自托管运维

在内部服务器、Kubernetes 或 Docker 上运行 CVE Radar 的团队,需要超出默认单用户浏览器工作流的生产能力。本章记录同一 MIT 代码库中的 生产向功能:结构化 audit 日志、密钥文件挂载、RBAC、PostgreSQL 多租户、Prometheus 指标、离线镜像与 Kubernetes 栈发现。

无 enterprise 层级或许可证门控。 环境变量仅切换 运维 行为。日常排障见 运维;基础 env 见 配置

flowchart TB
  classDef ui fill:#e9edf5,stroke:#00baba,color:#253343
  classDef api fill:#f3fcfc,stroke:#008c8c,color:#253343
  classDef data fill:#fff7ed,stroke:#eda232,color:#253343
  classDef ext fill:#f5f5f5,stroke:#666,color:#253343

  subgraph deploy["自托管部署"]
    Browser[浏览器 SPA]:::ui
    API[Express API]:::api
    PG[(可选 PostgreSQL)]:::data
    Metrics["GET /metrics"]:::api
    Audit[stdout audit JSON]:::ext
  end

  Browser --> API
  API --> PG
  API --> Metrics
  API --> Audit
  API --> Feeds[NVD / OSV / 镜像]:::ext
  API --> Notify[Slack / SMTP / webhook]:::ext

图示可选 Postgres、Prometheus 抓取 /metrics、stdout audit 与 watch 后的出站通知。airgap 安装用本地镜像替代公网 feed(见 airgap 部署)。

Audit 日志

CVE Radar 可在 stdout 输出 每个 audit 事件一行 JSON,供 ELK、Loki、Splunk 或 Docker json-file 采集。

action 记录时机
scan 每次 POST /api/scan 之后
watch 每次 POST /api/watch 之后
health AUDIT_HEALTH=true 且 detailed health
export 尚无服务端(仅浏览器导出)
{
  "audit": true,
  "ts": "2026-06-06T14:30:00.000Z",
  "action": "scan",
  "ip": "10.0.0.42",
  "stack": ["redis", "nginx"],
  "duration_ms": 8420,
  "sources_failed": ["NVD"],
  "result_count": 12,
  "mode": "full"
}

反向代理后设置 TRUST_PROXY_HOPS。不记录 CVE 标题或 API 密钥。

docker logs cve-radar 2>&1 | grep '"audit":true'

密钥与生产环境

直接 env 文件 env (*_FILE)
NVD_API_KEY NVD_API_KEY_FILE
GITHUB_TOKEN GITHUB_TOKEN_FILE
DEEPL_API_KEY DEEPL_API_KEY_FILE
ALERT_WEBHOOK_URL ALERT_WEBHOOK_URL_FILE
NOTIFICATION_SLACK_WEBHOOK_URL
NOTIFICATION_DISCORD_WEBHOOK_URL
NOTIFICATION_TELEGRAM_BOT_TOKEN
NOTIFICATION_SMTP_PASS 通过 Compose env file 挂载
API_SECRET API_SECRET_FILE

示例:docker-compose.secrets.example.yml。Kubernetes 建议 Secret 文件挂载。

RBAC 与 API 认证

设置 API_SECRET 后,除 GET /api/healthGET /api/v1/health 外,所有 /api/*X-Api-Key 或 Bearer。API_ROLE

角色 权限
admin 设置、tenant 栈 CRUD、scan/watch、translate、meta 与 history
scanner scan/watch/validate、translate、meta 与 history
viewer 只读 meta 与 history — 不可 scan
auditor history/trends 与 meta — 不可 scan

默认 adminviewer 调用 POST /api/scan 返回 403 { "code": "FORBIDDEN" }

Watch 通知

自托管运维常需 watch 发现新 CVE 时的 带外告警POST /api/watch 返回非空 newVulns 后,CVE Radar 异步 派发服务端通知,HTTP 响应不变。

通道 主要 env
Slack NOTIFICATION_SLACK_WEBHOOK_URL 或 legacy ALERT_WEBHOOK_URL
Discord NOTIFICATION_DISCORD_WEBHOOK_URL
Telegram NOTIFICATION_TELEGRAM_BOT_TOKEN, NOTIFICATION_TELEGRAM_CHAT_ID
SMTP 邮件 NOTIFICATION_SMTP_HOST, FROM, TO;可选 PORT, USER, PASS
通用 webhook NOTIFICATION_WEBHOOK_URL
控制项 env 默认
最低严重度 NOTIFICATION_MIN_SEVERITY / ALERT_MIN_SEVERITY HIGH
去重窗口 (ms) NOTIFICATION_DEDUP_MS 900000
Slack 格式 ALERT_WEBHOOK_FORMAT slack / generic

legacy ALERT_WEBHOOK_URL 仍经 NotificationService 工作。NOTIFICATION_* 支持 *_FILE 挂载。验证:GET /api/health?detailed=truealerts.webhookConfigured。详见 告警NOTIFICATIONS.md

多租户 (PostgreSQL)

DATABASE_URL=postgres://cve_radar:cve_radar@127.0.0.1:5432/cve_radar

首次连接 pool 时运行 migration。server/db/schema.tsDrizzle ORM)用于渐进式 ORM 迁移;tenant/stack 与 scan-history 的运行时查询通过 getDb() 使用 Drizzle(共享 pg pool)。

X-Tenant-Id: arvancloud-sre
Method Path 说明
POST /api/v1/tenants 创建 tenant
GET /api/v1/tenants/stacks 列出栈
POST /api/v1/tenants/stacks 创建栈
GET /api/v1/tenants/stacks/:id 按 UUID 获取
PUT /api/v1/tenants/stacks/:id 更新
DELETE /api/v1/tenants/stacks/:id 删除
GET /api/v1/scans/history 租户 history
GET /api/v1/scans/trends 租户 trends

history 按 tenant_id 过滤。legacy /api/* 写入 default tenant。

Prometheus 与 Grafana

GET /metricsMETRICS_ENABLED(默认 true),METRICS_PROTECT 需认证。

指标 类型 说明
cve_radar_scans_total counter scan/watch
cve_radar_vulns_found gauge 最近成功 scan
cve_radar_scan_duration_seconds histogram 处理时长
cve_radar_source_reachable gauge 上游可达
cve_radar_cache_entries_total gauge 缓存大小

仪表盘:docs/self-hosted/grafana/cve-radar-dashboard.json

sum(rate(cve_radar_scans_total{status="success"}[5m]))
  / sum(rate(cve_radar_scans_total[5m]))

airgap 部署

AIRGAPPED=true
NVD_MIRROR_URL=http://internal-mirror/nvd
KEV_MIRROR_URL=http://internal-mirror/kev/catalog.json
# OSV — mirror 或离线 bulk:
OSV_MIRROR_URL=http://internal-mirror/osv
# OSV_BULK_PATH=/data/osv/extracted

设置 OSV_BULK_PATH 时从本地 JSON 树读取 OSV(scripts/sync-osv-bulk.sh / make sync-osv-bulk),不调用 query API。GitHub/RSS/外部翻译 跳过。缺少 NVD/KEV mirror env → fail-closed。curl …/api/health?detailed=true | jq .airgap。参考 scripts/sync-mirrors.shscripts/sync-osv-bulk.sh

Kubernetes 栈发现

K8S_DISCOVERY_ENABLED=true
# K8S_DISCOVERY_NAMESPACES=production,staging

GET /api/v1/discovery/kubernetes — 生产环境需认证。禁用时 503 K8S_DISCOVERY_DISABLED

{
  "enabled": true,
  "images": ["haproxy", "nginx", "redis"],
  "tools": ["HAProxy", "Nginx", "Redis"],
  "unmapped": ["my-sidecar"]
}

ServiceAccount 仅对 deployments 只读。与 租户隔离 组合使用。

快速参考

主题 env
Audit AUDIT_HEALTH, TRUST_PROXY_HOPS
密钥 *_FILE 挂载
RBAC API_SECRET, API_ROLE
通知 NOTIFICATION_*, ALERT_WEBHOOK_URL
Tenant DATABASE_URL, X-Tenant-Id
指标 METRICS_ENABLED, METRICS_PROTECT
Airgap AIRGAPPED, *_MIRROR_URL, OSV_BULK_PATH
K8s K8S_DISCOVERY_ENABLED, K8S_DISCOVERY_NAMESPACES
Enrichment EPSS_ENABLED, COMPLIANCE_ENABLED

维护者副本:docs/self-hosted/

返回首页 · 上一章:运维