🛰️ export from upstream (251518c)
This commit is contained in:
+224
@@ -0,0 +1,224 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user