Skip to content

[Bug] Cloud credentials passed as docker run -e NAME=value (secret values exposed on argv) #11

Description

@sjohnston1972

Background

When the deploy/destroy tools spawn a cloud-CLI sidecar, they pass credentials to it as docker run -e NAME=value flags where value is the literal secret. See backend/src/claude/custom-tools.ts:

  • Azure SP secret: -e AZURE_CLIENT_SECRET=${config.AZURE_CLIENT_SECRET} in runDestroy (~line 711) and runBicepDeploy (~line 886), alongside tenant/client/subscription ids.
  • AWS keys: -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} / AWS_SESSION_TOKEN in awsCliDockerArgs (~1035-1041) and runDestroyAws finalArgs (~1366-1373).

Problem / Goal

Passing secrets as -e NAME=value on the docker run command line exposes them. Command-line arguments are world-readable: any process on the host can see them via ps auxww / /proc/<pid>/cmdline, they show up in docker inspect of the sidecar, and they can be captured by process-accounting or audit logging. For a tool whose entire value proposition is holding cloud credentials, leaking the SP secret / IAM secret key this way is a meaningful hardening gap even on a single-user host.

Goal: pass secrets to the sidecar without placing their values on the argv.

Where to look

  • backend/src/claude/custom-tools.ts: runDestroy docker args (~703-719), runBicepDeploy docker args (~877-894), awsCliDockerArgs (~1026-1053), runDestroyAws finalArgs (~1360-1388).
  • docker-compose.yml:46-51 — the backend already receives these secrets via env_file: .env, so they're present in the backend process environment.

Suggested approach

Two clean options (pick one; env-passthrough is simplest):

  1. Env passthrough by name. Because the backend already has AZURE_CLIENT_SECRET etc. in its own environment (env_file: .env), pass -e AZURE_CLIENT_SECRET (name only, no =value). Docker forwards the value from the backend's environment without it ever appearing on the sidecar's argv.
  2. --env-file. Write the secrets to a 0600 temp file and pass --env-file /path; delete it in finally. More moving parts than option 1.

Apply the chosen approach consistently to all four spawn sites; keep non-secret values (region, subscription id if you consider it non-secret) as-is or move them too for uniformity.

Reproduce: run a deploy/destroy and ps auxww | grep AZURE_CLIENT_SECRET (or docker inspect the transient sidecar) — today the secret value is visible; after the fix it is not.

Acceptance criteria

  • No secret value (AZURE_CLIENT_SECRET, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) appears in any docker run argv produced by the tools.
  • The sidecars still authenticate and deploy/destroy successfully.

Testing

  • Assert in a unit test that the generated dockerArgs for each handler contain no secret value (only -e NAME name-only entries, or an --env-file path).
  • Manual: run a deploy and confirm ps/docker inspect no longer shows the secret.

Out of scope

  • Rotating/scoping the service principal's Azure RBAC.
  • Removing the Docker-socket mount.

Sub-issues: (1) Azure SP secret passthrough, (2) AWS credential passthrough.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions