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
API_SECRET_PREVIOUS API_SECRET_PREVIOUS_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 و/یا OIDC_ISSUER، همهٔ routeهای /api/* به‌جز GET /api/health و GET /api/v1/health به احراز هویت نیاز دارند: X-Api-Key (نقش از API_ROLE) یا Bearer JWT (نقش از OIDC_ROLE_MAPPING). روی پروسهٔ سرور API_ROLE را برای کلاینت‌های shared-secret یکی از موارد زیر بگذارید:

نقش مجوزها
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 داشته باشد — فقط برای نصب تک‌مستاجری مورد اعتماد.

بستهٔ ممیزی امنیت

برای بررسی‌های سازمانی و GRC (#259):

سند کاربرد
docs/security/THREAT_MODEL.md مدل تهدید STRIDE-lite
docs/security/DEPENDENCY_AUDIT.md رویهٔ ممیزی وابستگی‌ها (npm audit / Trivy)
docs/self-hosted/SECRETS.md چرخش بدون قطعی API_SECRET + API_SECRET_PREVIOUS
SECURITY.md OIDC و SAML از طریق federation به OIDC (بدون SP بومی؛ ADR 007)

هنگام چرخش، مقدار جدید را در API_SECRET بگذارید و مقدار قدیم را تا به‌روزرسانی همهٔ کلاینت‌ها در API_SECRET_PREVIOUS نگه دارید.

اعلان‌های 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 دسترسی ندارد.

اگر X-Tenant-Id فرستاده شود ولی slug نامعتبر باشد، API 400 TENANT_SLUG_INVALID برمی‌گرداند — به default برنمی‌گردد.

چک‌لیست چندمستاجری

قبل از production مشترک، هر مورد را بررسی کنید:

موضوع رفتار
جداسازی داده SQL محدود به tenant با WHERE tenant_id = … (پشته، history، prefs اعلان).
اعتبارسنجی header slug معتبر وقتی header هست؛ slug ناشناخته → 404 TENANT_NOT_FOUND.
slugهای رزرو default, system, admin, root, postgres, public برای ایجاد tenant ممنوع (409 TENANT_SLUG_RESERVED).
Auth / RBAC API_SECRET و OIDC اختیاری؛ routeهای admin tenant نقش admin می‌خواهند.
محدودیت نرخ سطل scan/watch به ازای IP + tenant در v1.
Audit خطوط audit شامل tenant؛ GET /api/v1/audit محدود به tenant جاری.
اعلان‌ها prefs و webhook هر tenant با X-Tenant-Id؛ dedup شامل slug tenant.
Routeهای legacy /api/* بدون نسخه history را زیر default می‌نویسد.
Health GET /api/health?detailed=truedatabase.multiTenant؛ capabilities → features.multiTenant.

Docker Compose با PostgreSQL: docker compose --profile postgres up -d و DATABASE_URL را تنظیم کنید (ر.ک. TENANTS.md).

متریک 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_mirror_age_days gauge mirror سن آینهٔ offline
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
# MIRROR_MANIFEST_PATH=/data/mirrors/.mirror-manifest.json
# MIRROR_STALE_DAYS=7

با OSV_BULK_PATH، جست‌وجوی بستهٔ OSV از درخت JSON محلی (npm run mirrors:sync / 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.mirrorHealth'

نوار کناری وقتی سن آینه از MIRROR_STALE_DAYS (پیش‌فرض ۷) بیشتر باشد هشدار آینهٔ کهنه نشان می‌دهد. Prometheus: cve_radar_mirror_age_days.

همگام‌سازی آینه (zone اینترنت)

npm run mirrors:sync (یا make mirrors-sync) KEV، صفحات NVD 2.0 (MIRROR_NVD_DAYS) و bulk OSV را با manifest و SHA256 دانلود می‌کند. manifest را با MIRROR_MANIFEST_PATH روی میزبان airgap تنظیم کنید. RPO/RTO و اندازهٔ دیسک: docs/self-hosted/AIRGAP.md.

پرچم اثر
MIRROR_SKIP_NVD=true فقط KEV + OSV
MIRROR_SKIP_OSV=true فقط NVD + KEV
MIRROR_SKIP_KEV=true فقط NVD + OSV

CI نمونه‌های JSON آینه را با schemas/airgap/*.json اعتبارسنجی می‌کند (npm run check:airgap-schemas).

نمودار Helm

نمودار رسمی: charts/cve-radar/. Deployment، Service، Ingress اختیاری، Redis، Postgres اختیاری، PVC برای /app/data و mount Secret با envهای *_FILE (همان الگوی Secrets).

helm install cve-radar ./charts/cve-radar -n cve-radar --create-namespace

راهنما: docs/self-hosted/HELM.md.

Kubernetes Operator (CVEScanStack)

Operator نسخهٔ v0 منابع سفارشی CVEScanStack را به CronJob عامل اسکن و watch هم‌تراز می‌کند. گروه CRD: cve-radar.io/v1alpha1؛ manifestها در deploy/k8s/operator/. اجرای درون‌کلاستر: npm run operator:rundocs/self-hosted/OPERATOR.md.

kubectl apply -f deploy/k8s/operator/crd-cvescanstack.yaml
kubectl apply -f deploy/k8s/operator/rbac.yaml
kubectl apply -f deploy/k8s/operator/deployment.yaml

هر CVEScanStack فیلدهای stack، apiKeySecretRef، tenantId اختیاری و زمان‌بندی cron برای import Trivy و watch را تعریف می‌کند. پیگیری: #254.

worker کارهای اسکن (BullMQ)

وقتی REDIS_URL تنظیم شده، worker جدا کنار API اجرا کنید: npm run worker:run (image تولید: node dist-server/server/worker/run.js). API درخواست POST /api/v1/scan/queue را می‌پذیرد؛ کلاینت‌ها GET /api/v1/jobs/:id را poll می‌کنند و نتایج صفحه‌بندی‌شده را از GET /api/v1/jobs/:id/results می‌گیرند. Web UI وقتی /api/capabilities مقدار features.jobQueue: true گزارش کند، به صف + polling می‌رود. بدون Redis، اسکن‌ها همگام می‌مانند (POST /api/v1/scan/stream). #257.

REDIS_URL=redis://127.0.0.1:6379 npm run worker:run

عامل اسکن (Trivy / Grype)

POST /api/v1/scans/import برای JSONهای Trivy/Grype. CLI: npm run agent:scan. نمونه CronJob: deploy/k8s/agent-scan-cronjob.example.yaml. docs/self-hosted/AGENT.md.

کشف پشته 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, MIRROR_MANIFEST_PATH, MIRROR_STALE_DAYS
K8s K8S_DISCOVERY_ENABLED, K8S_DISCOVERY_NAMESPACES
غنی‌سازی EPSS_ENABLED, COMPLIANCE_ENABLED (پیش‌فرض روشن)

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

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