fix(ansible): clear error when the managed SSH key can't be decrypted
CI / lint (push) Successful in 3s
CI / unit (push) Successful in 6s
CI / integration (push) Successful in 2m17s
CI / publish (push) Successful in 54s

When ansible.ssh_private_key can't be decrypted (app secret key changed),
decrypt_secret returns the ciphertext unchanged; the executor was writing that
enc:v1: blob as the SSH key file → cryptic "Load key ...: error in libcrypto"
→ Permission denied. Now:
- build_credentials skips a still-encrypted key value (never writes ciphertext
  as a key file).
- start_run broadcasts a plain-language run error: "The managed SSH key could
  not be decrypted (the app secret key changed). Regenerate it in Settings →
  Ansible and re-provision the host(s)."

The run still fails (no usable key), but the reason is now obvious instead of a
libcrypto error. Operator remedy: regenerate the managed key + re-provision.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 12:52:24 -04:00
parent e0253fba48
commit 2594ca517d
+12 -1
View File
@@ -13,6 +13,8 @@ from datetime import datetime, timezone
from pathlib import Path
from typing import TYPE_CHECKING
from steward.core.crypto import is_encrypted as _crypto_is_encrypted
if TYPE_CHECKING:
from quart import Quart
@@ -133,8 +135,12 @@ def build_credentials(creds: dict | None, tmpdir: str) -> tuple[list[str], list[
files: list[tuple[str, str]] = []
creds = creds or {}
from steward.core.crypto import is_encrypted
key = creds.get("ssh_private_key")
if key:
# A still-encrypted value means decryption failed (app secret key changed) —
# writing the ciphertext as a key file just yields a cryptic libcrypto error,
# so skip it; start_run surfaces a clear message instead.
if key and not is_encrypted(key):
path = os.path.join(tmpdir, "id_key")
files.append((path, key if key.endswith("\n") else key + "\n"))
args += ["--private-key", path]
@@ -391,6 +397,11 @@ async def start_run(
effective_creds = dict(creds)
if conn.get("password"):
effective_creds.pop("ssh_private_key", None)
elif _crypto_is_encrypted(effective_creds.get("ssh_private_key") or ""):
_broadcast(run_id,
"[run error] The managed SSH key could not be decrypted "
"(the app secret key changed). Regenerate it in "
"Settings → Ansible and re-provision the host(s).")
cred_args, cred_files = build_credentials(effective_creds, tmpdir)
boot_args, boot_files = build_bootstrap(conn, tmpdir)