Skip to content

تشغيل self-host

الفرق التي تشغّل CVE Radar على خوادم داخلية أو Kubernetes أو Docker تحتاج أكثر من سير العمل الافتراضي للمتصفّح. يغطي هذا الفصل قدرات الإنتاج ضمن نفس كود MIT: سجلات audit منظمة، mount للأسرار، RBAC، تعدد المستأجرين PostgreSQL، مقاييس Prometheus، مرايا airgap، واكتشاف المكدس في Kubernetes.

لا يوجد مستوى enterprise أو قفل ترخيص. متغيرات البيئة تضبط السلوك التشغيلي فقط. للاستكشاف اليومي راجع التشغيل؛ للبيئة الأساسية الإعداد.

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 / mirrors]:::ext
  API --> Notify[Slack / SMTP / webhooks]:::ext

يُظهر المخطط PostgreSQL الاختياري، وscraping Prometheus على /metrics، وسطور audit على stdout، وقنوات الإشعار بعد watch. في airgap تُستبدل المصادر العامة بمرايا محلية (انظر نشر airgap).

سجلات audit

CVE Radar يمكنه إخراج سطر JSON واحد لكل حدث audit على stdout لـ ELK أو Loki أو Splunk أو driver json-file في Docker.

action متى يُسجَّل
scan بعد كل POST /api/scan (نجاح، فشل جزئي، أو خطأ)
watch بعد كل POST /api/watch
health عند AUDIT_HEALTH=true و GET /api/health?detailed=true
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 خلف reverse proxy. لا تُسجَّل عناوين 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 mount عبر env file في Compose
API_SECRET API_SECRET_FILE
API_SECRET_PREVIOUS API_SECRET_PREVIOUS_FILE

في Kubernetes يُفضَّل mount ملفات Secret. راجع docker-compose.secrets.example.yml.

RBAC ومصادقة API

مع API_SECRET، كل /api/* ما عدا GET /api/health و GET /api/v1/health يتطلب مفتاح API. API_ROLE:

الدور الصلاحيات
admin إعدادات، CRUD مكدسات tenant، scan/watch، translate، meta و history
scanner scan/watch/validate، translate، meta و history
viewer قراءة meta و history — بدون scan
auditor history/trends و meta — بدون scan أو تغيير مكدسات

الافتراضي admin. viewer على POST /api/scan يحصل 403 { "code": "FORBIDDEN" }. اجمع RBAC مع تعدد المستأجرين على منصات مشتركة.

حزمة تدقيق الأمن

لمراجعات المؤسسات وGRC (#259):

المستند الغرض
docs/security/THREAT_MODEL.md نموذج تهديد STRIDE-lite
docs/security/DEPENDENCY_AUDIT.md إجراء تدقيق التبعيات
docs/self-hosted/SECRETS.md تدوير API_SECRET + API_SECRET_PREVIOUS بدون انقطاع
SECURITY.md OIDC وSAML عبر federation إلى OIDC (بدون SP أصلي؛ ADR 007)

إشعارات watch

يحتاج مشغّلو self-host غالباً تنبيهات out-of-band عند اكتشاف CVE جديدة في watch المجدول. CVE Radar يرسل إشعارات الخادم بشكل غير متزامن بعد POST /api/watch مع newVulns غير فارغ — دون تغيير استجابة 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
نافذة dedup (ms) NOTIFICATION_DEDUP_MS 900000
تنسيق Slack ALERT_WEBHOOK_FORMAT slack أو generic

ALERT_WEBHOOK_URL القديم ما زال يعمل عبر NotificationService. NOTIFICATION_* يدعم mounts *_FILE — أو استخدم env من المنصة أو env file غير مُلتزَم. تحقق: GET /api/health?detailed=truealerts.webhookConfigured. راجع التنبيهات و NOTIFICATIONS.md.

تعدد المستأجرين (PostgreSQL)

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

تُشغَّل migrations عند أول اتصال pool. تعريفات schema في server/db/schema.ts (Drizzle ORM) للمهاجرة التدريجية؛ استعلامات runtime لـ tenant/stack وscan-history تستخدم Drizzle عبر getDb() (pool pg المشترك).

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 مكدس بالمعرّف
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 و trends محدودة بـ tenant_id. بدون DATABASE_URL يبقى التطبيق single-tenant.

إذا أُرسل X-Tenant-Id بصيغة غير صالحة، يعيد API 400 TENANT_SLUG_INVALID — ولا يعود صامتاً إلى default.

قائمة تعدد المستأجرين

الموضوع السلوك
عزل البيانات SQL بـ WHERE tenant_id = … للمكدسات والhistory وتفضيلات الإشعار.
التحقق من header slug صالح عند الإرسال؛ slug غير معروف → 404 TENANT_NOT_FOUND.
slugs محجوزة default, system, admin, root, postgres, public ممنوعة عند إنشاء tenant (409 TENANT_SLUG_RESERVED).
Auth / RBAC API_SECRET وOIDC اختياري؛ مسارات admin tenant تتطلب دور admin.
حدود المعدل scan/watch لكل IP + tenant على v1.
Audit سطور audit تتضمن tenant؛ GET /api/v1/audit للمستأجر الحالي فقط.
الإشعارات تفضيلات وwebhooks لكل tenant مع X-Tenant-Id؛ dedup يتضمن slug.
مسارات legacy /api/* بدون إصدار يكتب history تحت default.
Health GET /api/health?detailed=truedatabase.multiTenant؛ capabilities → features.multiTenant.

Docker Compose: docker compose --profile postgres up -d مع DATABASE_URL (راجع TENANTS.md).

Prometheus و Grafana

GET /metrics. METRICS_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 وصول upstream
cve_radar_mirror_age_days gauge mirror
cve_radar_cache_entries_total gauge حجم cache

لوحة Grafana: 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
# 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. GitHub و RSS وترجمة خارجية تُتخطى. env mirror مفقود لـ NVD/KEV → fail-closed.

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

الشريط الجانبي يعرض تحذير mirror قديم عند تجاوز MIRROR_STALE_DAYS (افتراضي 7). Prometheus: cve_radar_mirror_age_days.

مزامنة المرآة (منطقة الإنترنت)

npm run mirrors:sync (أو make mirrors-sync) يحمّل KEV وصفحات NVD 2.0 (MIRROR_NVD_DAYS) و bulk OSV مع manifest و SHA256. اضبط MIRROR_MANIFEST_PATH على المضيف المعزول. 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 chart

المخطط الرسمي: charts/cve-radar/. Deployment و Service و Ingress اختياري و Redis و Postgres اختياري و PVC لـ /app/data و mounts Secret عبر env *_FILE.

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

راجع docs/self-hosted/HELM.md.

Kubernetes Operator (CVEScanStack)

Operator v0 يُوازِن موارد CVEScanStack إلى CronJob للوكيل والمراقبة. مجموعة CRD: cve-radar.io/v1alpha1؛ manifests في deploy/k8s/operator/. التشغيل داخل الكلuster: 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 للاستيراد والمراقبة. #254.

worker مهام المسح (BullMQ)

عند ضبط REDIS_URL، شغّل worker منفصلاً بجانب API: npm run worker:run (صورة الإنتاج: node dist-server/server/worker/run.js). يقبل API POST /api/v1/scan/queue؛ يستطلع العملاء GET /api/v1/jobs/:id ويجلبون النتائج المقسمة من GET /api/v1/jobs/:id/results. تنتقل Web UI إلى قائمة + استطلاع عندما يُبلغ /api/capabilities عن features.jobQueue: true. بدون 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

K8S_DISCOVERY_ENABLED=true
# K8S_DISCOVERY_NAMESPACES=production,staging

GET /api/v1/discovery/kubernetes — يتطلب auth في الإنتاج. عند التعطيل 503 K8S_DISCOVERY_DISABLED.

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

ServiceAccount للقراءة فقط على deployments. اجمع مع عزل tenant.

مرجع سريع

الموضوع env
Audit AUDIT_HEALTH, TRUST_PROXY_HOPS
Secrets mount *_FILE
RBAC API_SECRET, API_ROLE
إشعارات NOTIFICATION_*, ALERT_WEBHOOK_URL
Tenant DATABASE_URL, X-Tenant-Id
Metrics METRICS_ENABLED, METRICS_PROTECT
Airgap AIRGAPPED, *_MIRROR_URL, OSV_BULK_PATH, MIRROR_MANIFEST_PATH, MIRROR_STALE_DAYS
K8s K8S_DISCOVERY_ENABLED, K8S_DISCOVERY_NAMESPACES
Enrichment EPSS_ENABLED, COMPLIANCE_ENABLED

نسخة maintainer: docs/self-hosted/.

الرئيسية · السابق: التشغيل