Skip to content

feat: SSH agent relay for DDEV containers and Coder key in an agent, revert PHP2Shell workaround, fixes #220, fixes #210 - #223

Merged
rfay merged 2 commits into
mainfrom
20261026_rfay_ssh_agent_relay
Oct 26, 2026
Merged

rfay merged 2 commits into
mainfrom
20261026_rfay_ssh_agent_relay

Conversation

@rfay

@rfay rfay commented Oct 26, 2026

Copy link
Copy Markdown
Member

The Issue

DDEV v1.25.5 adds ssh_agent_upstream (ddev/ddev#8861) and resolves symlinked agent sockets (ddev/ddev#8963). v1.25.5 also fixes PHP2Shell, so the #222 workaround can go.

How This PR Solves The Issue

  • Revert of fix: PHP2Shell workaround, upgrade PHP in DDEV web images on workspace start #222: the PHP2Shell workaround is removed, because DDEV v1.25.5 web images have fixed PHP.
  • Image v0.6 (already pushed): DDEV v1.25.5, plus socat, fzf and zstd. It also ships /home/coder-files/ssh-agent-relay/connect and bashrc.sh.
  • shared/ssh-agent-setup.sh: copied into each template by make sync-ssh-agent-setup. It runs in every template's startup script after Docker is up:
    • It loads the Coder key into ssh-agent at ~/.coder-ssh-agent/agent.sock. The key is piped from the API into ssh-add - and never written to disk.

    • It runs a socat relay at ~/.ssh-agent-relay/agent.sock. On each connection, connect uses the first live agent of:

      1. current, the forwarded agent of the most recent interactive shell;
      2. the newest live /tmp/auth-agent*/listener.sock;
      3. the Coder-key agent.

      This means it follows a VS Code reconnect without a new terminal, and falls back to the Coder key when nothing is forwarded.

    • It sets ddev config global --ssh-agent-upstream to the relay socket. This is never current, because DDEV resolves symlinks once, when the project starts.

    • It adds one line to ~/.bashrc that sources bashrc.sh. In an interactive shell with a live forwarded agent, that script retargets current, then points the shell's SSH_AUTH_SOCK at the relay.

  • Liveness checks: an agent counts as live only if timeout 2 ssh-add -l exits 0 or 1. A dead relay never replaces a working socket, and a hung forwarded agent can't stall a shell.
  • Socket directories: each socket has its own directory, because DDEV mounts the socket's whole directory into ddev-ssh-agent.
  • Spec, tests and docs:
    • OpenSpec change add-ssh-agent-relay;
    • an ssh_agent_relay run in each template's tests;
    • a new docs section, "SSH Keys in DDEV Containers", in docs/user/using-workspaces.md.

Known limitation, documented: the last session to forward an agent wins for the whole workspace. With workspace sharing, a collaborator's coder ssh -A switches the containers to their keys until the owner opens a new terminal.

Manual Testing Instructions

These need the v0.6 image, on staging. Use a fresh workspace from each template:

  1. Run SSH_AUTH_SOCK=~/.coder-ssh-agent/agent.sock ssh-add -l. It should list the Coder key.
  2. With no agent forwarded (web terminal), run ddev auth ssh and ddev exec ssh -T git@github.com. They should list and use the Coder key, and plain ssh -T git@github.com should work too.
  3. Run coder ssh -A <ws> from a machine with your own agent, then repeat step 2. The containers should get your keys.
  4. Do the same from VS Code Desktop. Then reconnect VS Code without opening a new terminal and run ddev exec ssh-add -l again. It should still work.
  5. Disconnect every forwarding session. ddev exec ssh-add -l should fall back to the Coder key.
  6. Check that the PHP2Shell workaround isn't needed: ddev exec php -v should show a fixed PHP.

Automated Testing Overview

  • terraform fmt, make validate and make test-templates pass, and openspec validate add-ssh-agent-relay --strict passes.
  • The connect/bashrc.sh fallback order was tested in a plain ubuntu:24.04 container with fake agents in six scenarios: Coder key only, a forwarded agent, a shell retargeting current, the forwarded agent gone, all forwarded agents gone, and a shell started with a dead SSH_AUTH_SOCK.
  • Not yet verified: whether ddev-ssh-agent in the nested Docker under Sysbox can connect to the mode=600 relay socket. That's step 2 above.

Related Issue Link(s)

🤖 Generated with Claude Code

rfay and others added 2 commits October 26, 2026 11:12
DDEV v1.25.5 ships ddev-webserver images with fixed PHP, and the v0.6
image installs it, so the per-start Dockerfile.php2shell upgrade step is
no longer needed.

This reverts commit cd5e899.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#220, fixes #210

DDEV's containers couldn't use the Coder key or the user's forwarded
agent, and a forwarded agent's socket dies on every VS Code reconnect.
DDEV v1.25.5 adds ssh_agent_upstream and resolves symlinked sockets.

- Image (v0.6, DDEV v1.25.5): add socat, fzf, zstd, and ship
  ssh-agent-relay/connect and bashrc.sh in /home/coder-files.
- shared/ssh-agent-setup.sh (vendored by make sync-ssh-agent-setup,
  included in all three startup scripts):
  - loads the Coder key into ssh-agent at ~/.coder-ssh-agent/agent.sock
  - runs a socat relay at ~/.ssh-agent-relay/agent.sock that, per
    connection, uses the first live agent of: the most recent shell's
    forwarded agent (current), the newest /tmp/auth-agent*/listener.sock,
    the Coder-key agent
  - sets ddev ssh_agent_upstream to the relay socket
  - sources bashrc.sh from ~/.bashrc, which retargets current from shells
    with a live forwarded agent and points SSH_AUTH_SOCK at the relay
- Liveness checks use timeout 2 ssh-add -l (exit 0/1 = live).
- OpenSpec change add-ssh-agent-relay, user docs, template tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-26 19:05 UTC

@rfay
rfay merged commit 3bf95b8 into main Oct 26, 2026
18 checks passed
@rfay
rfay deleted the 20261026_rfay_ssh_agent_relay branch October 26, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant