بهرهبرداری 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=true → alerts.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=true → database.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:run — docs/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/ (انگلیسی، همتراز این فصل).
بازگشت به خانه · قبلی: بهرهبرداری