Skip to content

Encrypt SSH proxy password at rest

Placeholder ppxd requested to merge fix/p2-atrest-ssh-proxy-password into main

Summary

  • The SSH proxy password lived as plaintext inside the machine endpoint JSON column. Encrypt it at rest via the shared AtRestSecretProtector — at the two write seams (MachineRegistrationService register + MachineService.MergeSsh update) and decrypt at the two read seams (SshEndpointVariableContributor deploy path + SshHealthCheckStrategy connection builder).
  • The read seams are synchronous (IEndpointVariableContributor.ContributeVariables), and decrypt is pure CPU work, so add a synchronous Decrypt to IVariableEncryptionService (the already-sync core of DecryptAsync, which now delegates to it) and a synchronous Unprotect to IAtRestSecretProtector. This keeps the contributor interface sync — no async ripple across every transport's contributor.

Second P2 at-rest finisher (after AtRestSecretProtector extraction #445). Remaining: Tentacle private key (agent-side, separate mechanism).

Non-breaking

  • No new plaintext exposure: the proxy password ships inside the opaque Machine.Endpoint JSON returned by machine GET/list — previously as plaintext, now as the encrypted envelope (a net improvement). No consumer round-trips it (SquidWeb has zero ProxyPassword references; updates send changed fields only).
  • Read-both: a legacy plaintext proxy password in an existing machine's endpoint passes through Unprotect verbatim — no migration.
  • The protector is an optional ctor param on all four seams; DI injects the real one in production, and any test that constructs them without it degrades to the pre-feature plaintext path. Encrypt is idempotent, so a re-save (or the unchanged value carried through an update merge) never double-wraps.

Test plan

  • Unit (SshProxyPasswordAtRestTests): contributor decrypts an envelope before contributing the SSH connection variable; legacy plaintext passes through; no-protector leaves the value untouched.
  • Integration (real Postgres + real DI + real AES-256-GCM, IntegrationSshProxyPasswordAtRest): a proxy password encrypted via the real protector and stored in a machine's endpoint jsonb is decrypted by the DI-resolved contributor (proves the protector is wired into the read seam, raw column carries the SQUID_ENCRYPTED_V2: envelope not cleartext); legacy plaintext reads back verbatim.
  • Full unit suite green (6048).

Merge request reports

Loading