Skip to content

feat(auth): lerd auth ssh — shared ssh-agent for composer git over SSH#646

Merged
geodro merged 3 commits into
lerd-env:mainfrom
prabowosd:feat/auth-ssh-agent
Jun 26, 2026
Merged

feat(auth): lerd auth ssh — shared ssh-agent for composer git over SSH#646
geodro merged 3 commits into
lerd-env:mainfrom
prabowosd:feat/auth-ssh-agent

Conversation

@prabowosd

Copy link
Copy Markdown
Member

Fixes #643

Stacked on #645. This branch carries the two openssh image commits from #645 so the agent sidecar can reuse the ssh-capable FPM image. Please merge #645 first; the only commit to review here is feat(auth): lerd auth ssh …. I'll rebase onto main to drop the #645 commits once it merges.

What

lerd auth ssh, mirroring ddev auth ssh: load SSH keys into a shared ssh-agent so lerd composer can authenticate to private git repositories, including passphrase-protected keys.

Keys without a passphrase already work via the bind-mounted ~/.ssh once the image has an SSH client (#645). The agent covers passphrase-protected keys, which need an agent the FPM container can reach. On macOS the host agent socket can't cross the podman-machine VM boundary, so the agent runs inside podman.

How

  • A shared lerd-ssh-agent sidecar container runs ssh-agent and holds the unlocked keys in memory. It reuses the FPM image (the openssh-client added in fix(podman): enable git+ssh in PHP runtime images (FPM + FrankenPHP) #645 provides ssh-agent/ssh-add), so no extra image is pulled or built.
  • Its socket lives on the lerd-ssh-agent named volume, which is also mounted into the FPM containers, so the socket never crosses the host-VM boundary on macOS.
  • lerd composer / lerd php inject SSH_AUTH_SOCK into the container exec only when the agent is running, so they fall back to on-disk keys otherwise.
  • Consent/security: the sidecar mounts ~/.ssh read-only; unlocked key material stays in the agent's memory; keys clear when the agent stops or the machine restarts.

Commands: lerd auth ssh [key...] (default ~/.ssh/id_*), --list, --remove.

Verified end-to-end (local macOS, podman machine)

  1. php:rebuild 8.5 --local → image ships ssh/ssh-agent/ssh-add.
  2. lerd auth ssh <key> → sidecar up, key loaded (Identity added).
  3. From inside the FPM container: SSH_AUTH_SOCK=/ssh-agent/agent.sock ssh-add -l lists the key, proving the socket is shared across containers via the named volume.
  4. lerd php -r 'echo getenv("SSH_AUTH_SOCK")'/ssh-agent/agent.sock, the composer/php path injects it.

go build ./..., go vet, go test ./internal/cli/ ./internal/podman/ (1229 pass) all green with -tags nogui.

The FPM runtime image installs git but no SSH client, so composer cannot
fetch private packages over git+ssh — git's ssh transport is missing
entirely, which surfaces as an auth failure during `lerd composer
install`.

Add openssh-client to the runtime apk list. With $HOME already
bind-mounted, on-disk keys without a passphrase plus the user's
~/.ssh/known_hosts then work after an FPM image rebuild. Sharing an
ssh-agent for passphrase-protected keys builds on this and is tracked
separately (lerd-env#643).
FrankenPHP sites serve from a separate image that shipped neither git
nor an SSH client, so git over SSH run inside the FrankenPHP container
(private composer packages, source installs) failed. Add git +
openssh-client to match the FPM image, so every PHP runtime lerd builds
can do git+ssh.
Private composer packages pulled over git+ssh with passphrase-protected
keys can't authenticate inside the FPM container: there is no agent, and
the host agent socket isn't reachable (it can't cross the podman-machine
VM boundary on macOS).

Add `lerd auth ssh`, mirroring `ddev auth ssh`: a shared ssh-agent
sidecar container holds the unlocked keys in memory and exposes its
socket on the lerd-ssh-agent named volume, which is also mounted into the
FPM containers. `lerd composer` injects SSH_AUTH_SOCK only when the agent
is running, so it falls back to on-disk keys otherwise.

- internal/podman/sshagent.go: sidecar quadlet + SSH_AUTH_SOCK env helper
- FPM quadlet template mounts the lerd-ssh-agent named volume
- internal/cli/auth_ssh.go: `lerd auth ssh [key...]`, --list, --remove
- php_exec.go injects SSH_AUTH_SOCK into the composer/php container exec

The sidecar reuses the FPM image (its openssh-client provides ssh-agent
and ssh-add), so no extra image is pulled or built. Keys live only in the
agent's memory and are cleared when it stops or the machine restarts.
@geodro geodro merged commit 3da1ec0 into lerd-env:main Jun 26, 2026
3 checks passed
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.

Private packages authenticated with SSH key

2 participants