Skip to content

Load the Coder SSH key into an ssh-agent so DDEV containers can use it #210

Description

@rfay

Problem

In a workspace, only git can use the Coder-managed SSH key: the startup script sets GIT_SSH_COMMAND to coder gitssh, which fetches the key with the workspace agent's credentials for each git command. There is no SSH agent (SSH_AUTH_SOCK is unset), so plain ssh in the workspace doesn't have the key, and DDEV's containers have neither the coder binary nor the credentials. Anything inside web that needs the key fails, for example composer install with private repositories, drush aliases, or git in a post-start hook.

Solution

Load the Coder key into a regular ssh-agent at startup and point DDEV at it. This was tested by hand in two workspaces (freeform and drupal-contrib templates), where ddev exec ssh -T git@github.com authenticated with the Coder key; details are in HANDOFF.md on ddev/ddev#8861.

In the startup script, after the Docker daemon is up:

# Start an agent on a fixed socket in its own directory, and load the Coder key.
AGENT_DIR="$HOME/.coder-ssh-agent"
mkdir -p "$AGENT_DIR" && chmod 700 "$AGENT_DIR"
rm -f "$AGENT_DIR/agent.sock"
setsid ssh-agent -a "$AGENT_DIR/agent.sock" >/dev/null
curl -fsS -H "Coder-Session-Token: $CODER_AGENT_TOKEN" \
  "$${CODER_AGENT_URL%/}/api/v2/workspaceagents/me/gitsshkey" \
  | jq -r .private_key | SSH_AUTH_SOCK="$AGENT_DIR/agent.sock" ssh-add -q -

# Make plain ssh in the workspace use it.
sed -i '/^export SSH_AUTH_SOCK=/d' ~/.bashrc || true
echo "export SSH_AUTH_SOCK=$AGENT_DIR/agent.sock" >> ~/.bashrc

The $${...} is escaped for Terraform, as elsewhere in template.tf; in a plain shell it is ${CODER_AGENT_URL%/}.

Then make DDEV's containers use the same agent. With a DDEV release that includes ddev/ddev#8861:

ddev config global --ssh-agent-upstream="$HOME/.coder-ssh-agent/agent.sock" > /dev/null 2>&1 || true

With released DDEV, a global override file does the same:

# ~/.ddev/ssh-auth-compose.coder-agent.yaml
services:
  ddev-ssh-agent:
    volumes:
      - ${HOME}/.coder-ssh-agent:/upstream
    command: ["socat", "UNIX-LISTEN:/tmp/.ssh-agent/socket,perm=0666,fork,unlink-early", "UNIX-CONNECT:/upstream/agent.sock"]
    healthcheck:
      test: "killall -0 socat"

Notes:

  • The socket needs a directory of its own. DDEV mounts the socket's directory into its relay container so that a restarted agent keeps working, so a socket directly in ~/.ssh or $HOME would expose that whole directory.
  • Docker runs inside the workspace, so workspace paths are Docker host paths and the mount works as on native Linux.
  • The key is never printed or written to disk; ssh-add - reads it from the pipe.
  • The rm -f is needed because the home directory persists across restarts and ssh-agent -a refuses an existing socket file.
  • Check that the agent survives the end of the startup script. It is started with setsid for that reason, since the script's children may be cleaned up.
  • GIT_SSH_COMMAND can stay as it is; git in the workspace keeps working either way.
  • The same change applies to the drupal-contrib and drupal-core templates.
  • Every container on the DDEV network can then ask the agent to sign with the Coder key, which is the same key the workspace user can already fetch.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions