Skip to content

H-043 Deploy key confinement and blast-radius reduction #54

Description

@dashprotocol

Context

Surfaced during H-041 security review. The deploy SSH key (DEPLOY_SSH_KEY GitHub secret) is currently added to authorized_keys without a command= restriction. A compromised key grants a full interactive shell as adminuser — not just the ability to run deploy.sh. Combined with adminuser owning /opt/havenhold, this allows modifying deploy.sh before the sudoers-allowed invocation, creating a path from key compromise to root.

This is not a regression introduced by H-041 — it is a known hardening gap to address in a follow-up pass.

Scope

Three independent hardening layers, in priority order:

1. Deploy key forced-command wrapper (highest value)

Add a command= restriction to the deploy key's authorized_keys entry. A bare command="sudo /usr/bin/bash /opt/havenhold/infra/deploy.sh" breaks scp (which uses scp -t <path> as the remote command). The solution is a wrapper script that:

  • Allows scp -t /opt/havenhold/server/.env and scp -t /opt/havenhold/.env (exact target paths only)
  • Allows sudo /usr/bin/bash /opt/havenhold/infra/deploy.sh
  • Allows the curl health check invocation
  • Rejects everything else

The authorized_keys entry becomes:

command="/opt/havenhold/infra/deploy-shell-wrapper.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 <key>

The wrapper inspects $SSH_ORIGINAL_COMMAND and either allows the exact known commands or exits non-zero.

2. Reduce adminuser ownership blast radius

Change /opt/havenhold ownership from adminuser:adminuser to root:havenhold (or a narrower user) where possible, so a compromised adminuser shell cannot freely modify the repo or deploy script. The deploy script itself should be owned by root and only writable by root.

3. GitHub Environment protection for deploy secrets

Add an environment: production key to the deploy job in ci.yml and configure the GitHub environment with:

  • Required reviewer before deploy runs
  • Restricted to main branch only

This adds a human gate and also adds an environment claim to the OIDC token, allowing the IAM trust policy to be tightened further.

Acceptance Criteria

  • authorized_keys deploy key entry uses command= with a wrapper that allowlists exact SCP targets and deploy command only
  • Wrapper rejects all other SSH commands (interactive shell, arbitrary scp paths, etc.)
  • /opt/havenhold/infra/deploy.sh is owned by root, not adminuser
  • Deploy job has environment: production with required reviewer configured
  • Emergency manual deploy path still works (documented in docs/deployment.md)

Deploy Gate

None — security hardening, no feature changes.

Notes

  • The wrapper approach must be designed carefully before implementation; a quick patch risks breaking automated deploys silently.
  • Codex review note: "A strict command='...' restriction on the deploy key will break your current scp steps unless you also implement a wrapper that safely allows the scp subcommands."
  • Reference: H-041 security review observation.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions