Files
explorer-monorepo/docs/SECURITY.md
Devin 4cbea21d8f fix(security): fail-fast on missing JWT_SECRET, harden CSP, strip hardcoded passwords
backend/api/rest/server.go:
- NewServer() now delegates to loadJWTSecret(), which:
    - Rejects JWT_SECRET < 32 bytes (log.Fatal).
    - Requires JWT_SECRET when APP_ENV=production or GO_ENV=production.
    - Generates a 32-byte crypto/rand ephemeral secret in dev only.
    - Treats rand.Read failure as fatal (removes the prior time-based
      fallback that was deterministic and forgeable).
- Default Content-Security-Policy rewritten:
    - Drops 'unsafe-inline' and 'unsafe-eval'.
    - Drops private CIDRs (192.168.11.221:854[5|6]).
    - Adds frame-ancestors 'none', base-uri 'self', form-action 'self'.
    - CSP_HEADER is required in production; fatal if unset there.

backend/api/rest/server_security_test.go (new):
- Covers the three loadJWTSecret() paths (valid, whitespace-trimmed,
  ephemeral in dev).
- Covers isProductionEnv() across APP_ENV / GO_ENV combinations.
- Asserts defaultDevCSP contains no unsafe directives or private CIDRs
  and includes the frame-ancestors / base-uri / form-action directives.

scripts/*.sh:
- Removed 'L@kers2010' default value from SSH_PASSWORD / NEW_PASSWORD in
  7 helper scripts. Each script now fails with exit 2 and points to
  docs/SECURITY.md if the password isn't supplied via env or argv.

EXECUTE_DEPLOYMENT.sh, EXECUTE_NOW.sh:
- Replaced hardcoded DB_PASSWORD='L@ker$2010' with a ':?' guard that
  aborts with a clear error if DB_PASSWORD (and, for EXECUTE_DEPLOYMENT,
  RPC_URL) is not exported. Other env vars keep sensible non-secret
  defaults via ${VAR:-default}.

README.md:
- Removed the hardcoded Database Password / RPC URL lines. Replaced with
  an env-variable reference table pointing at docs/SECURITY.md and
  docs/DATABASE_CONNECTION_GUIDE.md.

docs/DEPLOYMENT.md:
- Replaced 'PASSWORD: SSH password (default: L@kers2010)' with a
  required-no-default contract and a link to docs/SECURITY.md.

docs/SECURITY.md (new):
- Full secret inventory keyed to the env variable name and the file that
  consumes it.
- Five-step rotation checklist covering the Postgres role, the Proxmox
  VM SSH password, JWT_SECRET, vendor API keys, and a gitleaks-based
  history audit.
- Explicit note that merging secret-scrub PRs does NOT invalidate
  already-leaked credentials; rotation is the operator's responsibility.

Verification:
- go build ./... + go vet ./... pass clean.
- Targeted tests (LoadJWTSecret*, IsProduction*, DefaultDevCSP*) pass.

Advances completion criterion 2 (Secrets & config hardened). Residual
leakage from START_HERE.md / LETSENCRYPT_CONFIGURATION_GUIDE.md is
handled by PR #2 (doc consolidation), which deletes those files.
2026-04-18 19:02:27 +00:00

4.1 KiB
Raw Blame History

Security policy and rotation checklist

This document describes how secrets flow through the SolaceScan explorer and the operator steps required to rotate credentials that were previously checked into this repository.

Secret inventory

All runtime secrets are read from environment variables. Nothing sensitive is committed to the repo.

Variable Used by Notes
JWT_SECRET backend/api/rest/server.go HS256 signing key. Must be ≥32 bytes. Required when APP_ENV=production or GO_ENV=production. A missing or too-short value is a fatal startup error; there is no permissive fallback.
CSP_HEADER backend/api/rest/server.go Full Content-Security-Policy string. Required in production. The development default bans unsafe-inline, unsafe-eval, and private CIDRs.
DB_PASSWORD deployment scripts (EXECUTE_DEPLOYMENT.sh, EXECUTE_NOW.sh) and the API Postgres password for the explorer role.
SSH_PASSWORD scripts/analyze-besu-logs.sh, scripts/check-besu-config.sh, scripts/check-besu-logs-with-password.sh, scripts/check-failed-transaction-details.sh, scripts/enable-besu-debug-api.sh SSH password used to reach the Besu VMs. Scripts fail fast if unset.
NEW_PASSWORD scripts/set-vmid-password.sh, scripts/set-vmid-password-correct.sh Password being set on a Proxmox VM. Fail-fast required.
CORS_ALLOWED_ORIGIN backend/api/rest/server.go Optional. When set, restricts Access-Control-Allow-Origin. Defaults to * — do not rely on that in production.
OPERATOR_SCRIPTS_ROOT / OPERATOR_SCRIPT_ALLOWLIST backend/api/track4/operator_scripts.go Required to enable the Track-4 run-script endpoint.
OPERATOR_SCRIPT_TIMEOUT_SEC as above Optional cap (1599 seconds).

Rotation checklist

The repository's git history contains historical versions of credentials that have since been removed from the working tree. Treat those credentials as compromised. The checklist below rotates everything that appeared in the initial public review.

This repository does not rotate credentials on its own. The checklist below is the operator's responsibility. Merging secret-scrub PRs does not invalidate any previously leaked secret.

  1. Rotate the Postgres explorer role password.

    • Generate a new random password (openssl rand -base64 24).
    • ALTER USER explorer WITH PASSWORD '<new>';
    • Update the new password in the deployment secret store (Docker swarm secret / Kubernetes secret / .env.secrets on the host).
    • Restart the API and indexer services so they pick up the new value.
  2. Rotate the Proxmox / Besu VM SSH password.

    • sudo passwd besu (or equivalent) on each affected VM.
    • Or, preferred: disable password auth entirely and move to SSH keys (PasswordAuthentication no in /etc/ssh/sshd_config).
  3. Rotate JWT_SECRET.

    • Generate 32+ bytes (openssl rand -base64 48).
    • Deploy the new value to every API replica simultaneously.
    • Note: rotating invalidates every outstanding wallet auth token. Plan for a short window where users will need to re-sign.
    • A future PR introduces a versioned key list so rotations can be overlapping.
  4. Rotate any API keys (e.g. xAI / OpenSea) referenced by backend/api/rest/ai.go and the frontend. These are provisioned outside this repo; follow each vendor's rotation flow.

  5. Audit git history.

    • Run gitleaks detect --source . --redact at HEAD.
    • Run gitleaks detect --log-opts="--all" over the full history.
    • Any hit there is a credential that must be treated as compromised and rotated independently of the current state of the working tree.
    • Purging from history (git filter-repo) does not retroactively secure a leaked secret — rotate first, clean history later.

Build-time / CI checks (wired in PR #5)

  • gitleaks pre-commit + CI gate on every PR.
  • govulncheck, staticcheck, and go vet -vet=all on the backend.
  • eslint and tsc --noEmit on the frontend.

Reporting a vulnerability

Do not open public issues for security reports. Email the maintainers listed in CONTRIBUTING.md.