Skip to content

Latest commit

 

History

History
126 lines (99 loc) · 4.07 KB

File metadata and controls

126 lines (99 loc) · 4.07 KB

Secrets management (production)

CVE Radar reads sensitive values from process.env at startup. For development, a local .env file is fine. For production, do not bake secrets into image layers or commit them in Compose files.

Supported keys:

Direct env File 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
API_SECRET API_SECRET_FILE

Watch notification channels (NOTIFICATION_*) do not support *_FILE yet — use platform env or a gitignored Compose env_file. See NOTIFICATIONS.md.

Direct env only Notes
NOTIFICATION_SLACK_WEBHOOK_URL Preferred Slack channel
NOTIFICATION_DISCORD_WEBHOOK_URL Discord incoming webhook
NOTIFICATION_TELEGRAM_BOT_TOKEN Pair with NOTIFICATION_TELEGRAM_CHAT_ID
NOTIFICATION_SMTP_PASS With NOTIFICATION_SMTP_HOST, FROM, TO
NOTIFICATION_WEBHOOK_URL Generic JSON webhook

When *_FILE is set, the server reads the file path (trimmed UTF-8) and hydrates the direct env var before routes load. File wins over an empty direct env; if both exist, the file content is used when *_FILE is present.

Implementation: server/lib/secrets.ts.

Docker Compose secrets

Example: docker-compose.secrets.example.yml

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

Secrets mount under /run/secrets/ inside the container. Never add secrets/*.txt to git (see .gitignore).

Kubernetes / External Secrets

Typical pattern:

  1. Store values in HashiCorp Vault, AWS Secrets Manager, or your platform vault.
  2. Use External Secrets Operator to sync into a Kubernetes Secret.
  3. Mount as files (preferred) or env vars.

File mount (recommended):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cve-radar
spec:
  template:
    spec:
      containers:
        - name: cve-radar
          image: raminnietzsche/cve-radar:latest
          env:
            - name: NVD_API_KEY_FILE
              value: /etc/cve-radar-secrets/nvd_api_key
            - name: GITHUB_TOKEN_FILE
              value: /etc/cve-radar-secrets/github_token
          volumeMounts:
            - name: cve-radar-secrets
              mountPath: /etc/cve-radar-secrets
              readOnly: true
      volumes:
        - name: cve-radar-secrets
          secret:
            secretName: cve-radar-secrets
            optional: true

ExternalSecret (illustrative — adjust to your provider):

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: cve-radar-secrets
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: cve-radar-secrets
    creationPolicy: Owner
  data:
    - secretKey: nvd_api_key
      remoteRef:
        key: cve-radar/nvd
        property: api_key
    - secretKey: github_token
      remoteRef:
        key: cve-radar/github
        property: token

Do not put secrets in Dockerfile ENV/ARG or image labels — they remain in layer history.

Verify image layers

Spot-check that no key material appears in build history:

docker history --no-trunc raminnietzsche/cve-radar:latest | head -20
docker inspect raminnietzsche/cve-radar:latest --format '{{json .Config.Env}}'

Only non-secret defaults (e.g. PORT, NODE_ENV) should appear.

CI

GitHub Actions should continue using repository secrets / environments — never commit .env or secrets/ contents. See .github/workflows/ci-app.yml.

Related