Skip to content

fix(deploy): container config contract — compose .env, in-container SSH unlock, Git PAT hardening#112

Open
pparage wants to merge 3 commits into
devfrom
fix/deploy-container-config-131
Open

fix(deploy): container config contract — compose .env, in-container SSH unlock, Git PAT hardening#112
pparage wants to merge 3 commits into
devfrom
fix/deploy-container-config-131

Conversation

@pparage

@pparage pparage commented Jul 14, 2026

Copy link
Copy Markdown
Member

Backend side of range42/range42-playbooks#131 (align the deployer-ui/backend bundle config contract). Three commits, all TDD; full suite green except the pre-existing overlay failure (#84).

compose .env drives runtime config (ec8c1bf)
docker-compose.yml hard-coded API_BACKEND_WWWAPP_PLAYBOOKS_DIR=/app/, overriding the deploy bundle's .env — so the RO playbooks bind-mount was never read (baked-in /app/ has no scenarios/). Now .env-driven via env_file (required:false) + ${VAR:-default}.

In-container SSH-key unlock (0a9dc2f) — answers Ben's SSH question
range42 deploy keys are passphrase-protected (per-family passphrase in the workspace vault). The container has no ssh-agent, so ansible-runner couldn't reach the VMs. New app/core/ssh_agent.py decrypts the vault with vault_pass.txt, starts an ssh-agent, and ssh-adds the keys via the same SSH_ASKPASS mechanism range42-context uses; wired into deploy_trigger.start_attempt (SSH_AUTH_SOCK into the runner env, agent closed after). Keys stay passphrase-protected — nothing changes on the deployer-cli side.

Git PAT hardening (e6f5fae) — makes it safe to wire the source seed

  • SourceOut no longer returns token_ref (exposes has_token: bool). The deployer-ui never read the token back.
  • catalog clones (entries, refresh) now authenticate with the token and redact it from errors.
  • checkout_project fetches the URL ad-hoc instead of git remote add origin, so the PAT is no longer persisted in the workspace .git/config.

Deferred (flagged): projects/compose.py base-catalog clone (different repo/token scope); token-at-rest encryption (needs a secret-store decision); Proxmox HostOut token_ref (separate credential).

Refs range42/range42-playbooks#131.

pparage added 3 commits July 14, 2026 18:01
The environment: block hard-coded API_BACKEND_WWWAPP_PLAYBOOKS_DIR=/app/,
overriding any value the deployer bundle rendered in .env. Since the baked-in
/app/ has no scenarios/, the RO playbooks bind-mount the bundle sets up was
never actually read.

- Make WWWAPP/PUBLIC/INVENTORY dirs overridable via ${VAR:-default} so the
  bundle's .env wins while plain `docker compose up` keeps the /app/ default.
- Add env_file (required:false) so RANGE42_WORKSPACE_ROOT / RANGE42_DB_URL and
  other bundle .env vars reach the container only when present (avoids forcing
  them to an empty string, which the app treats as a broken value).
…nsible-runner (#131)

The range42 deploy keys are ed25519 keys with a per-family passphrase stored in
the workspace vault (ssh_passphrase_*). On the deployer host range42-context
loads them into an ssh-agent via SSH_ASKPASS; the backend container has no such
agent, so ansible-runner could not authenticate to the Proxmox jump + VMs.

- app/core/ssh_agent.py: decrypt the workspace vault with vault_pass.txt, start
  a dedicated ssh-agent, ssh-add every private key under ssh_keys/ (passphrase
  fed non-interactively via a throwaway SSH_ASKPASS shim, mapped by filename
  suffix exactly like range42-context). Returns a handle exposing SSH_AUTH_SOCK,
  or None when there is no vault/keys (falls back to ambient ~/.ssh).
- deploy_trigger.start_attempt: unlock before spawning the runner, merge
  SSH_AUTH_SOCK into envvars, close the agent in _run()'s finally.
- Tests exercise real ssh-agent/ssh-add/ansible-vault (single + multi-family +
  extra-student + passphrase-less keys, None fallbacks, agent teardown, and the
  start_attempt wiring).
The Source Git PAT (token_ref) was returned verbatim by GET/POST
/v1/catalog/sources, and the catalog-source clones ignored it (private repos
failed). This hardens the token handling ahead of wiring the deploy-bundle seed.

- SourceOut no longer carries token_ref; exposes has_token: bool instead. The
  deployer-ui never read the token back, so nothing breaks.
- Extract authed_url(url, token) in project.py; entries.py + refresh.py now
  clone via it and redact the token from any clone error (_redact_authed_url).
- checkout_project fetches the URL ad-hoc instead of 'git remote add origin':
  a named remote persisted the authed URL (PAT) in the workspace .git/config.

Deferred (flagged): projects/compose.py base_catalog clone (different repo/token
scope) and token-at-rest encryption (needs a secret-store decision). The
Proxmox HostOut token_ref leak is a separate credential, out of this scope.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant