Skip to content

بهره‌برداری self-host

تیم‌هایی که CVE Radar را روی سرور داخلی، Kubernetes یا Docker اجرا می‌کنند به چیزی فراتر از گردش کار پیش‌فرض مرورگر تک‌کاربره نیاز دارند. این فصل قابلیت‌های آمادهٔ production را که در همان کدبیس MIT عرضه می‌شوند مستند می‌کند: لاگ audit ساخت‌یافته، mount فایل secret، RBAC، چندمستاجری PostgreSQL، متریک Prometheus، mirror آفلاین و کشف پشته در Kubernetes.

سطح enterprise یا قفل مجوزی وجود ندارد. متغیرهای محیط فقط رفتار عملیاتی را تنظیم می‌کنند (airgap، Postgres اختیاری، محافظت متریک). برای عیب‌یابی روز دوم و rate limit به بهره‌برداری و برای 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["استقرار self-host"]
    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 / mirror]:::ext
  API --> Notify[Slack / SMTP / webhook]:::ext

نمودار Postgres اختیاری برای چندمستاجری، scrape Prometheus روی /metrics، خطوط audit روی stdout و کانال‌های اعلان پس از watch را نشان می‌دهد. در نصب airgap، feedهای عمومی با mirror محلی جایگزین می‌شوند (بخش استقرار airgap).

لاگ audit

CVE Radar می‌تواند یک خط JSON به ازای هر رویداد audit روی stdout بنویسد تا ELK، Loki، Splunk یا driver json-file در Docker آن را جذب کند. این مسیر برای تیم امنیت و پلتفرم، ردپای tamper-friendly از trigger اسکن‌ها را بدون لاگ عنوان CVE یا secret فراهم می‌کند.

action زمان ثبت
scan پس از هر POST /api/scan (موفق، شکست جزئی منبع، یا خطای سخت)
watch پس از هر POST /api/watch
health فقط وقتی AUDIT_HEALTH=true و GET /api/health?detailed=true
export هنوز سمت سرور نیست (export فقط در مرورگر)

هر خط یک شیء JSON با "audit": true است:

{
  "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"
}

پشت reverse proxy مقدار TRUST_PROXY_HOPS را تنظیم کنید تا ip کلاینت واقعی باشد (مثل rate limiting). فیلتر لاگ Docker:

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

رویدادهای audit هرگز عنوان آسیب‌پذیری، URL webhook یا کلید API را شامل نمی‌شوند — فقط نام ابزار پشته و شمارش تجمیعی.

secret و کلید production

در توسعه می‌توان از .env محلی استفاده کرد. در production secret را به‌صورت فایل یا sync از vault پلتفرم mount کنید — نه در لایهٔ image و نه در Compose commit‌شده.

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 mount از env file در Compose
API_SECRET API_SECRET_FILE

وقتی *_FILE تنظیم است، سرور مسیر را می‌خواند و قبل از load شدن routeها env مستقیم را پر می‌کند. نمونه Docker Compose: docker-compose.secrets.example.yml.

mkdir -p secrets
printf '%s' 'your-nvd-key' > secrets/nvd_api_key.txt
chmod 600 secrets/*.txt
docker compose -f docker-compose.secrets.example.yml up -d

در Kubernetes، Secret را به‌صورت فایل mount کنید و NVD_API_KEY_FILE=/etc/cve-radar-secrets/nvd_api_key بگذارید. از External Secrets Operator برای sync از Vault استفاده کنید.

RBAC و احراز هویت API

با تنظیم API_SECRET، همهٔ routeهای /api/* به‌جز GET /api/health و GET /api/v1/health به X-Api-Key یا Authorization: Bearer نیاز دارند. روی پروسهٔ سرور API_ROLE را یکی از موارد زیر بگذارید:

نقش مجوزها
admin تنظیمات، CRUD پشتهٔ tenant، scan/watch، translate، meta و history
scanner scan/watch/validate، translate، meta و history
viewer meta، translate، history — بدون اجرای scan
auditor history/trends و meta — بدون scan یا تغییر پشته

پیش‌فرض admin است. viewer با POST /api/scan 403 { "code": "FORBIDDEN" } می‌گیرد. برای پلتفرم مشترک RBAC را با چندمستاجری و network policy ترکیب کنید.

مرورگر می‌تواند VITE_API_KEY build-time مطابق API_SECRET داشته باشد — فقط برای نصب تک‌مستاجری مورد اعتماد.

اعلان‌های watch

اپراتورهای self-host اغلب به هشدار out-of-band وقتی watch زمان‌بندی‌شده CVE جدید پیدا می‌کند نیاز دارند. CVE Radar پس از POST /api/watch با آرایهٔ غیرخالی newVulns، اعلان سمت سرور را ناهم‌زمان ارسال می‌کند — پاسخ HTTP تغییر نمی‌کند و dispatch handler را block نمی‌کند.

کانال 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, NOTIFICATION_SMTP_FROM, NOTIFICATION_SMTP_TO; اختیاری PORT, USER, PASS
webhook عمومی NOTIFICATION_WEBHOOK_URL
کنترل env پیش‌فرض
حداقل severity NOTIFICATION_MIN_SEVERITY یا legacy ALERT_MIN_SEVERITY HIGH
پنجره dedup (ms) NOTIFICATION_DEDUP_MS 900000 (۱۵ دقیقه)
قالب Slack ALERT_WEBHOOK_FORMAT slack یا generic

webhook تک Slack قدیمی (ALERT_WEBHOOK_URL) همچنان کار می‌کند — server/services/alerts.ts به NotificationService واگذار می‌شود. NOTIFICATION_* از mountهای *_FILE پشتیبانی می‌کند (مثل NVD) — یا از env پلتفرم / env file gitignore‌شده استفاده کنید. بررسی: GET /api/health?detailed=truealerts.webhookConfigured. جزئیات: اعلان‌ها و docs/self-hosted/NOTIFICATIONS.md.

چندمستاجری (PostgreSQL)

جداسازی PostgreSQL اختیاری برای deploy مشترک CVE Radar است. بدون DATABASE_URL اپ تک‌مستاجری می‌ماند (فقط localStorage مرورگر).

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

migrationها در اولین اتصال pool اجرا می‌شوند. تعریف schema در server/db/schema.ts (Drizzle ORM) برای مهاجرت تدریجی ORM است؛ queryهای runtime مربوط به tenant/stack و scan-history از Drizzle و getDb() (pool مشترک pg) استفاده می‌کنند. tenant default برای نصب‌های موجود seed می‌شود. slug را در v1 بفرستید:

X-Tenant-Id: arvancloud-sre

بدون header از default استفاده می‌شود. routeهای کلیدی v1:

Method Path توضیح
POST /api/v1/tenants ایجاد tenant { slug, name }
GET /api/v1/tenants/stacks لیست پشته‌های tenant جاری
POST /api/v1/tenants/stacks ایجاد پشته { name, tools, settings? }
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 محدود به tenant
GET /api/v1/scans/trends trends محدود به tenant

routeهای legacy /api/* history را زیر default ذخیره می‌کنند. SQL همیشه WHERE tenant_id = … دارد — tenant A به ردیف tenant B دسترسی ندارد.

متریک Prometheus و Grafana

متریک‌ها در GET /metrics (مسیر root، نه زیر /api) در دسترس‌اند.

متغیر پیش‌فرض توضیح
METRICS_ENABLED true false → 404
METRICS_PROTECT false true → نیاز به API_SECRET روی /metrics
متریک نوع برچسب توضیح
cve_radar_scans_total counter mode, status درخواست scan/watch
cve_radar_vulns_found gauge severity, source از آخرین scan موفق
cve_radar_scan_duration_seconds histogram mode مدت handler
cve_radar_source_reachable gauge source دسترسی upstream
cve_radar_cache_entries_total gauge اندازه cache

برای scrape در cluster، METRICS_PROTECT را خاموش بگذارید و با NetworkPolicy محدود کنید. داشبورد JSON: docs/self-hosted/grafana/cve-radar-dashboard.json.

نمونه PromQL:

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

استقرار airgap

بدون دسترسی خروجی به NVD/OSV/CISA عمومی، با mirror محلی یا پوشهٔ bulk OSV:

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، جست‌وجوی بستهٔ OSV از درخت JSON محلی (scripts/sync-osv-bulk.sh / make sync-osv-bulk) انجام می‌شود و API query OSV فراخوانی نمی‌شود.

با AIRGAPPED=true GitHub Advisories، RSS و ترجمهٔ خارجی رد می‌شوند. env mirror گم‌شده برای NVD/KEV → خطای fail-closed. وضعیت:

curl -s 'http://localhost:3001/api/health?detailed=true' | jq .airgap

برای layout mirror از scripts/sync-mirrors.sh و برای bulk OSV از scripts/sync-osv-bulk.sh در zone sync خود استفاده کنید.

کشف پشته Kubernetes

کشف اختیاری imageهای Deployment و نگاشت به presetها: GET /api/v1/discovery/kubernetes.

K8S_DISCOVERY_ENABLED=true
# K8S_DISCOVERY_NAMESPACES=production,staging
# K8S_KUBECONFIG=/path/to/kubeconfig

در production احراز هویت لازم است (نقش admin یا scanner). وقتی غیرفعال است 503 { "code": "K8S_DISCOVERY_DISABLED" }. نمونه پاسخ:

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

ServiceAccount فقط list/get روی deployments — بدون secrets یا write. با جداسازی tenant روی cluster مشترک ترکیب کنید. وقتی features.k8sDiscovery در /api/capabilities true است، تب Stack می‌تواند ابزارهای کشف‌شده را import کند.

مرجع سریع

موضوع env کلیدی
Audit AUDIT_HEALTH, TRUST_PROXY_HOPS
Secret mount *_FILE
Auth / RBAC API_SECRET, API_ROLE
اعلان NOTIFICATION_*, legacy 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
غنی‌سازی EPSS_ENABLED, COMPLIANCE_ENABLED (پیش‌فرض روشن)

نسخهٔ maintainer این راهنماها در docs/self-hosted/ (انگلیسی، هم‌تراز این فصل).

بازگشت به خانه · قبلی: بهره‌برداری