From 8fe5bbd0695b3502aa90bdd80626673e7f3d2659 Mon Sep 17 00:00:00 2001 From: sascha Date: Wed, 22 Apr 2026 21:20:19 +0200 Subject: [PATCH] Add SOPS + .env automation after ansible/run --- app.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/app.py b/app.py index a1486b0..bed35f3 100644 --- a/app.py +++ b/app.py @@ -430,6 +430,72 @@ async def ansible_run(request: Request, _=Depends(_verify)): break except Exception as e: log.warning(f"Hawser token sync failed for {hostname}: {e}") + + # NEW: SOPS + .env handling for Git-centric deployments + try: + log.info(f"Checking for SOPS .env setup for {hostname}") + # Get SOPS age public key from automation1 + rc4, age_pub, _ = _ssh(AUTOMATION1, "cat ~/.config/sops/age/keys.txt | grep '^# public key' | awk '{print $4}'", timeout=10) + age_pub = age_pub.strip() if rc4 == 0 else None + + if age_pub: + # Check if compose.yaml exists in /app-config/github/{hostname}/ + rc5, compose_check, _ = _ssh(AUTOMATION1, f"test -f /app-config/github/{hostname}/compose.yaml && echo 'found' || echo 'missing'", timeout=10) + + if compose_check.strip() == "found": + log.info(f"Found compose.yaml for {hostname}, generating .env") + + # Generate secrets + import secrets as sec + secret_key = base64.b64encode(sec.token_bytes(32)).decode() + admin_pw = sec.token_urlsafe(16) + db_pw = sec.token_urlsafe(16) + + # Store in vault cache + _vault_cache[f"{hostname}_secret_key"] = secret_key + _vault_cache[f"{hostname}_admin_password"] = admin_pw + _vault_cache[f"{hostname}_db_password"] = db_pw + + # Build .env content + env_lines = ["# Auto-generated by Butler", f"TZ=Europe/Berlin", "PUID=1000", "PGID=1000"] + + # Detect service type from hostname + if "paperless" in hostname.lower(): + env_lines.extend([ + f"PAPERLESS_ADMIN_USER=admin", + f"PAPERLESS_ADMIN_PASSWORD={admin_pw}", + f"PAPERLESS_SECRET_KEY={secret_key}", + f"PAPERLESS_URL=http://{vm_ip}:8000", + "PAPERLESS_TIME_ZONE=Europe/Berlin", + "PAPERLESS_OCR_LANGUAGE=deu", + "PAPERLESS_REDIS=redis://redis:6379", + "PAPERLESS_DBHOST=postgres", + "PAPERLESS_DBPORT=5432", + "PAPERLESS_DBNAME=paperless", + "PAPERLESS_DBUSER=paperless", + f"PAPERLESS_DBPASS={db_pw}", + "POSTGRES_DB=paperless", + "POSTGRES_USER=paperless", + f"POSTGRES_PASSWORD={db_pw}", + ]) + + env_content = "\\n".join(env_lines) + "\\n" + + # Write .env to automation1 + env_tmp = f"/tmp/{hostname}.env" + _ssh(AUTOMATION1, f"printf '%s' '{env_content}' > {env_tmp}", timeout=10) + + # Encrypt with SOPS + enc_tmp = f"/tmp/{hostname}.env.enc" + _ssh(AUTOMATION1, f"cd /tmp && SOPS_AGE_RECIPIENT={age_pub} sops --encrypted-regex 'PASSWORD|SECRET_KEY|_PASS' --encrypt {env_tmp} > {enc_tmp}", timeout=30) + + # Copy to VM + _ssh(AUTOMATION1, f"scp -o StrictHostKeyChecking=no {env_tmp} sascha@{vm_ip}:/app-config/github/{hostname}/.env 2>/dev/null || true", timeout=15) + _ssh(AUTOMATION1, f"scp -o StrictHostKeyChecking=no {enc_tmp} sascha@{vm_ip}:/app-config/github/{hostname}/.env.enc 2>/dev/null || true", timeout=15) + + log.info(f"SOPS .env setup completed for {hostname}") + except Exception as e: + log.warning(f"SOPS .env setup failed for {hostname}: {e}") return {"status": "ok" if rc == 0 else "error", "rc": rc, "output": out[-1000:]} @@ -562,3 +628,4 @@ async def proxy(service: str, path: str, request: Request, _=Depends(_verify)): +