#!/usr/bin/env python3 """ Hämta Cibello-prod-hemligheter från AWS SSM och skriv /opt/cibello-platform/.env. SSM är enda sanningskällan. Kör detta vid varje deploy INNAN applikationen startas om. Inga värden loggas. set +x aktiveras automatiskt. """ import json import os import subprocess import sys from pathlib import Path REGION = os.environ.get("AWS_REGION", "eu-north-1") SSM_PREFIX = "/cibello/prod" ENV_PATH = Path(os.environ.get("ENV_PATH", "/opt/cibello-platform/.env")) # Icke-hemliga värden som ändå behöver styras centralt. # Dessa kan med fördel också flyttas till SSM (String/SecureString) senare. STATIC_CONFIG = { "NODE_ENV": "production", "API_PORT": "4000", "ADMIN_PORT": "5173", "API_BASE_URL": "https://api.cibello.app", "VITE_API_BASE_URL": "https://api.cibello.app", "CORS_ORIGINS": "https://cibello.app,https://admin.cibello.app,https://api.cibello.app", "REDIS_URL": "redis://localhost:6379", "LOG_LEVEL": "info", "AAMOS_MODE": "gemini", "AAMOS_API_URL": "https://amos.aamos.systems", "EOC_URL": "https://eoc.aamos.systems", "AAMOS_TIMEOUT_MS": "60000", # GEMINI_MODEL hämtas från SSM (String-typ) "GEMINI_TIMEOUT_MS": "60000", "GEMINI_DAILY_BUDGET_USD": "25", "ENTITLEMENT_TTL_HOURS": "24", "ENTITLEMENT_GRACE_DAYS": "7", "JWT_ACCESS_TTL_SECONDS": "900", "JWT_REFRESH_TTL_SECONDS": "2592000", "S3_MODE": "aws", "S3_BUCKET": "cibello-production", "S3_REGION": "eu-north-1", "SMTP_HOST": "mail.aamos.systems", "SMTP_PORT": "587", "SMTP_SECURE": "false", "SMTP_USER": "hello@cibello.app", "MAIL_FROM": "hello@cibello.app", "EMAIL_MODE": "smtp", "EXPO_PUSH_ENABLED": "false", "DATABASE_CA_CERT": "/opt/cibello-platform/certs/rds-global-bundle.pem", } # SSM-parameter → env-var. Värden från SSM skriver över STATIC_CONFIG om de krockar. SSM_MAP = { "aamos-api-key": "AAMOS_API_KEY", "db-password": "DB_PASSWORD", # används för att bygga DATABASE_URL "entitlement-signing-secret": "ENTITLEMENT_SIGNING_SECRET", "eoc-push-token": "EOC_PUSH_TOKEN", "gemini-api-key": "GEMINI_API_KEY", "gemini-model": "GEMINI_MODEL", "jwt-access-secret": "JWT_ACCESS_SECRET", "jwt-refresh-secret": "JWT_REFRESH_SECRET", "smtp-pass": "SMTP_PASS", "ops-token": "OPS_TOKEN", # Följande är valfria just nu men bör ligga i SSM "google-service-account-json": "GOOGLE_SERVICE_ACCOUNT_JSON", } def get_parameters(names: list[str]) -> dict[str, str]: if not names: return {} out: dict[str, str] = {} invalid: list[str] = [] # AWS GetParameters max 10 namn per anrop. for i in range(0, len(names), 10): batch = names[i : i + 10] cmd = [ "aws", "ssm", "get-parameters", "--names", *batch, "--with-decryption", "--region", REGION, ] result = subprocess.run(cmd, capture_output=True, text=True, check=True) data = json.loads(result.stdout) for p in data.get("Parameters", []): out[p["Name"]] = p["Value"].strip() invalid.extend(data.get("InvalidParameters", [])) for inv in invalid: print(f"[sync-env] WARN: SSM-parameter saknas: {inv}", file=sys.stderr) return out def build_env(ssm: dict[str, str]) -> dict[str, str]: env = dict(STATIC_CONFIG) for short, var in SSM_MAP.items(): val = ssm.get(f"{SSM_PREFIX}/{short}") if val is not None: env[var] = val db_password = env.get("DB_PASSWORD", "") db_host = os.environ.get("DB_HOST", "cibello-prod-db.cvi0qcksmsfj.eu-north-1.rds.amazonaws.com") db_name = os.environ.get("DB_NAME", "cibello") test_db_name = os.environ.get("TEST_DB_NAME", "cibello_test") db_user = os.environ.get("DB_USER", "cibello_admin") db_sslmode = os.environ.get("DB_SSLMODE", "require") if db_password: env["DATABASE_URL"] = f"postgres://{db_user}:{db_password}@{db_host}:5432/{db_name}?sslmode={db_sslmode}" env["TEST_DATABASE_URL"] = f"postgres://{db_user}:{db_password}@{db_host}:5432/{test_db_name}?sslmode={db_sslmode}" # Rensa temporär nyckel env.pop("DB_PASSWORD", None) return env def write_env(env: dict[str, str], dry_run: bool = False) -> None: lines = [f"{k}={v}" for k, v in env.items()] if dry_run: print(f"[sync-env] DRY-RUN: skulle skriva {ENV_PATH} ({len(env)} rader)") return ENV_PATH.parent.mkdir(parents=True, exist_ok=True) ENV_PATH.write_text("\n".join(lines) + "\n", encoding="utf-8") os.chmod(ENV_PATH, 0o600) print(f"[sync-env] skrev {ENV_PATH} ({len(env)} rader)") def main() -> int: dry_run = "--dry-run" in sys.argv names = [f"{SSM_PREFIX}/{short}" for short in SSM_MAP] ssm = get_parameters(names) env = build_env(ssm) write_env(env, dry_run=dry_run) return 0 if __name__ == "__main__": sys.exit(main())