6.0 KiB
SSH Key Access
Overview
Containers launched by sandcage often need SSH access to clone private repositories or push code. Since the agent user inside the container does not have access to your host SSH keys by default, sandcage provides a setup command that copies or mounts the relevant keys into the container environment.
Modes
Sandcage supports two SSH modes, configured via the ssh_mode field:
Volume mode (recommended)
ssh_mode: volume
A Docker named volume (sandcage-ssh) is created and populated with only the
keys required for your git remotes. The volume is mounted read-only at
/home/agent/.ssh inside the container. A synthesized SSH config is written
into the volume so the agent can connect to the correct hosts with the correct
keys.
Advantages:
- Only selected keys are copied (not your entire
~/.sshdirectory). - A minimal SSH config is synthesized from your host config.
- The volume persists across container restarts without re-reading host files.
- Works identically on Linux, macOS, and Windows (Docker Desktop).
Bind mode
ssh_mode: bind
Your host ~/.ssh directory is bind-mounted read-only into the container at
/home/agent/.ssh:ro. This exposes all files in that directory to the
container.
Advantages:
- Zero setup after initial configuration.
- Changes to host keys are immediately visible.
Disadvantages:
- Exposes all keys and config, not just the ones needed.
- On some platforms, file permission translation can cause issues.
None
ssh_mode: none
No SSH mount is added. Use this if you do not need SSH inside containers or handle key injection through other means.
Setup workflow
Run the interactive setup:
sandcage setup ssh
The command:
- Scans
~/.ssh/config(includingIncludedirectives) forHostblocks withUser git. - Runs
git remote -vin the current directory to discover SSH remote hosts. - Merges the two sources and displays discovered host/key pairs.
- Prompts you to select which keys to copy.
- Asks whether to include
known_hosts. - Populates the
sandcage-sshDocker volume with the selected keys and a synthesized SSH config. - Writes the configuration to the appropriate config file.
Flags
| Flag | Effect |
|---|---|
--global |
Write to ~/.sandcage/config.toml instead of project-local config |
--yes |
Accept all defaults without prompting |
--bind |
Use bind-mount mode instead of volume mode |
--refresh |
Re-populate the volume from existing config (no interactive selection) |
Examples
# Interactive setup for current project
sandcage setup ssh
# Non-interactive, global config
sandcage setup ssh --global --yes
# Legacy bind-mount mode
sandcage setup ssh --bind
# Refresh volume after adding a new key to ~/.ssh
sandcage setup ssh --refresh
Configuration
Project config (.sandcage.local.yml or .sandcage.yml)
ssh_mode: volume
ssh_keys:
- host: github.com
identity_file: ~/.ssh/id_ed25519
- host: gitea
identity_file: ~/.ssh/work_gitea
Global config (~/.sandcage/config.toml)
ssh_mode = "volume"
[[ssh_keys]]
host = "github.com"
identity_file = "~/.ssh/id_ed25519"
[[ssh_keys]]
host = "gitea"
identity_file = "~/.ssh/work_gitea"
Fields
| Field | Type | Description |
|---|---|---|
ssh_mode |
string | One of volume, bind, or none |
ssh_keys |
array | List of host/key pairs to include in the volume |
ssh_keys[].host |
string | SSH host alias (matches Host line in SSH config) |
ssh_keys[].identity_file |
string | Path to the private key (supports ~/ expansion) |
Configuration layers are merged in order: global, project, local. Later layers override earlier ones.
Refreshing keys
After adding or rotating SSH keys on the host, update the container volume:
sandcage setup ssh --refresh
This re-reads the existing ssh_keys configuration and the current
~/.ssh/config, then repopulates the sandcage-ssh Docker volume with fresh
copies.
For bind mode, no refresh is needed since the host directory is mounted directly.
Security considerations
- Volume mode copies only the selected private keys into a Docker volume.
The volume is mounted read-only into containers. Keys are owned by the
agentuser with600permissions. - Bind mode mounts the entire
~/.sshdirectory read-only. The container can read all keys and config present in that directory. - Neither mode exposes keys to other containers or Docker networks. Keys exist only on the filesystem inside the running container.
- The
sandcage-sshvolume persists on disk until explicitly removed (docker volume rm sandcage-ssh). Run--refreshor delete the volume when decommissioning keys.
Troubleshooting
Volume not found
sandcage: SSH volume not found — run 'sandcage setup ssh --refresh' to populate it
The sandcage-ssh volume does not exist or was removed. Re-run:
sandcage setup ssh --refresh
Or, if no config exists yet:
sandcage setup ssh
Permission denied inside container
If the agent cannot read keys, the volume population step may have failed or
the image does not have an agent user at the expected UID. Verify:
docker run --rm -v sandcage-ssh:/home/agent/.ssh:ro sandcage:latest ls -la /home/agent/.ssh
Private keys should be 600 and owned by agent:agent.
No git SSH hosts found
sandcage: no git SSH hosts found in remotes or ~/.ssh/config
This means:
- The current directory has no SSH git remotes (all remotes use HTTPS).
~/.ssh/confighas noHostblocks withUser git.
Add an SSH remote or configure your SSH config, then re-run setup.
Migration from legacy bind mount
If your config contains a raw ~/.ssh:/home/agent/.ssh:ro entry in mounts
without an ssh_mode field, sandcage detects this as a legacy configuration
and offers to migrate to volume mode. Accepting the migration removes the old
mount entry and writes ssh_mode: volume with discovered keys. Declining sets
ssh_mode: bind explicitly to suppress future prompts.