Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/build-vm-driver.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,48 @@ permissions:
contents: read

jobs:
host-supervisor-macos:
name: native host supervisor (aarch64-apple-darwin)
permissions:
contents: read
uses: ./.github/workflows/build-binaries.yml
with:
package: openshell-supervisor
binary: openshell-supervisor
triple: aarch64-apple-darwin
runner: macos-15-xlarge
cargo-version: ${{ inputs.cargo-version }}
image-tag: ${{ inputs.image-tag }}
checkout-ref: ${{ inputs.checkout-ref }}
secrets: inherit

guest-init:
name: guest init (${{ matrix.triple }})
strategy:
matrix:
include:
- arch: x86_64
triple: x86_64-unknown-linux-musl
runner: linux-amd64-cpu8
- arch: aarch64
triple: aarch64-unknown-linux-musl
runner: linux-arm64-cpu8
uses: ./.github/workflows/build-binaries.yml
with:
package: openshell-driver-vm
binary: openshell-vm-init
triple: ${{ matrix.triple }}
runner: ${{ matrix.runner }}
cargo-version: ${{ inputs.cargo-version }}
image-tag: ${{ inputs.image-tag }}
extra-cargo-flags: --no-default-features
interpreter: none
checkout-ref: ${{ inputs.checkout-ref }}
secrets: inherit

build:
name: openshell-driver-vm (${{ matrix.triple }})
needs: [guest-init, host-supervisor-macos]
strategy:
matrix:
include:
Expand Down Expand Up @@ -59,15 +99,41 @@ jobs:
name: openshell-sandbox-${{ matrix.arch }}-unknown-linux-musl
path: sandbox

- name: Download openshell-supervisor
if: endsWith(matrix.triple, '-unknown-linux-gnu')
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openshell-supervisor-${{ matrix.arch }}-unknown-linux-gnu
path: supervisor

- name: Download native macOS host supervisor
if: endsWith(matrix.triple, '-apple-darwin')
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openshell-supervisor-aarch64-apple-darwin
path: host-supervisor

- name: Download VM guest init
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openshell-vm-init-${{ matrix.arch }}-unknown-linux-musl
path: vm-init

- name: Build VM runtime
run: nix build .#vm-runtime

- name: Assemble compressed VM runtime
run: |
compressed_dir="${RUNNER_TEMP}/vm-runtime-compressed"
host_supervisor="supervisor/openshell-supervisor"
if [[ "${{ matrix.triple }}" == *-apple-darwin ]]; then
host_supervisor="host-supervisor/openshell-supervisor"
fi
install -d "$compressed_dir"
cp result/compressed/*.zst "$compressed_dir/"
zstd -19 -T1 sandbox/openshell-sandbox -o "$compressed_dir/openshell-sandbox.zst"
zstd -19 -T1 "$host_supervisor" -o "$compressed_dir/openshell-supervisor.zst"
zstd -19 -T1 vm-init/openshell-vm-init -o "$compressed_dir/openshell-vm-init.zst"

- name: Build openshell-driver-vm
uses: ./.github/actions/build-rust-binary
Expand Down
31 changes: 21 additions & 10 deletions .github/workflows/release-vm-kernel.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: Release VM Kernel

# Build custom libkrunfw (kernel firmware) + libkrun (VMM) + gvproxy for all
# Build custom libkrunfw (kernel firmware) + libkrun (VMM) for all
# supported openshell-driver-vm platforms. Artifacts are uploaded to the
# rolling "vm-runtime" GitHub Release and consumed by normal dev/tag release
# rolling "vm-runtime-capability-free" GitHub Release and consumed by normal dev/tag release
# workflows when building the openshell-driver-vm binary.
#
# The Linux kernel is compiled once on aarch64 Linux. The resulting kernel.c
Expand All @@ -16,14 +16,20 @@ name: Release VM Kernel

on:
workflow_dispatch:
inputs:
release-tag:
description: Rolling prerelease tag to create or update
required: false
default: vm-runtime-capability-free
type: string

permissions:
contents: write
packages: read

# Serialize runtime release updates.
concurrency:
group: vm-runtime-release
group: vm-runtime-release-${{ inputs.release-tag || 'vm-runtime-capability-free' }}
cancel-in-progress: false

defaults:
Expand Down Expand Up @@ -194,23 +200,28 @@ jobs:
release/vm-runtime-darwin-aarch64.tar.zst

- name: Ensure vm-runtime tag exists
env:
RELEASE_TAG: ${{ inputs.release-tag || 'vm-runtime-capability-free' }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -fa vm-runtime -m "VM Runtime Development Build" "${GITHUB_SHA}"
git push --force origin vm-runtime
git tag -fa "$RELEASE_TAG" -m "VM Runtime Development Build" "${GITHUB_SHA}"
git push --force origin "$RELEASE_TAG"

- name: Prune stale runtime assets from vm-runtime release
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
RELEASE_TAG: ${{ inputs.release-tag || 'vm-runtime-capability-free' }}
with:
script: |
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
const tag = process.env.RELEASE_TAG;
let release;
try {
release = await github.rest.repos.getReleaseByTag({ owner, repo, tag: 'vm-runtime' });
release = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
} catch (err) {
if (err.status === 404) {
core.info('No existing vm-runtime release; will create fresh.');
core.info(`No existing ${tag} release; will create fresh.`);
return;
}
throw err;
Expand All @@ -228,7 +239,7 @@ jobs:
with:
name: OpenShell VM Runtime
prerelease: true
tag_name: vm-runtime
tag_name: ${{ inputs.release-tag || 'vm-runtime-capability-free' }}
target_commitish: ${{ github.sha }}
body: |
Build of the OpenShell VM runtime artifacts used by `openshell-driver-vm`.
Expand All @@ -237,7 +248,7 @@ jobs:

### Kernel Runtime Artifacts

Pre-built kernel runtime (libkrunfw + libkrun + gvproxy + umoci) for embedding
Pre-built kernel runtime (libkrunfw + libkrun + umoci) for embedding
into the `openshell-driver-vm` binary. These are rebuilt on demand when the
kernel config or pinned dependency versions change.

Expand All @@ -250,7 +261,7 @@ jobs:
### Verify

```bash
gh release download vm-runtime -R NVIDIA/OpenShell -p vm-runtime-linux-x86_64.tar.zst
gh release download ${{ inputs.release-tag || 'vm-runtime-capability-free' }} -R NVIDIA/OpenShell -p vm-runtime-linux-x86_64.tar.zst
gh attestation verify vm-runtime-linux-x86_64.tar.zst -R NVIDIA/OpenShell
```

Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion architecture/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ Runtime layout:
contain every non-empty embedding input; the driver build fails before
packaging when an input is absent or empty.
- **Sandbox**: Alpine-based `openshell/sandbox` image containing the static
musl `/openshell-sandbox` binary and its guest bootstrap helper runtime.
musl `/openshell-sandbox` binary and its static VM guest-init helper.
Drivers stage this binary into the workload trust domain.
- **Supervisor**: Debian-based `openshell/supervisor` image containing only the
dynamically linked GNU `/openshell-supervisor` binary. GNU supervisor builds
Expand Down
11 changes: 6 additions & 5 deletions architecture/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,11 +382,12 @@ file and builds the `Proxy-Authorization: Basic` header; a credential that is
empty, contains control characters, or is not in `user:pass` form is fatal on
both sides.

The VM driver runs `openshell-supervisor` on the host. Corporate-proxy
credentials, private CA keys, policy, and gateway credentials never enter the
guest. The NIC-less guest reaches the host supervisor only through the
authenticated vsock channel; the host supervisor performs DNS and upstream
connections.
The VM driver starts `openshell-supervisor` on the host and
`openshell-sandbox` as capability-free guest PID 1. Corporate proxy arguments,
credentials, private CA keys, policy, and gateway credentials stay host-side.
Both libkrun and QEMU guests are NIC-less; intercepted workload connections
cross the authenticated vsock channel. A gateway-host proxy is addressed as
`host.openshell.internal`, which the host supervisor normalizes to `127.0.0.1`.

The Docker driver runs `openshell-supervisor` in a separate companion container.
Its private named volume contains supervisor bootstrap and channel material.
Expand Down
105 changes: 74 additions & 31 deletions crates/openshell-driver-vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,88 @@ path = "src/lib.rs"
[[bin]]
name = "openshell-driver-vm"
path = "src/main.rs"
required-features = ["compute-driver"]

[[bin]]
name = "openshell-vm-init"
path = "src/bin/openshell-vm-init.rs"

[dependencies]
openshell-core = { path = "../openshell-core", default-features = false }
openshell-otel = { path = "../openshell-otel" }
openshell-policy = { path = "../openshell-policy" }
openshell-driver-podman = { path = "../openshell-driver-podman" }
openshell-vfio = { path = "../openshell-vfio" }
openshell-isolation-interface = { path = "../openshell-isolation-interface" }
openshell-sandbox-backend = { path = "../openshell-sandbox-backend" }
openshell-otel = { path = "../openshell-otel", optional = true }
openshell-policy = { path = "../openshell-policy", optional = true }
openshell-driver-podman = { path = "../openshell-driver-podman", optional = true }
openshell-vfio = { path = "../openshell-vfio", optional = true }

bollard = { version = "0.20", features = ["ssh"] }
base64 = { workspace = true, optional = true }
bollard = { version = "0.20", features = ["ssh"], optional = true }
tokio = { workspace = true }
tonic = { workspace = true, features = ["transport"] }
tower-http = { workspace = true }
http = { workspace = true }
prost = { workspace = true }
prost-types = { workspace = true }
futures = { workspace = true }
tokio-stream = { workspace = true, features = ["net"] }
nix = { workspace = true }
clap = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
opentelemetry = { workspace = true }
opentelemetry_sdk = { workspace = true }
tracing-opentelemetry = { workspace = true }
miette = { workspace = true }
url = { workspace = true }
tonic = { workspace = true, features = ["transport"], optional = true }
tower-http = { workspace = true, optional = true }
http = { workspace = true, optional = true }
prost = { workspace = true, optional = true }
prost-types = { workspace = true, optional = true }
futures = { workspace = true, optional = true }
tokio-stream = { workspace = true, features = ["net"], optional = true }
nix = { workspace = true, optional = true }
clap = { workspace = true, optional = true }
tracing = { workspace = true, optional = true }
tracing-subscriber = { workspace = true, optional = true }
opentelemetry = { workspace = true, optional = true }
opentelemetry_sdk = { workspace = true, optional = true }
tracing-opentelemetry = { workspace = true, optional = true }
miette = { workspace = true, optional = true }
rand = { workspace = true, optional = true }
url = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true }
oci-client = "0.16"
oci-client = { version = "0.16", optional = true }
libc = "0.2"
rustix = { workspace = true }
libloading = "0.8"
tar = "0.4"
flate2 = "1"
sha2 = "0.10"
zstd = "0.13"
rustix = { workspace = true, optional = true }
libloading = { version = "0.8", optional = true }
tar = { version = "0.4", optional = true }
flate2 = { version = "1", optional = true }
sha2 = { version = "0.10", optional = true }
zstd = { version = "0.13", optional = true }

[features]
default = ["telemetry"]
default = ["compute-driver", "telemetry"]
## Build the standalone compute driver and its host runtime implementation.
compute-driver = [
"dep:base64",
"dep:bollard",
"dep:clap",
"dep:flate2",
"dep:futures",
"dep:http",
"dep:libloading",
"dep:miette",
"dep:nix",
"dep:oci-client",
"dep:openshell-otel",
"dep:openshell-policy",
"dep:openshell-driver-podman",
"dep:openshell-vfio",
"dep:opentelemetry",
"dep:opentelemetry_sdk",
"dep:polling",
"dep:prost",
"dep:prost-types",
"dep:rand",
"dep:rustix",
"dep:sha2",
"dep:tar",
"dep:tokio-stream",
"dep:tonic",
"dep:tower-http",
"dep:tracing",
"dep:tracing-opentelemetry",
"dep:tracing-subscriber",
"dep:url",
"dep:zstd",
]
## Compile in telemetry support (forwards to openshell-core/telemetry). On by
## default; build with `--no-default-features` for a telemetry-free VM driver
## that reports telemetry disabled to the sandboxes it launches.
Expand All @@ -68,7 +111,7 @@ telemetry = ["openshell-core/telemetry"]
## enabling it alongside `telemetry` is a compile error rather than a silent
## telemetry-on build. Kept in sync with `default` by
## `rust:verify:defaults-without-telemetry`.
defaults-without-telemetry = []
defaults-without-telemetry = ["compute-driver"]

[dev-dependencies]
openshell-otel-test-support = { path = "../openshell-otel-test-support" }
Expand All @@ -82,7 +125,7 @@ opentelemetry_sdk = { workspace = true, features = ["testing"] }
# nix::sys::prctl::set_pdeathsig there keeps the Linux path a single
# syscall with no helper thread.
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly"))'.dependencies]
polling = "3.11"
polling = { version = "3.11", optional = true }

[lints]
workspace = true
Loading
Loading