225 lines
6.0 KiB
Markdown
225 lines
6.0 KiB
Markdown
# 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 `~/.ssh` directory).
|
|
- 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:
|
|
|
|
1. Scans `~/.ssh/config` (including `Include` directives) for `Host` blocks
|
|
with `User git`.
|
|
2. Runs `git remote -v` in the current directory to discover SSH remote hosts.
|
|
3. Merges the two sources and displays discovered host/key pairs.
|
|
4. Prompts you to select which keys to copy.
|
|
5. Asks whether to include `known_hosts`.
|
|
6. Populates the `sandcage-ssh` Docker volume with the selected keys and a
|
|
synthesized SSH config.
|
|
7. 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`)
|
|
|
|
```yaml
|
|
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`)
|
|
|
|
```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
|
|
`agent` user with `600` permissions.
|
|
- **Bind mode** mounts the entire `~/.ssh` directory 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-ssh` volume persists on disk until explicitly removed
|
|
(`docker volume rm sandcage-ssh`). Run `--refresh` or 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/config` has no `Host` blocks with `User 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.
|