319 lines
12 KiB
Markdown
319 lines
12 KiB
Markdown
# uvm
|
|
|
|
`uvm` is a small, local Firecracker microVM command-line manager for Ubuntu.
|
|
It creates one Firecracker process per VM, attaches it to a host TAP device,
|
|
and persists enough state to list, stop, and destroy the VM later.
|
|
|
|
This directory is a self-contained project. All UVM code, tests, packaging
|
|
metadata, launcher scripts, and documentation live here.
|
|
|
|
For architecture, lifecycle invariants, extension guidance, and contributor
|
|
workflows, see [DEVELOPER.md](DEVELOPER.md).
|
|
|
|
For end-to-end operator scenarios, CLI examples, and HTTP API calls, see
|
|
[USAGE.md](USAGE.md).
|
|
|
|
## Quick Start
|
|
|
|
From the parent repository directory, run commands from this project root:
|
|
|
|
```sh
|
|
cd uvm
|
|
./uvm.py --help
|
|
python3 -m uvm --help
|
|
```
|
|
|
|
The `pyproject.toml` file also defines an installable `uvm` console command.
|
|
The project is an application, not a reusable Python SDK.
|
|
|
|
## Requirements
|
|
|
|
- Ubuntu or another compatible Linux host
|
|
- Python 3.12 or newer
|
|
- Root access for UVM commands that read or mutate the protected VM registry
|
|
- KVM enabled and accessible at `/dev/kvm`
|
|
- An x86_64 host for the default guest assets
|
|
- Working host networking, `iproute2`, `iptables`, `e2fsprogs`, `openssl`, and
|
|
`openssh-client`
|
|
|
|
`uvm install` installs the required host packages. It validates `/dev/kvm`
|
|
before downloading or launching a microVM.
|
|
|
|
## Trusted Installation
|
|
|
|
Installation requires trusted SHA-256 digests by default. Supply digests from
|
|
an independently trusted source before running the privileged installer:
|
|
|
|
```sh
|
|
cd uvm
|
|
|
|
sudo env \
|
|
UVM_FIRECRACKER_SHA256='<sha256-of-firecracker-archive>' \
|
|
UVM_KERNEL_SHA256='<sha256-of-vmlinux>' \
|
|
UVM_ROOTFS_SHA256='<sha256-of-rootfs>' \
|
|
./uvm.py install
|
|
```
|
|
|
|
Use `--force` to redownload Firecracker and guest assets when replacing an
|
|
image or refreshing artifacts:
|
|
|
|
```sh
|
|
sudo env \
|
|
UVM_FIRECRACKER_SHA256='<sha256-of-firecracker-archive>' \
|
|
UVM_KERNEL_SHA256='<sha256-of-vmlinux>' \
|
|
UVM_ROOTFS_SHA256='<sha256-of-rootfs>' \
|
|
./uvm.py install --force
|
|
```
|
|
|
|
For local experimentation only, the old unverified quick-start behavior is
|
|
available through an explicit opt-out:
|
|
|
|
```sh
|
|
sudo env UVM_ALLOW_UNVERIFIED_DOWNLOADS=1 ./uvm.py install
|
|
```
|
|
|
|
An unverified install is recorded as such and cannot satisfy a later strict
|
|
run. Reinstall with trusted digests to establish a verified local manifest.
|
|
To create VMs from an unverified local-development install, retain
|
|
`UVM_ALLOW_UNVERIFIED_DOWNLOADS=1` on later `create` commands and on the API
|
|
server process.
|
|
|
|
## Commands
|
|
|
|
```sh
|
|
# Install after supplying the trusted checksum variables shown above.
|
|
# A bare `install` command fails under the default strict integrity policy.
|
|
|
|
# Create and boot a VM with the defaults: 1 CPU, 512 MiB RAM, and root/root.
|
|
sudo ./uvm.py create
|
|
|
|
# Request CPU and RAM explicitly. RAM defaults to MiB; B/K/M/G suffixes work.
|
|
sudo ./uvm.py create --cpu 1 --ram 1G
|
|
|
|
# Set credentials for an account that already exists in the guest image.
|
|
sudo ./uvm.py create --username root --password '<guest-password>'
|
|
|
|
# Select the guest IP. Despite the legacy flag name, this is the guest IP.
|
|
sudo ./uvm.py create --host-ip 10.42.0.10
|
|
|
|
# Show persisted VM state and observed process status.
|
|
sudo ./uvm.py list
|
|
|
|
# Connect over SSH by VM ID or guest IP.
|
|
sudo ./uvm.py ssh vm-<id>
|
|
ssh root@10.42.0.10
|
|
|
|
# Stop or remove a VM.
|
|
sudo ./uvm.py stop vm-<id>
|
|
sudo ./uvm.py destroy vm-<id>
|
|
```
|
|
|
|
SSH uses `StrictHostKeyChecking=accept-new` and a VM-ID-specific host-key
|
|
alias by default. `--insecure-host-key` restores the old no-verification
|
|
behavior for a single connection.
|
|
|
|
## Management API
|
|
|
|
Install the project dependencies before serving the API:
|
|
|
|
```sh
|
|
cd uvm
|
|
python3 -m pip install .
|
|
```
|
|
|
|
Launch the FastAPI management server with the requested CLI form:
|
|
|
|
```sh
|
|
cd uvm
|
|
export UVM_API_TOKEN='replace-with-a-long-random-secret'
|
|
sudo env UVM_API_TOKEN="$UVM_API_TOKEN" \
|
|
uvm --serve --host 127.0.0.1 --port 8000
|
|
|
|
# Equivalent when running the source checkout directly.
|
|
sudo env UVM_API_TOKEN="$UVM_API_TOKEN" \
|
|
./uvm.py --serve --host 127.0.0.1 --port 8000
|
|
```
|
|
|
|
The default bind address is `127.0.0.1` and the default port is `8000`.
|
|
OpenAPI documentation is available at `/docs` once the server is running.
|
|
|
|
The server exposes these routes:
|
|
|
|
| Method | Route | Purpose |
|
|
|---|---|---|
|
|
| `GET` | `/health` | Unauthenticated liveness response |
|
|
| `POST` | `/install` | Install or refresh host assets; body: `{ "force": false }` |
|
|
| `GET` | `/vms` | List VM records and observed status |
|
|
| `POST` | `/vms` | Create a VM; body supports `cpu`, `ram`, `guest_ip`, `username`, and `password` |
|
|
| `GET` | `/vms/{id-or-ip}` | Retrieve one VM by ID or guest IP |
|
|
| `POST` | `/vms/{id}/stop` | Stop a VM while retaining its private disk |
|
|
| `DELETE` | `/vms/{id}` | Destroy a VM and release its resources |
|
|
|
|
The API intentionally does not proxy interactive SSH. Use the returned guest
|
|
IP with `uvm ssh` or a normal SSH client.
|
|
|
|
### API Security
|
|
|
|
The server controls privileged host operations. `UVM_API_TOKEN` is required
|
|
for every API server, including loopback-only deployments. All management
|
|
routes require it in the `X-UVM-Token` header. FastAPI's informational `/docs`,
|
|
`/redoc`, and `/openapi.json` endpoints do not require a token.
|
|
|
|
A non-loopback bind additionally requires TLS certificate and key paths:
|
|
|
|
```sh
|
|
export UVM_API_TOKEN='replace-with-a-long-random-secret'
|
|
export UVM_API_TLS_CERT='/etc/uvm/api-cert.pem'
|
|
export UVM_API_TLS_KEY='/etc/uvm/api-key.pem'
|
|
sudo env \
|
|
UVM_API_TOKEN="$UVM_API_TOKEN" \
|
|
UVM_API_TLS_CERT="$UVM_API_TLS_CERT" \
|
|
UVM_API_TLS_KEY="$UVM_API_TLS_KEY" \
|
|
./uvm.py --serve --host 0.0.0.0 --port 8000
|
|
```
|
|
|
|
Send the token on management requests:
|
|
|
|
```sh
|
|
curl \
|
|
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
|
http://127.0.0.1:8000/vms
|
|
```
|
|
|
|
Do not expose this server directly to an untrusted network. For remote access,
|
|
keep TLS enabled and consider placing it behind an additional authenticated
|
|
reverse proxy.
|
|
|
|
## Guest Image Contract
|
|
|
|
The installed kernel and rootfs are templates. `create` copies the rootfs to
|
|
the VM runtime directory before Firecracker opens it read-write, so VMs do not
|
|
share a mutable root disk.
|
|
|
|
During creation, UVM updates the private ext4 disk with a password for an
|
|
existing guest account and enables OpenSSH password authentication. The
|
|
defaults are username `root` and password `root`. A custom image must provide:
|
|
|
|
- `sshd`
|
|
- `/etc/passwd`, `/etc/shadow`, and `/etc/ssh/sshd_config`
|
|
- The requested user account; UVM does not create missing users
|
|
|
|
The known public demo key bundled in Firecracker's default bionic image is
|
|
removed from each private disk. UVM also replaces the conventional RSA, ECDSA,
|
|
and Ed25519 SSH host keys in every private disk so cloned VMs have distinct
|
|
host identities. Never rely on SSH private keys baked into a shared template.
|
|
|
|
## Host Networking
|
|
|
|
By default, UVM manages:
|
|
|
|
```text
|
|
Network: 10.42.0.0/24
|
|
Gateway: 10.42.0.1
|
|
Bridge: uvm0
|
|
TAPs: uvm-<unique suffix>
|
|
```
|
|
|
|
It creates the bridge when absent, validates an existing bridge before using
|
|
it, enables IPv4 forwarding, adds an egress masquerade rule, and adds matching
|
|
forward rules for return traffic. Guest IPs, gateway, bridge name, and guest
|
|
boot netmask derive from the configured CIDR.
|
|
|
|
## Persistent State
|
|
|
|
The default data directory is `/var/lib/uvm`:
|
|
|
|
```text
|
|
/var/lib/uvm/
|
|
├── bin/ # Firecracker and Jailer binaries
|
|
├── images/ # Kernel and rootfs templates
|
|
├── vms/<vm-id>/ # Per-VM disk, config, log, and API socket
|
|
├── integrity.json # Download/build checksums and verification provenance
|
|
└── state.json # Root-only VM inventory and guest credentials
|
|
```
|
|
|
|
State writes are locked and atomic. Lifecycle operations share an additional
|
|
operation lock so concurrent `create`, `stop`, `destroy`, and `install`
|
|
commands cannot overwrite each other's state or binaries.
|
|
|
|
The registry stores guest usernames and passwords in plaintext, so `state.json`
|
|
is mode `0600`. Treat it as a secret, do not include it in bug reports, and
|
|
replace the default `root` password immediately.
|
|
|
|
Credential provisioning applies only to VMs created by this version. Existing
|
|
VM disks are not modified automatically.
|
|
|
|
## Configuration
|
|
|
|
All optional configuration is supplied through environment variables:
|
|
|
|
| Variable | Purpose |
|
|
|---|---|
|
|
| `UVM_BASE` | Data directory; defaults to `/var/lib/uvm` |
|
|
| `UVM_NETWORK` | Guest IPv4 CIDR; defaults to `10.42.0.0/24` |
|
|
| `UVM_GATEWAY` | Bridge/guest gateway; defaults to `10.42.0.1` |
|
|
| `UVM_BRIDGE` | Linux bridge name; defaults to `uvm0` |
|
|
| `UVM_KERNEL_URL` | Guest kernel download URL |
|
|
| `UVM_ROOTFS_URL` | Guest rootfs download URL |
|
|
| `UVM_FIRECRACKER_SHA256` | Trusted Firecracker release archive SHA-256 |
|
|
| `UVM_KERNEL_SHA256` | Trusted guest kernel SHA-256 |
|
|
| `UVM_ROOTFS_SHA256` | Trusted guest rootfs SHA-256 |
|
|
| `UVM_FIRECRACKER_BINARY_SHA256` | Optional trusted local Firecracker binary SHA-256 |
|
|
| `UVM_ALLOW_UNVERIFIED_DOWNLOADS` | Set to `1` only for unverified local development |
|
|
| `UVM_API_TOKEN` | Required for every API server; sent as `X-UVM-Token` |
|
|
| `UVM_API_TLS_CERT` | TLS certificate path required for a non-loopback API bind |
|
|
| `UVM_API_TLS_KEY` | TLS private-key path required for a non-loopback API bind |
|
|
|
|
## Project Layout
|
|
|
|
```text
|
|
uvm/
|
|
├── README.md
|
|
├── pyproject.toml
|
|
├── uvm.py # Executable compatibility launcher
|
|
├── uvm/ # Private application implementation
|
|
│ ├── cli.py # argparse, terminal output, SSH execution
|
|
│ ├── app.py # Application composition root
|
|
│ ├── api_models.py # FastAPI request and response models
|
|
│ ├── config.py # Settings and filesystem layout
|
|
│ ├── domain.py # Typed VM and state models
|
|
│ ├── errors.py # Expected application failures
|
|
│ ├── integrity.py # Checksums and manifest provenance
|
|
│ ├── install.py # Host/artifact installation
|
|
│ ├── images.py # Guest templates and per-VM disks
|
|
│ ├── lifecycle.py # Create/list/stop/destroy orchestration
|
|
│ ├── network.py # IP/MAC/TAP/bridge/NAT management
|
|
│ ├── routers/ # Health, installation, and VM HTTP routers
|
|
│ ├── server.py # FastAPI application factory and Uvicorn launcher
|
|
│ ├── state.py # Locked atomic JSON persistence
|
|
│ ├── system.py # Host command and KVM adapters
|
|
│ └── firecracker/ # Firecracker config, API, and process adapters
|
|
└── tests/ # Unit tests with mocked host boundaries
|
|
```
|
|
|
|
## Testing
|
|
|
|
From the parent repository directory, run the unit suite from this project root:
|
|
|
|
```sh
|
|
cd uvm
|
|
python3 -m pip install '.[test]'
|
|
PYTHONDONTWRITEBYTECODE=1 python3 -B -m unittest discover -s tests -v
|
|
```
|
|
|
|
The unit tests do not need root, KVM, Firecracker, or host network changes.
|
|
They cover validation, state migration and locking, integrity manifests,
|
|
network command rendering, Firecracker request ordering, process safety, and
|
|
lifecycle rollback. API route tests run automatically when the FastAPI
|
|
and `httpx` test dependencies are installed.
|
|
|
|
## Current Boundaries
|
|
|
|
- Fractional CPU requests still round Firecracker vCPUs up and are advisory.
|
|
- `stop` retains the private VM disk; the current command surface does not yet
|
|
include a separate `start` command.
|
|
- Firecracker is launched directly by this local CLI. Jailer/cgroup hardening
|
|
is not implemented by the current command set.
|
|
- Run real KVM, TAP, and guest-connectivity checks on an isolated Linux host
|
|
before relying on UVM for workloads.
|