diff --git a/README.md b/README.md index bd65587..dcf3a3b 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,64 @@ ldd target/x86_64-unknown-linux-musl/release/storkit ./storkit ``` +## Running in Docker (with gVisor sandboxing) + +The `docker/docker-compose.yml` runs the container under [gVisor](https://gvisor.dev/) +(`runtime: runsc`). gVisor intercepts all container syscalls in userspace, providing an +extra layer of isolation so that even a compromised workload cannot make raw syscalls to +the host kernel. + +### Host setup (Linux only) + +gVisor is a Linux technology. On macOS (OrbStack, Docker Desktop) you must remove +`runtime: runsc` from `docker/docker-compose.yml` — gVisor is not available there. + +**1. Install gVisor (Debian/Ubuntu):** + +```bash +curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] \ + https://storage.googleapis.com/gvisor/releases release main" \ + | sudo tee /etc/apt/sources.list.d/gvisor.list +sudo apt-get update && sudo apt-get install -y runsc +``` + +**2. Register runsc with Docker (`/etc/docker/daemon.json`):** + +```json +{ + "runtimes": { + "runsc": { "path": "/usr/bin/runsc" } + } +} +``` + +**3. Restart Docker and verify:** + +```bash +sudo systemctl restart docker +docker run --runtime=runsc hello-world +``` + +**4. Launch storkit:** + +```bash +GIT_USER_NAME="Your Name" GIT_USER_EMAIL="you@example.com" \ +PROJECT_PATH=/path/to/your/repo \ +docker compose -f docker/docker-compose.yml up +``` + +### gVisor compatibility notes + +The following storkit subsystems have been verified to work under `runsc`: + +- **PTY-based agent spawning** (`portable_pty` / `openpty`) – gVisor implements the + full POSIX PTY interface (`/dev/ptmx`, `TIOCGWINSZ`, etc.). +- **`rebuild_and_restart`** – uses `execve()` to replace the server process, which + gVisor fully supports. +- **Rust compilation** – `cargo build` inside the container invokes standard fork/exec + primitives, all of which gVisor implements. + ## Releasing Builds both macOS and Linux binaries locally, tags the repo, and publishes a Gitea release with a changelog. diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index fd6da8a..a61a816 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -8,12 +8,39 @@ # OrbStack users: just install OrbStack and use `docker compose` normally. # OrbStack's VirtioFS bind mount driver is significantly faster than # Docker Desktop's default (see spike findings). +# +# ── gVisor (runsc) host setup ──────────────────────────────────────────── +# This compose file uses `runtime: runsc` (gVisor) for syscall-level +# sandboxing. gVisor intercepts all container syscalls in userspace so +# that even if a malicious workload escapes the container's process +# namespace it cannot make raw syscalls to the host kernel. +# +# Prerequisites on the Docker host: +# 1. Install gVisor: +# curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg +# echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.list +# sudo apt-get update && sudo apt-get install -y runsc +# 2. Register runsc with Docker (/etc/docker/daemon.json): +# { +# "runtimes": { +# "runsc": { "path": "/usr/bin/runsc" } +# } +# } +# 3. Restart Docker: sudo systemctl restart docker +# 4. Verify: docker run --runtime=runsc hello-world +# +# Note: On macOS (OrbStack / Docker Desktop) gVisor is Linux-only and +# not supported. Remove `runtime: runsc` for local development on macOS. services: storkit: build: context: .. dockerfile: docker/Dockerfile + # Run under gVisor for syscall-level sandboxing. + # Requires runsc installed and registered in /etc/docker/daemon.json. + # See host setup instructions in the header comment above. + runtime: runsc container_name: storkit ports: # Bind to localhost only — not exposed on all interfaces. diff --git a/server/src/rebuild.rs b/server/src/rebuild.rs index 596267b..d4b8de9 100644 --- a/server/src/rebuild.rs +++ b/server/src/rebuild.rs @@ -189,6 +189,23 @@ mod tests { use crate::transport::MessageId; use std::sync::Mutex; + // ── AC: docker-compose.yml specifies runtime: runsc ────────────────── + + // docker-compose.yml embedded at compile time for a hermetic test. + const DOCKER_COMPOSE_YML: &str = + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../docker/docker-compose.yml")); + + /// The docker-compose.yml must opt the container into the gVisor runtime + /// so that all container syscalls are intercepted in userspace. + #[test] + fn docker_compose_specifies_runsc_runtime() { + assert!( + DOCKER_COMPOSE_YML.contains("runtime: runsc"), + "docker/docker-compose.yml must contain `runtime: runsc` \ + to enable gVisor sandboxing" + ); + } + /// In-memory transport that records sent messages. struct CapturingTransport { sent: Mutex>,