Skip to content

Commit a9cc91c

Browse files
committed
chore(local-mounts): 🔧 update SSH agent detection and improve README
- enhance SSH agent forwarding with runtime detection - update README with clearer instructions and features - bump version to 1.0.7 in devcontainer-feature.json
1 parent c433455 commit a9cc91c

4 files changed

Lines changed: 113 additions & 44 deletions

File tree

src/local-mounts/README.md

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Mounts local Git, SSH, GPG, and npm configuration files into the devcontainer fo
66

77
- **Git configuration**: Your `.gitconfig` is automatically mounted
88
- **SSH keys**: Access your SSH keys for Git operations and remote connections
9-
- **SSH agent forwarding (stable)**: Uses `~/.ssh/agent.sock` to avoid dynamic socket path issues
9+
- **SSH agent forwarding**: Runtime detection with fallback chain (stable socket → VS Code native → legacy)
1010
- **GPG keys**: Sign commits with your GPG keys
1111
- **npm authentication**: Your `.npmrc` for private registry access
1212
- **Post-start verification**: Validates mounted content and reports issues
@@ -49,26 +49,60 @@ This feature mounts host files/directories into the container. Docker bind mount
4949
- Docker running
5050
- Host paths to mount must exist (for example: `~/.gitconfig`, `~/.ssh`, `~/.gnupg`, `~/.npmrc`)
5151

52-
### Required for SSH agent forwarding
52+
### SSH agent forwarding
5353

54-
- An SSH agent must be running on the host
55-
- Recommended: expose a **stable** socket at `~/.ssh/agent.sock`
54+
SSH agent forwarding works **out of the box** via VS Code's native mechanism — no extra configuration needed.
5655

57-
Check on host:
56+
For **optimal reliability** (especially across container rebuilds and reconnections), you can optionally configure a stable socket on your host:
57+
58+
**macOS / Linux (zsh)** — add to `~/.zshrc`:
5859

5960
```bash
60-
echo "$SSH_AUTH_SOCK"
61-
test -S "$SSH_AUTH_SOCK" && echo OK || echo MISSING
62-
ssh-add -l
61+
# Stable SSH agent socket (optional, recommended for devcontainers)
62+
export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"
63+
if [[ ! -S "$SSH_AUTH_SOCK" ]]; then
64+
rm -f "$SSH_AUTH_SOCK"
65+
eval "$(ssh-agent -a "$SSH_AUTH_SOCK")" >/dev/null
66+
ssh-add 2>/dev/null
67+
fi
68+
```
69+
70+
**Linux (bash)** — add to `~/.bashrc`:
6371

64-
# Recommended stable socket for this feature
72+
```bash
73+
# Stable SSH agent socket (optional, recommended for devcontainers)
74+
export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"
75+
if [ ! -S "$SSH_AUTH_SOCK" ]; then
76+
rm -f "$SSH_AUTH_SOCK"
77+
eval "$(ssh-agent -a "$SSH_AUTH_SOCK")" > /dev/null
78+
ssh-add 2>/dev/null
79+
fi
80+
```
81+
82+
**macOS with Keychain** — add to `~/.zshrc`:
83+
84+
```bash
6585
export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"
6686
if [[ ! -S "$SSH_AUTH_SOCK" ]]; then
67-
rm -f "$SSH_AUTH_SOCK"
68-
eval "$(ssh-agent -a "$SSH_AUTH_SOCK")" >/dev/null
87+
rm -f "$SSH_AUTH_SOCK"
88+
eval "$(ssh-agent -a "$SSH_AUTH_SOCK")" >/dev/null
89+
ssh-add --apple-use-keychain 2>/dev/null
6990
fi
7091
```
7192

93+
After adding the snippet, reload your shell (`source ~/.zshrc`) and verify:
94+
95+
```bash
96+
echo "$SSH_AUTH_SOCK" # Should show ~/.ssh/agent.sock
97+
ssh-add -l # Should list your keys
98+
```
99+
100+
The feature detects the socket at **runtime** with this priority:
101+
102+
1. **Stable socket** (`/tmp/local-mounts/.ssh/agent.sock`) — if exposed on the host
103+
2. **VS Code native forwarding** — automatic, works out of the box
104+
3. **Legacy `/ssh-agent`** — backward compatibility
105+
72106
If one of these host paths does not exist, container startup can fail with a bind-mount error.
73107

74108
## Options
@@ -93,7 +127,7 @@ The feature automatically configures these environment variables:
93127

94128
| Variable | Value | Purpose |
95129
|----------|-------|---------|
96-
| `SSH_AUTH_SOCK` | `/tmp/local-mounts/.ssh/agent.sock` | SSH agent socket forwarding |
130+
| `SSH_AUTH_SOCK` | *(set at runtime via `/etc/profile.d/`)* | SSH agent socket — detected with fallback chain |
97131
| `GPG_TTY` | `/dev/pts/0` | GPG tty for signing |
98132

99133
## How It Works
@@ -145,24 +179,28 @@ ls -la ~/.gitconfig
145179
git config --global user.name # Should show your name
146180
```
147181

148-
### SSH keys not working
182+
### SSH agent not forwarded (Permission denied)
149183

150-
1. Ensure SSH agent is running on host:
151-
```bash
152-
ssh-add -l # Should list your keys
153-
```
184+
If `git fetch` or `ssh -T git@github.com` fails with `Permission denied (publickey)`:
154185

155-
2. Prefer a stable socket path on host:
186+
1. Check the socket inside the container:
156187
```bash
157-
export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"
188+
echo "$SSH_AUTH_SOCK"
189+
test -S "$SSH_AUTH_SOCK" && echo "OK" || echo "MISSING"
190+
ssh-add -l
158191
```
159192

160-
3. Inside container, verify:
193+
2. If the socket is missing, check VS Code's native forwarding on the host:
161194
```bash
162-
env | grep SSH_AUTH_SOCK
163-
ssh-add -l # Should work
195+
# On host
196+
ssh-add -l # Must show keys
197+
echo "$SSH_AUTH_SOCK" # Must point to a valid socket
164198
```
165199
200+
3. Optionally configure a stable socket on the host (see SSH section above).
201+
202+
4. Rebuild the container after fixing the host configuration.
203+
166204
### GPG keys not found
167205
168206
1. Verify GPG is set up locally:
@@ -188,7 +226,8 @@ The `install.sh` script verifies all mounts and provides clear feedback on what'
188226

189227
## Version History
190228

191-
- **v1.0.5**: Removed fragile direct `$SSH_AUTH_SOCK` bind mount and switched to stable `~/.ssh/agent.sock` strategy
229+
- **v1.0.7**: Replaced static `containerEnv` SSH_AUTH_SOCK with runtime detection via `/etc/profile.d/` — preserves VS Code native forwarding as fallback
192230
- **v1.0.6**: Added `username` option with mount staging (`/tmp/local-mounts`) and sync to `/home/<username>`
231+
- **v1.0.5**: Removed fragile direct `$SSH_AUTH_SOCK` bind mount and switched to stable `~/.ssh/agent.sock` strategy
193232
- **v1.0.4**: Fixed `.npmrc` mounting with robust fallback verification
194233
- **v1.0.3**: Initial release

src/local-mounts/devcontainer-feature.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"id": "local-mounts",
3-
"version": "1.0.6",
3+
"version": "1.0.7",
44
"name": "Local Development Files Mount",
5-
"description": "Mounts local Git, SSH, GPG, and npm configuration files into the devcontainer with support for custom usernames. Uses a stable SSH socket strategy to avoid dynamic bind-mount failures.",
5+
"description": "Mounts local Git, SSH, GPG, and npm configuration files into the devcontainer with support for custom usernames. Uses runtime SSH agent detection with fallback to VS Code native forwarding.",
66
"documentationURL": "https://github.com/helpers4/devcontainer/tree/main/features/local-mounts",
77
"options": {
88
"username": {
@@ -34,8 +34,7 @@
3434
}
3535
],
3636
"containerEnv": {
37-
"GPG_TTY": "/dev/pts/0",
38-
"SSH_AUTH_SOCK": "/tmp/local-mounts/.ssh/agent.sock"
37+
"GPG_TTY": "/dev/pts/0"
3938
},
4039
"installsAfter": [
4140
"ghcr.io/devcontainers/features/common-utils"

src/local-mounts/install.sh

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -153,30 +153,52 @@ _check_mount_status "${TARGET_HOME}" ".gitconfig" ".gitconfig" || true
153153

154154
echo ""
155155

156-
# Test SSH agent forwarding
156+
# ============================================================================
157+
# SSH_AUTH_SOCK: Runtime detection with fallback chain
158+
# ============================================================================
159+
# containerEnv is static and overrides VS Code's native SSH forwarding.
160+
# Instead, detect at shell startup time with proper fallback.
161+
# Priority: 1) Stable host socket 2) VS Code native forwarding 3) Legacy /ssh-agent
162+
163+
cat > /etc/profile.d/local-mounts-ssh.sh << 'PROFILE_EOF'
164+
# local-mounts: SSH agent socket detection (runtime)
165+
_LOCAL_MOUNTS_SSH_SOCK="/tmp/local-mounts/.ssh/agent.sock"
166+
167+
if [ -S "$_LOCAL_MOUNTS_SSH_SOCK" ]; then
168+
# Stable host socket is mounted and active
169+
export SSH_AUTH_SOCK="$_LOCAL_MOUNTS_SSH_SOCK"
170+
elif [ -n "$SSH_AUTH_SOCK" ] && [ -S "$SSH_AUTH_SOCK" ]; then
171+
# VS Code's native SSH agent forwarding is working — keep it
172+
:
173+
elif [ -S "/ssh-agent" ]; then
174+
# Legacy external mount
175+
export SSH_AUTH_SOCK="/ssh-agent"
176+
fi
177+
178+
unset _LOCAL_MOUNTS_SSH_SOCK
179+
PROFILE_EOF
180+
181+
chmod +x /etc/profile.d/local-mounts-ssh.sh
182+
echo "✅ SSH agent detection installed (/etc/profile.d/local-mounts-ssh.sh)"
183+
184+
# Test SSH agent forwarding at install time (informational only)
157185
STABLE_SSH_AGENT_SOCKET="${SOURCE_HOME}/.ssh/agent.sock"
158186

159187
if [ -S "$STABLE_SSH_AGENT_SOCKET" ]; then
160-
export SSH_AUTH_SOCK="$STABLE_SSH_AGENT_SOCKET"
161-
echo "✅ SSH agent forwarding is working (stable socket: $SSH_AUTH_SOCK)"
188+
echo "✅ SSH stable socket found at ${STABLE_SSH_AGENT_SOCKET}"
162189
if command -v ssh-add >/dev/null 2>&1; then
163-
if ssh-add -l >/dev/null 2>&1; then
164-
KEY_COUNT=$(ssh-add -l 2>/dev/null | wc -l)
165-
echo " - ${KEY_COUNT} SSH key(s) loaded"
166-
else
167-
echo " - No SSH keys loaded in agent"
168-
fi
190+
SSH_AUTH_SOCK="$STABLE_SSH_AGENT_SOCKET" ssh-add -l >/dev/null 2>&1 \
191+
&& echo " - SSH keys accessible via stable socket" \
192+
|| echo " - No SSH keys loaded in agent"
169193
fi
170194
elif [ -n "$SSH_AUTH_SOCK" ] && [ -S "$SSH_AUTH_SOCK" ]; then
171-
echo "✅ SSH agent forwarding is working"
195+
echo "✅ SSH agent forwarding available (VS Code native: $SSH_AUTH_SOCK)"
172196
elif [ -S "/ssh-agent" ]; then
173-
# Backward compatibility if an external config still mounts /ssh-agent
174-
export SSH_AUTH_SOCK="/ssh-agent"
175-
echo "✅ SSH agent forwarding is working (legacy socket: $SSH_AUTH_SOCK)"
197+
echo "✅ SSH agent forwarding available (legacy socket: /ssh-agent)"
176198
elif [ -d "${TARGET_HOME}/.ssh" ]; then
177-
echo "SSH keys directory available at ${TARGET_HOME}/.ssh"
199+
echo "ℹ️ No SSH agent socket found — VS Code native forwarding will be used at runtime"
178200
if [ -f "${TARGET_HOME}/.ssh/id_rsa" ] || [ -f "${TARGET_HOME}/.ssh/id_ed25519" ]; then
179-
echo " - SSH keys detected"
201+
echo " - SSH keys detected in ${TARGET_HOME}/.ssh"
180202
fi
181203
else
182204
echo "ℹ️ No SSH configuration detected (optional)"

test/local-mounts/test.sh

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,16 @@ for mount in "${MOUNT_POINTS[@]}"; do
4141
fi
4242
done
4343

44-
# Test 4: SSH agent socket strategy (informational)
44+
# Test 4: SSH agent runtime detection script exists
45+
PROFILE_SCRIPT="/etc/profile.d/local-mounts-ssh.sh"
46+
if [ -f "$PROFILE_SCRIPT" ]; then
47+
echo "✅ PASS: SSH agent detection script installed at ${PROFILE_SCRIPT}"
48+
else
49+
echo "❌ FAIL: SSH agent detection script not found at ${PROFILE_SCRIPT}"
50+
exit 1
51+
fi
52+
53+
# Test 5: SSH agent socket strategy (informational)
4554
if [ -n "$SSH_AUTH_SOCK" ]; then
4655
echo "ℹ️ INFO: SSH_AUTH_SOCK is set to: $SSH_AUTH_SOCK"
4756
if [ -S "$SSH_AUTH_SOCK" ]; then
@@ -50,7 +59,7 @@ if [ -n "$SSH_AUTH_SOCK" ]; then
5059
echo "⚠️ WARN: SSH_AUTH_SOCK is set but is not a valid socket"
5160
fi
5261
else
53-
echo "ℹ️ INFO: SSH_AUTH_SOCK not set (SSH agent forwarding optional)"
62+
echo "ℹ️ INFO: SSH_AUTH_SOCK not set (will be resolved at shell startup via profile.d)"
5463
fi
5564

5665
STABLE_SOCKET="/tmp/local-mounts/.ssh/agent.sock"

0 commit comments

Comments
 (0)