fix(setup): address security and robustness issues
- Use dollar-quoting in generated SQL to prevent injection - Set .env.prod and init-postgres-prod.sql to mode 0600 - Use run_compose for OpenBao log capture (consistent env-file) - Prompt user before continuing if OpenBao bootstrap fails - Improve mask_secret to fully mask short secrets - Check sysctl return code before parsing RAM
This commit is contained in:
23
setup.py
23
setup.py
@@ -142,8 +142,8 @@ def ask_yes_no(prompt: str, default: bool = False) -> bool:
|
||||
|
||||
def mask_secret(value: str) -> str:
|
||||
"""Show first 8 chars of a secret, mask the rest."""
|
||||
if len(value) <= 8:
|
||||
return value
|
||||
if len(value) <= 12:
|
||||
return "*" * len(value)
|
||||
return value[:8] + "..."
|
||||
|
||||
|
||||
@@ -221,6 +221,8 @@ def check_ram() -> None:
|
||||
["sysctl", "-n", "hw.memsize"],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return
|
||||
ram_bytes = int(result.stdout.strip())
|
||||
else:
|
||||
with open("/proc/meminfo") as f:
|
||||
@@ -577,6 +579,7 @@ CONFIG_BACKUP_MAX_CONCURRENT=10
|
||||
"""
|
||||
|
||||
ENV_PROD.write_text(content)
|
||||
ENV_PROD.chmod(0o600)
|
||||
ok(f"Wrote {ENV_PROD.name}")
|
||||
|
||||
|
||||
@@ -586,6 +589,7 @@ def write_init_sql_prod(config: dict) -> None:
|
||||
poll_pw = config["poller_user_password"]
|
||||
db = config["postgres_db"]
|
||||
|
||||
# Use dollar-quoting ($pw$...$pw$) to avoid SQL injection from passwords
|
||||
content = f"""\
|
||||
-- Production database init — generated by setup.py
|
||||
-- Passwords match those in .env.prod
|
||||
@@ -593,7 +597,7 @@ def write_init_sql_prod(config: dict) -> None:
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'app_user') THEN
|
||||
CREATE ROLE app_user WITH LOGIN PASSWORD '{app_pw}' NOSUPERUSER NOCREATEDB NOCREATEROLE;
|
||||
CREATE ROLE app_user WITH LOGIN PASSWORD $pw${app_pw}$pw$ NOSUPERUSER NOCREATEDB NOCREATEROLE;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
@@ -604,7 +608,7 @@ GRANT USAGE ON SCHEMA public TO app_user;
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'poller_user') THEN
|
||||
CREATE ROLE poller_user WITH LOGIN PASSWORD '{poll_pw}' NOSUPERUSER NOCREATEDB NOCREATEROLE BYPASSRLS;
|
||||
CREATE ROLE poller_user WITH LOGIN PASSWORD $pw${poll_pw}$pw$ NOSUPERUSER NOCREATEDB NOCREATEROLE BYPASSRLS;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
@@ -614,6 +618,7 @@ GRANT USAGE ON SCHEMA public TO poller_user;
|
||||
"""
|
||||
|
||||
INIT_SQL_PROD.write_text(content)
|
||||
INIT_SQL_PROD.chmod(0o600)
|
||||
ok(f"Wrote {INIT_SQL_PROD.name}")
|
||||
|
||||
|
||||
@@ -673,10 +678,7 @@ def bootstrap_openbao(config: dict) -> bool:
|
||||
|
||||
# Parse credentials from container logs
|
||||
info("Capturing OpenBao credentials from logs...")
|
||||
result = subprocess.run(
|
||||
["docker", "compose", "-f", COMPOSE_BASE, "-f", COMPOSE_PROD, "logs", "openbao"],
|
||||
capture_output=True, text=True, timeout=30, cwd=PROJECT_ROOT,
|
||||
)
|
||||
result = run_compose("logs", "openbao", check=False, capture=True, timeout=30)
|
||||
|
||||
logs = result.stdout + result.stderr
|
||||
unseal_match = re.search(r"BAO_UNSEAL_KEY=(\S+)", logs)
|
||||
@@ -693,6 +695,7 @@ def bootstrap_openbao(config: dict) -> bool:
|
||||
env_content = env_content.replace("BAO_UNSEAL_KEY=PLACEHOLDER_RUN_SETUP",
|
||||
f"BAO_UNSEAL_KEY={unseal_key}")
|
||||
ENV_PROD.write_text(env_content)
|
||||
ENV_PROD.chmod(0o600)
|
||||
|
||||
ok("OpenBao credentials captured and saved to .env.prod")
|
||||
info(f"OPENBAO_TOKEN={mask_secret(root_token)}")
|
||||
@@ -859,6 +862,10 @@ def main() -> int:
|
||||
|
||||
# Phase 4: OpenBao
|
||||
bao_ok = bootstrap_openbao(config)
|
||||
if not bao_ok:
|
||||
if not ask_yes_no("Continue without OpenBao credentials? (stack will need manual fix)", default=False):
|
||||
warn("Fix OpenBao credentials in .env.prod and re-run setup.py.")
|
||||
return 1
|
||||
|
||||
# Phase 5: Build
|
||||
if not build_images():
|
||||
|
||||
Reference in New Issue
Block a user