banger/README.md
Thales Maciel 1850904d9c
file_sync: skip nested symlinks during recursive copy
A user who sets `[[file_sync]] host = "~/.aws"` (per the README's
own example) can unintentionally copy files from outside that
directory if .aws contains symlinks. copyHostDir used os.Stat
during recursion, which transparently follows: a symlink to a
credential dir elsewhere would be recursed into, materialising
unrelated secrets inside the guest. For credential trees that's
an avoidable sprawl vector.

Switched copyHostDir's per-entry probe from os.Stat to os.Lstat
and added a default skip-with-warning branch for ModeSymlink.
Files and dirs at the SAME level copy as before; symlinks (both
file and directory flavours) surface a "file_sync skipped
symlink (would escape the requested tree)" warn log and are
otherwise omitted.

Top-level entry paths still follow — the Stat in runFileSync is
unchanged. The user explicitly named that path, so resolving
"~/.aws" through a symlink out of $HOME is on them.

Tests:
- TestRunFileSyncSkipsNestedSymlinks — builds a synced dir with
  both a file symlink and a directory symlink pointing outside
  the tree; asserts real files copy, symlinks do not materialise
  anywhere in the guest mount, and each skipped symlink surfaces
  a warn log entry.

README updated with a one-line note about the skip behaviour so
users know to expect it rather than chasing "why didn't my file
show up."

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 14:11:58 -03:00

246 lines
7.9 KiB
Markdown

# banger
One-command development sandboxes on Firecracker microVMs.
## Quick start
```bash
make install
banger vm run --name sandbox
```
That's it. `banger vm run` auto-pulls the default golden image (Debian
bookworm with systemd, sshd, Docker CE, git, jq, mise, and the usual
dev tools) and kernel, creates a VM, starts it, and drops you into
an interactive ssh session. First run takes a couple minutes (bundle
download); subsequent `vm run`s are seconds.
## Requirements
- **x86_64 / amd64 Linux** — arm64 is not supported today. The companion
binaries, the published kernel catalog, and the OCI import path all
assume `linux/amd64`. `banger doctor` surfaces this as a failing
check on other architectures.
- `/dev/kvm`
- `sudo`
- Firecracker on `PATH`, or `firecracker_bin` set in config
- host tools checked by `banger doctor`
## Build + install
```bash
make install
```
Installs `banger` (CLI), `bangerd` (daemon, auto-starts on first
CLI call), and `banger-vsock-agent` (companion, under
`$PREFIX/lib/banger/`).
To remove the binaries (and stop the daemon):
```bash
make uninstall
```
User data stays in place — the target prints the paths so you can
`rm -rf` them if you want a full purge:
- `~/.config/banger/` — config, managed SSH keys
- `~/.local/state/banger/` — VM records, rootfs images, kernels, daemon DB/log
- `~/.cache/banger/` — OCI layer cache
### Shell completion
`banger` ships completion scripts for bash, zsh, fish, and
powershell. Tab-completion covers subcommands, flags, and live
resource names (VM, image, kernel) looked up from the
daemon. With the daemon down, resource completion silently
returns nothing — no file-completion fallback.
```bash
# bash (system-wide)
banger completion bash | sudo tee /etc/bash_completion.d/banger
# zsh (user-local; ~/.zfunc must be on fpath)
banger completion zsh > ~/.zfunc/_banger
# fish
banger completion fish > ~/.config/fish/completions/banger.fish
```
`banger completion --help` shows the shell-specific loading
recipes.
## `vm run`
One command, four common shapes:
```bash
banger vm run # bare sandbox — drops into ssh
banger vm run ./repo # workspace at /root/repo — drops into ssh
banger vm run ./repo -- make test # workspace + run command, exits with its status
banger vm run --rm -- script.sh # ephemeral: VM is deleted on exit
```
- **Bare mode** gives you a clean shell.
- **Workspace mode** (path given) copies the repo's git-tracked files
into `/root/repo` and kicks off a best-effort `mise` tooling
bootstrap from the repo's `.mise.toml` / `.tool-versions`. Log:
`/root/.cache/banger/vm-run-tooling-<repo>.log`. Untracked files
(including local `.env`, scratch notes, credentials that aren't
gitignored) are skipped by default — pass `--include-untracked` to
also ship them. Pass `--dry-run` to print the exact file list and
exit without creating a VM.
- **Command mode** (`-- <cmd>`) runs the command in the guest; exit
code propagates through `banger`.
Disconnecting from an interactive session leaves the VM running. Use
`vm stop` / `vm delete` to clean up — or pass `--rm` so the VM
auto-deletes once the session / command exits.
`--branch`, `--from`, `--include-untracked`, and `--dry-run` apply
only to workspace mode. `--rm` skips the delete when the initial ssh
wait times out, so a wedged sshd leaves the VM alive for `banger vm
logs` inspection.
## Hostnames: reaching `<vm>.vm`
banger's daemon runs a DNS server for the `.vm` zone. With host-side
DNS routing you can `curl http://sandbox.vm:3000` from anywhere on
the host — no copy-pasting guest IPs. On systemd-resolved hosts this
is auto-wired; everywhere else there's a short recipe. See
[`docs/dns-routing.md`](docs/dns-routing.md).
### Optional: `ssh <name>.vm` shortcut
`banger vm ssh <name>` works out of the box. If you'd also like plain
`ssh sandbox.vm` from any terminal (using banger's key + known_hosts),
opt in:
```bash
banger ssh-config --install # adds `Include ~/.config/banger/ssh_config`
# to ~/.ssh/config in a marker-fenced block
banger ssh-config --uninstall # reverse it
banger ssh-config # show the include line to paste manually
```
banger never touches `~/.ssh/config` on its own — the daemon keeps its
file fresh at `~/.config/banger/ssh_config`; whether and how it's
pulled into your SSH config is up to you.
## Image catalog
`banger image pull <name>` fetches a pre-built bundle from the
embedded catalog. `vm run` calls this for you on demand.
Today's catalog:
| Name | What it is |
|------|-----------|
| `debian-bookworm` | Debian 12 slim + sshd + docker + dev tools |
See [`docs/image-catalog.md`](docs/image-catalog.md) for the bundle
format and how to publish a new entry.
## Config
Config lives at `~/.config/banger/config.toml`. All keys optional.
Most commonly set:
- `default_image_name` — image used when `--image` is omitted
(default `debian-bookworm`, auto-pulled from the catalog if not
local).
- `ssh_key_path` — host SSH key. If unset, banger creates
`~/.local/state/banger/ssh/id_ed25519`. Accepts absolute paths or
`~/`-anchored paths; `~/foo` expands against `$HOME`. Relative
paths are rejected at config load.
- `firecracker_bin` — override the auto-resolved `PATH` lookup.
Full key list in `internal/config/config.go`.
### `vm_defaults` — sizing for new VMs
Every `vm run` / `vm create` prints a `spec:` line up front showing
the vCPU, RAM, and disk the VM will get. When the flags aren't set,
those values come from:
1. `[vm_defaults]` in config (if present, wins).
2. Host-derived heuristics (roughly: `cpus/4` capped at 4, `ram/8`
capped at 8 GiB, 8 GiB disk).
3. Built-in constants (floor).
`banger doctor` prints the effective defaults with provenance.
```toml
[vm_defaults]
vcpu = 4
memory_mib = 4096
disk_size = "16G"
```
All keys optional — omit whichever you want banger to decide.
### `file_sync` — host → guest file copies
```toml
[[file_sync]]
host = "~/.aws" # whole directory, recursive
guest = "~/.aws"
[[file_sync]]
host = "~/.config/gh/hosts.yml"
guest = "~/.config/gh/hosts.yml"
[[file_sync]]
host = "~/bin/my-script"
guest = "~/bin/my-script"
mode = "0755" # optional; default 0600 for files
```
Runs at `vm create` time. Each entry copies `host``guest` onto
the VM's work disk (mounted at `/root` in the guest). Guest paths
must live under `~/` or `/root/...`. Default is no entries — add the
ones you want. Symlinks encountered while recursing into a synced
directory are skipped with a warning — they'd otherwise leak files
from outside the named tree (e.g. a symlink inside `~/.aws` pointing
to an unrelated credential dir).
## Advanced
The common path is `vm run`. Power-user flows (`vm create`, OCI pull
for arbitrary images, `image register`, manual workspace prepare) are
documented in [`docs/advanced.md`](docs/advanced.md).
## Security
Guest VMs are single-user development sandboxes, not multi-tenant
servers. Each guest's sshd is configured with:
```
PermitRootLogin prohibit-password
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
AuthorizedKeysFile /root/.ssh/authorized_keys
```
The host SSH key is the only authentication mechanism. `StrictModes`
is on (sshd's default); banger normalises `/root`, `/root/.ssh`, and
`authorized_keys` perms at provisioning time so the default passes.
VMs are reachable only through the host bridge network
(`172.16.0.0/24` by default). Do not expose the bridge interface or
guest IPs to an untrusted network.
## Further reading
- [`docs/dns-routing.md`](docs/dns-routing.md) — resolving
`<vm>.vm` hostnames from the host.
- [`docs/image-catalog.md`](docs/image-catalog.md) — bundle format
and publishing.
- [`docs/kernel-catalog.md`](docs/kernel-catalog.md) — kernel
bundles.
- [`docs/oci-import.md`](docs/oci-import.md) — pulling arbitrary
OCI images.
- [`docs/advanced.md`](docs/advanced.md) — power-user flows.