feat(devcontainer): support YubiKey SSH/GPG relay on Windows hosts - #755
Open
woopstar wants to merge 14 commits into
Open
feat(devcontainer): support YubiKey SSH/GPG relay on Windows hosts#755woopstar wants to merge 14 commits into
woopstar wants to merge 14 commits into
Conversation
Docker Desktop on Windows cannot forward named pipes or GnuPG emulated sockets into containers. Add a pure-PowerShell TCP relay (start-agent-relay.ps1) that bridges the Windows OpenSSH named pipe and GnuPG emulation sockets (TCP+nonce) to localhost ports 9999/9998/9997, and an installer (install-agent-relay.ps1) that registers a per-user Scheduled Task or Startup-folder launcher. The container-side bridge (devcontainer-agent-bridge.sh) falls back to a workspace snapshot of the host GnuPG public key material when /root/.gnupg has no keyring (Windows layout), and devcontainer.json gains USERPROFILE fallback for .ssh/.gitconfig mounts. Verified end-to-end on a Windows 11 host with Gpg4win 2.5.21 and a YubiKey: SSH key listing, GPG assuan greeting, and scdaemon greeting all pass through the relays. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI creates ~/.gitconfig and ~/.gnupg as empty directories via 'mkdir -p'. The bridge script now guards the find/copy with a directory check and starts the git config from scratch when the stub is a directory instead of a file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The devcontainer mounts for ~/.ssh and ~/.gitconfig used
`${localEnv:HOME}${localEnv:USERPROFILE}`, which resolves to the WSL home
on Windows/WSL2 even when the user's actual SSH config lives under their
Windows profile. This caused container startup to fail with:
invalid mount config for type "bind": bind source path does not exist
Add a platform-specific initializeCommand that snapshots the host's .ssh
and .gitconfig into .devcontainer/host-config/ before the container is
started. The devcontainer.json mounts then source from this known,
guaranteed-to-exist location. Empty stubs are created when the host files
are missing so the bind mount never blocks container startup.
- .devcontainer/scripts/initialize-host.sh: Unix/WSL2 staging script
- .devcontainer/scripts/initialize-host.ps1: native Windows staging script
- .devcontainer/devcontainer.json: add initializeCommand and update mounts
- .devcontainer/README.md: document staging behavior
- .gitignore: ignore .devcontainer/host-config/
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds Windows-host YubiKey SSH/GPG forwarding for the devcontainer.
Changes:
- Adds PowerShell relay and installer scripts.
- Introduces cross-platform host configuration staging.
- Updates container bridging, CI, documentation, and ignored snapshots.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
.gitignore |
Ignores generated host snapshots. |
.github/workflows/lint-and-test.yml |
Adjusts CI mount stubs. |
.devcontainer/devcontainer.json |
Configures staging and mounts. |
.devcontainer/README.md |
Documents Windows setup. |
.devcontainer/scripts/start-agent-relay.ps1 |
Implements Windows TCP relays. |
.devcontainer/scripts/install-agent-relay.ps1 |
Installs and provisions the relay. |
.devcontainer/scripts/initialize-host.sh |
Stages Unix/WSL host configuration. |
.devcontainer/scripts/initialize-host.ps1 |
Stages Windows host configuration. |
.devcontainer/scripts/devcontainer-agent-bridge.sh |
Supports Windows GnuPG snapshots. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| }, | ||
| "postCreateCommand": "/usr/local/bin/setup-python-deps.sh; if [ -z \"$CI\" ]; then /usr/local/bin/devcontainer-agent-bridge.sh /bin/sh -c 'echo SSH agent ready in container || true'; fi", | ||
| "postStartCommand": "if [ -z \"$CI\" ]; then nohup /usr/local/bin/devcontainer-agent-bridge.sh /bin/true >/tmp/agent-bridge.log 2>&1 & fi", | ||
| "initializeCommand": "sh .devcontainer/scripts/initialize-host.sh", |
Comment on lines
+21
to
+22
| "type=bind,source=${localWorkspaceFolder}/.devcontainer/host-config/.ssh,target=/root/.ssh,readonly", | ||
| "type=bind,source=${localWorkspaceFolder}/.devcontainer/host-config/.gitconfig,target=/root/.gitconfig", |
Comment on lines
+107
to
+111
| # Flatten the keyboxd store: container GnuPG 2.4 (non-keyboxd) | ||
| # expects a classic keyring at pubring.kbx. | ||
| $kbx = Join-Path $GpgSnapshot 'public-keys.d\pubring.db' | ||
| if ((Test-Path $kbx) -and -not (Test-Path (Join-Path $GpgSnapshot 'pubring.kbx'))) { | ||
| Copy-Item $kbx (Join-Path $GpgSnapshot 'pubring.kbx') |
| -Description 'TCP relays for YubiKey SSH/GPG agents into Docker devcontainers' ` | ||
| -ErrorAction Stop | Out-Null | ||
| Start-ScheduledTask -TaskName $TaskName | ||
| $installed = $true |
Comment on lines
+35
to
+42
| if (Test-Path $SshSource) { | ||
| New-Item -ItemType Directory -Force -Path $SshTarget | Out-Null | ||
| Get-ChildItem -LiteralPath $SshSource -Force | ForEach-Object { | ||
| Copy-Item -LiteralPath $_.FullName -Destination $SshTarget -Recurse -Force | ||
| } | ||
| } else { | ||
| New-Item -ItemType Directory -Force -Path $SshTarget | Out-Null | ||
| } |
Comment on lines
+52
to
+57
| if [ -d "${HOST_CONFIG_DIR}/.ssh" ]; then | ||
| rm -rf "${STAGE_DIR}/.ssh" | ||
| cp -R "${HOST_CONFIG_DIR}/.ssh" "${STAGE_DIR}/.ssh" | ||
| else | ||
| mkdir -p "${STAGE_DIR}/.ssh" | ||
| fi |
| # certificates. Sockets, locks, sshcontrol and host-specific agent config | ||
| # are excluded — the container never runs its own agent. | ||
| $GpgIncludes = @('pubring.kbx', 'pubring.gpg', 'trustdb.gpg', 'common.conf') | ||
| $GpgIncludeDirs = @('openpgp-revocs.d', 'private-keys-v1.d', 'public-keys.d') |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Windows host support to the existing YubiKey SSH/GPG devcontainer relay. Docker Desktop on Windows cannot forward Windows named pipes or GnuPG's emulated sockets into containers, so the same TCP-relay architecture used for macOS is implemented in pure PowerShell.
Branch
feat/devcontainer-windows-yubikeyFiles changed
.devcontainer/scripts/start-agent-relay.ps1— new TCP relay: SSH agent via\\.\pipe\openssh-ssh-agentnamed pipe (port 9999), GPG agent and scdaemon via GnuPG socket-emulation files (ports 9998/9997). Uses one runspace per listener and per connection;PipeOptions.Asynchronous+Task.WaitAnyto avoid wedging gpg-agent's pipe server..devcontainer/scripts/install-agent-relay.ps1— new installer: registers a per-user Scheduled Task (falls back to a hidden Startup-folder launcher when policy blocks task creation), stages%USERPROFILE%\.sshand%USERPROFILE%\.gitconfiginto.devcontainer/host-config/, and snapshots%APPDATA%\gnupgpublic key material into.devcontainer/gnupg-host/for the container..devcontainer/scripts/devcontainer-agent-bridge.sh— falls back to the workspace gnupg snapshot when/root/.gnupghas no keyring (Windows layout)..devcontainer/scripts/initialize-host.sh— Unix/WSL2 staging script: copies/symlinks host~/.sshand~/.gitconfiginto.devcontainer/host-config/before the container starts. On WSL2 it resolves the Windows user profile so the Windows-side SSH config is used..devcontainer/scripts/initialize-host.ps1— native Windows staging script: snapshots host~/.sshand~/.gitconfiginto.devcontainer/host-config\; now treats CI-style.gitconfigdirectory stubs as empty config files, falls back toHOMEwhenUSERPROFILEis unavailable, and copies SSH entries explicitly for cross-platform reliability..devcontainer/devcontainer.json— uses a single spec-compliant UnixinitializeCommandand keeps mounts pointed at the staged.devcontainer/host-config/directory. Windows host-config staging now happens through the installer instead of a lifecycle object..devcontainer/README.md— Windows setup section and updated YubiKey/GPG support description, including that the installer stages.sshand.gitconfig..gitignore— ignores.devcontainer/gnupg-host/and.devcontainer/host-config/..github/workflows/lint-and-test.yml— creates a file stub for~/.gitconfigin CI instead of a directory, and adds apowershellshim on Ubuntu runners.Why
The devcontainer previously only supported YubiKey forwarding on macOS. This enables the same workflow on Windows hosts without requiring Python or admin rights.
Testing
Live-tested on Windows 11 with Gpg4win 2.5.21 and a YubiKey:
ssh-rsa cardno:...).OK Pleased to meet you).OK GNU Privacy Guard's Smartcard server ready).initialize-host.ps1staging script was run locally and produced the expected.devcontainer/host-config/.sshand.gitconfigentries..gitconfigis a directory stub, matching the CI mount-stub behavior.HOMEis set, matching the Linux CI environment.~/.gitconfigis created as a file, preventing devcontainer copy failures in GitHub Actions.Notes / limitations
powershell -NoProfile -ExecutionPolicy Bypass -File .devcontainer\scripts\install-agent-relay.ps1 installafter changing SSH/Git config or adding new GPG keys.127.0.0.1only; the container reaches it viahost.docker.internal..devcontainer/host-config/is git-ignored and regenerated by the Unix initialize command or the Windows installer.