Skip to content
Open
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 @@ -176,7 +176,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
27 changes: 0 additions & 27 deletions crates/openshell-core/src/container_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,30 +66,6 @@ pub const VM_GUEST_SANDBOX_TOKEN_PATH: &str = "/opt/openshell/auth/sandbox.jwt";
pub const VM_GUEST_INIT_DROPIN_DIR: &str = "/opt/openshell/init.d";
pub const VM_GUEST_INIT_DROPIN_MANIFEST: &str = "/opt/openshell/init.d.manifest";

/// Guest path for the corporate upstream-proxy credential in VM sandboxes.
///
/// The VM driver stages the `user:pass` credential here (mode `0600`,
/// root-only) inside the per-sandbox overlay upperdir, and passes only this
/// path on the supervisor's argv. A microVM has no bind mounts or container
/// secrets, so this is the same delivery the per-sandbox JWT already uses.
pub const VM_GUEST_UPSTREAM_PROXY_AUTH_PATH: &str = "/opt/openshell/auth/upstream-proxy";

/// Guest path for the corporate proxy CA bundle staged by the VM driver.
///
/// The bundle is operator-owned but not secret. The driver validates it and
/// writes it into each sandbox overlay with mode `0644`, then passes only this
/// guest path to the supervisor.
pub const VM_GUEST_PROXY_CA_PATH: &str = "/opt/openshell/tls/proxy-ca.pem";

/// Guest path for the driver-authored supervisor argument list in VM sandboxes.
///
/// Podman and Kubernetes build the supervisor's command line directly; the VM
/// guest init script execs a fixed argv, so driver-owned arguments travel
/// through this file instead. The driver writes it into the overlay upperdir
/// on every launch — empty when it has no arguments to pass — so a sandbox
/// image can neither forge entries nor shadow the driver's copy, and the
/// guest appends exactly what it finds there and nothing else.
pub const VM_GUEST_SUPERVISOR_ARGS_PATH: &str = "/opt/openshell/supervisor-args";
pub const VM_UMOCI_PATH: &str = "/opt/openshell/bin/umoci";
pub const VM_SANDBOX_OWNER_NORMALIZED_MARKER: &str = "/opt/openshell/.sandbox-owner-normalized";

Expand Down Expand Up @@ -126,11 +102,8 @@ mod tests {
VM_GUEST_TLS_CERT_PATH,
VM_GUEST_TLS_KEY_PATH,
VM_GUEST_SANDBOX_TOKEN_PATH,
VM_GUEST_UPSTREAM_PROXY_AUTH_PATH,
VM_GUEST_PROXY_CA_PATH,
VM_GUEST_INIT_DROPIN_DIR,
VM_GUEST_INIT_DROPIN_MANIFEST,
VM_GUEST_SUPERVISOR_ARGS_PATH,
VM_UMOCI_PATH,
VM_SANDBOX_OWNER_NORMALIZED_MARKER,
];
Expand Down
Loading
Loading