forked from wrenn/wrenn
feat: rewrite envd guest agent in Rust (envd-rs)
Complete Rust rewrite of the Go envd guest daemon that runs as PID 1 inside Firecracker microVMs. Feature-complete across all 8 phases: - Health, metrics, and env var endpoints - Crypto (SHA-256/512, HMAC), auth (secure token, signing), init/snapshot - Connect RPC via connectrpc + buffa (process + filesystem services) - File transfer (GET/POST /files) with gzip, multipart, chown, ENOSPC - Port subsystem (/proc/net/tcp scanner, socat forwarder) - Cgroup2 manager with noop fallback - Snapshot/restore lifecycle (conntracker, port subsystem stop/restart) - SIGTERM graceful shutdown, --cmd initial process spawn - MMDS metadata polling for Firecracker mode 42 source files, ~4200 LOC, 4.1MB stripped release binary. Makefile updated: build-envd now targets Rust (musl static), build-envd-go preserved for Go builds.
This commit is contained in:
141
envd-rs/README.md
Normal file
141
envd-rs/README.md
Normal file
@ -0,0 +1,141 @@
|
||||
# envd (Rust)
|
||||
|
||||
Wrenn guest agent daemon — runs as PID 1 inside Firecracker microVMs. Provides process management, filesystem operations, file transfer, port forwarding, and VM lifecycle control over Connect RPC and HTTP.
|
||||
|
||||
Rust rewrite of `envd/` (Go). Drop-in replacement — same wire protocol, same endpoints, same CLI flags.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Rust 1.88+ (required by `connectrpc` 0.3.3)
|
||||
- `protoc` (protobuf compiler, for proto codegen at build time)
|
||||
- `musl-tools` (for static linking)
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt install musl-tools protobuf-compiler
|
||||
|
||||
# Rust musl target
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
### Static binary (production — what goes into the rootfs)
|
||||
|
||||
```bash
|
||||
cd envd-rs
|
||||
ENVD_COMMIT=$(git rev-parse --short HEAD) \
|
||||
cargo build --release --target x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
Output: `target/x86_64-unknown-linux-musl/release/envd`
|
||||
|
||||
Verify static linking:
|
||||
|
||||
```bash
|
||||
file target/x86_64-unknown-linux-musl/release/envd
|
||||
# should say: "statically linked"
|
||||
|
||||
ldd target/x86_64-unknown-linux-musl/release/envd
|
||||
# should say: "not a dynamic executable"
|
||||
```
|
||||
|
||||
### Debug binary (dev machine, dynamically linked)
|
||||
|
||||
```bash
|
||||
cd envd-rs
|
||||
cargo build
|
||||
```
|
||||
|
||||
Run locally (outside a VM):
|
||||
|
||||
```bash
|
||||
./target/debug/envd --isnotfc --port 49983
|
||||
```
|
||||
|
||||
### Via Makefile (from repo root)
|
||||
|
||||
```bash
|
||||
make build-envd # static musl release build
|
||||
make build-envd-go # Go version (for comparison)
|
||||
```
|
||||
|
||||
## CLI Flags
|
||||
|
||||
```
|
||||
--port <PORT> Listen port [default: 49983]
|
||||
--isnotfc Not running inside Firecracker (disables MMDS, cgroups)
|
||||
--version Print version and exit
|
||||
--commit Print git commit and exit
|
||||
--cmd <CMD> Spawn a process at startup (e.g. --cmd "/bin/bash")
|
||||
--cgroup-root <PATH> Cgroup v2 root [default: /sys/fs/cgroup]
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### HTTP
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|---------------------|--------------------------------------|
|
||||
| GET | `/health` | Health check, triggers post-restore |
|
||||
| GET | `/metrics` | System metrics (CPU, memory, disk) |
|
||||
| GET | `/envs` | Current environment variables |
|
||||
| POST | `/init` | Host agent init (token, env, mounts) |
|
||||
| POST | `/snapshot/prepare` | Quiesce before Firecracker snapshot |
|
||||
| GET | `/files` | Download file (gzip, range support) |
|
||||
| POST | `/files` | Upload file(s) via multipart |
|
||||
|
||||
### Connect RPC (same port)
|
||||
|
||||
| Service | RPCs |
|
||||
|------------|-------------------------------------------------------------------------|
|
||||
| Process | List, Start, Connect, Update, StreamInput, SendInput, SendSignal, CloseStdin |
|
||||
| Filesystem | Stat, MakeDir, Move, ListDir, Remove, WatchDir, CreateWatcher, GetWatcherEvents, RemoveWatcher |
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
42 files, ~4200 LOC Rust
|
||||
Binary: ~4 MB (stripped, LTO, musl static)
|
||||
|
||||
src/
|
||||
├── main.rs # Entry point, CLI, server setup
|
||||
├── state.rs # Shared AppState
|
||||
├── config.rs # Constants
|
||||
├── conntracker.rs # TCP connection tracking for snapshot/restore
|
||||
├─<EFBFBD><EFBFBD><EFBFBD> execcontext.rs # Default user/workdir/env
|
||||
├── logging.rs # tracing-subscriber (JSON or pretty)
|
||||
├── util.rs # AtomicMax
|
||||
├── auth/ # Token, signing, middleware
|
||||
├── crypto/ # SHA-256, SHA-512, HMAC
|
||||
├── host/ # MMDS polling, system metrics
|
||||
├── http/ # Axum handlers (health, init, snapshot, files, encoding)
|
||||
├── permissions/ # Path resolution, user lookup, chown
|
||||
├── rpc/ # Connect RPC services
|
||||
│ ├── pb.rs # Generated proto types
|
||||
│ ├── process_*.rs # Process service + handler (PTY, pipe, broadcast)
|
||||
│ ├── filesystem_*.rs # Filesystem service (stat, list, watch, mkdir, move, remove)
|
||||
│ └── entry.rs # EntryInfo builder
|
||||
├── port/ # Port subsystem
|
||||
│ ├── conn.rs # /proc/net/tcp parser
|
||||
│ ├── scanner.rs # Periodic TCP port scanner
|
||||
│ ├── forwarder.rs # socat-based port forwarding
|
||||
│ └── subsystem.rs # Lifecycle (start/stop/restart)
|
||||
└── cgroups/ # Cgroup v2 manager (pty/user/socat groups)
|
||||
```
|
||||
|
||||
## Updating the rootfs
|
||||
|
||||
After building the static binary, copy it into the rootfs:
|
||||
|
||||
```bash
|
||||
bash scripts/update-debug-rootfs.sh [rootfs_path]
|
||||
```
|
||||
|
||||
Or manually:
|
||||
|
||||
```bash
|
||||
sudo mount -o loop /var/lib/wrenn/images/minimal.ext4 /mnt
|
||||
sudo cp target/x86_64-unknown-linux-musl/release/envd /mnt/usr/bin/envd
|
||||
sudo umount /mnt
|
||||
```
|
||||
Reference in New Issue
Block a user