
Git Credentials Without a Shell
Last updated 2026-07-15 ·f0c46c7
You ssh into the secure-agent-pod, run git push, and it works. You open VS Code’s source control panel, click Sync, and get:
remote: Invalid username or token. Password authentication is not supported for Git operations.
fatal: Authentication failed for 'https://github.com/...'Same pod, same user, same repo.
flowchart TB
subgraph pod["Secure Agent Pod"]
pid1["PID 1<br/>entrypoint.sh<br/>envFrom → GITHUB_TOKEN"]
sshd["sshd<br/>fresh env<br/>no GITHUB_TOKEN"]
vscode["VS Code Server<br/>git plugin<br/>no shell init"]
cron["supercronic<br/>inherits PID 1 env"]
pid1 -->|"inherits env"| cron
pid1 -.->|"does NOT inherit"| sshd
sshd -.->|"spawns"| shell["interactive shell<br/>sources .bashrc<br/>reads /proc/1/environ<br/>gets GITHUB_TOKEN"]
sshd -.->|"spawns"| vscode
end
subgraph git["git operations"]
ok["git push ✓"]
fail["git push ✗"]
end
cron --> ok
shell --> ok
vscode --> fail
Where the token actually lives
Kubernetes injects env vars into the container’s PID 1 at startup. GITHUB_TOKEN is there via envFrom. Everything PID 1 spawns directly (sshd, supercronic) inherits it. But SSH sessions do not inherit PID 1’s env — OpenSSH builds a fresh environment from /etc/environment, Pluggable Authentication ModulesThe Linux framework deciding what happens at login. Where an SSH session's environment and session setup are really determined., and login shell init files.
The usual workaround — sourcing /proc/1/environ in .bashrc — only works for interactive shells. VS Code’s git plugin doesn’t source .bashrc. Neither does cron, neither does any subprocess whose parent didn’t set up the env explicitly.
The fix
Read /proc/1/environ directly from the git credential helper, skipping the env var entirely:
[credential]
helper = "!f() { echo \"username=clawdia-ai-assistant\"; echo \"password=$(tr '\\0' '\\n' < /proc/1/environ | sed -n 's/^GITHUB_TOKEN=//p')\"; }; f"Every git invocation — regardless of how it was spawned — reads the kernel’s view of PID 1’s startup environment and returns the token. No shell init files needed.
# Verify
kubectl exec -n secure-agent-pod deploy/secure-agent-pod -c kali -- \
git config --get credential.helper
kubectl exec -n secure-agent-pod deploy/secure-agent-pod -c kali -- \
bash -c 'tr "\0" "\n" < /proc/1/environ | grep -c ^GITHUB_TOKEN='Baked into the image at /opt/gitconfig, seeded to ~/.gitconfig on first boot.
Is this secure?
Strictly more secure than the env-var approach:
/proc/PID/environis kernel-enforced. Readable only by the same uid (or root) viaptrace_may_access. PID 1 and theclaudeuser both run as uid 1000.- Nothing touches disk. The token is only materialised for the duration of one
tr+sedpipeline per git call. - Smaller leakage surface. The env-var approach puts
GITHUB_TOKENinto every interactive shell’s env, where it can leak viaenv,ps e, or shell history.
Token rotation works the same: External Secrets OperatorThe operator that pulls secrets from an external store into Kubernetes Secrets. On Frank it mints short-lived GitHub App tokens, so no long-lived credential is ever committed. updates the K8s Secret, you restart the pod, PID 1 gets the new value.
Recover
Credential Helper Returns Empty Token
# Test the helper directly
kubectl exec -n secure-agent-pod deploy/secure-agent-pod -c kali -- \
bash -c 'tr "\0" "\n" < /proc/1/environ | sed -n "s/^GITHUB_TOKEN=//p"'If empty: PID 1 doesn’t have GITHUB_TOKEN in its env. Check the K8s Secret exists, the ExternalSecret is synced, and the pod has been restarted since the last rotation.
EACCES Reading /proc/1/environ
The entrypoint must run as the same uid as the user running git. If the entrypoint drops privileges, chmod the helper or run git as the pod’s startup uid.
Missteps
| What we assumed | Why it was wrong | What it cost |
|---|---|---|
Sourcing /proc/1/environ in .bashrc is sufficient for all git workflows | VS Code’s git plugin doesn’t source shell init files. Credential failures appeared only in the editor, not the terminal. | Debugging session to identify the non-interactive gap, then implemented the credential helper approach. |
The credential helper’s inline tr | sed pipeline is fragile | It survived rotation testing and is baked into the CI-build image. The kernel guarantees /proc/1/environ is always available and atomically read. | None — the pattern is now the canonical approach for all agent pods. |
