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
105 changes: 105 additions & 0 deletions .github/workflows/build-vm-driver.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,88 @@ 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
dev-shell: .#devShells.aarch64-darwin.default
cargo-version: ${{ inputs.cargo-version }}
image-tag: ${{ inputs.image-tag }}
checkout-ref: ${{ inputs.checkout-ref }}
secrets: inherit

helper-runtime:
name: helper runtime (${{ matrix.arch }})
strategy:
matrix:
include:
- arch: x86_64
docker_arch: amd64
runner: linux-amd64-cpu8
- arch: aarch64
docker_arch: arm64
runner: linux-arm64-cpu8
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs['checkout-ref'] || github.sha }}

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

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

- name: Assemble trusted helper runtime
shell: bash
run: |
binary_dir="deploy/docker/.build/prebuilt-binaries/${{ matrix.docker_arch }}"
install -d "$binary_dir" artifacts
install -m 0555 sandbox/openshell-sandbox "$binary_dir/openshell-sandbox"
install -m 0555 supervisor/openshell-supervisor "$binary_dir/openshell-supervisor"

image="openshell-vm-helper-runtime:${{ matrix.docker_arch }}-${GITHUB_RUN_ID}"
container=""
cleanup() {
if [ -n "$container" ]; then docker rm -f "$container" >/dev/null 2>&1 || true; fi
docker image rm "$image" >/dev/null 2>&1 || true
}
trap cleanup EXIT

docker build \
--build-arg "TARGETARCH=${{ matrix.docker_arch }}" \
--file deploy/docker/Dockerfile.supervisor \
--tag "$image" \
.
container="$(docker create "$image")"
docker cp "$container:/openshell-runtime" - \
| zstd -19 -T0 -o artifacts/openshell-runtime.tar.zst
test -s artifacts/openshell-runtime.tar.zst

- name: Upload trusted helper runtime
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: openshell-vm-helper-runtime-${{ matrix.arch }}
path: artifacts/openshell-runtime.tar.zst
if-no-files-found: error

build:
name: openshell-driver-vm (${{ matrix.triple }})
needs: [helper-runtime, host-supervisor-macos]
strategy:
matrix:
include:
Expand Down Expand Up @@ -64,15 +144,40 @@ jobs:
name: openshell-sandbox-${{ matrix.arch }}-unknown-linux-musl
path: sandbox

- name: Download openshell-supervisor
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openshell-supervisor-${{ matrix.arch }}-unknown-linux-musl
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 trusted helper runtime
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openshell-vm-helper-runtime-${{ matrix.arch }}
path: helper-runtime

- 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"
cp helper-runtime/openshell-runtime.tar.zst "$compressed_dir/"

- name: Build openshell-driver-vm
uses: ./.github/actions/build-rust-binary
Expand Down
29 changes: 20 additions & 9 deletions .github/workflows/release-vm-kernel.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
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
# workflows when building the openshell-driver-vm binary.
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
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' }}
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' }}
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' }}
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' }}
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' }} -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
3 changes: 3 additions & 0 deletions Cargo.lock

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

11 changes: 6 additions & 5 deletions architecture/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,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
28 changes: 0 additions & 28 deletions crates/openshell-core/src/container_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,31 +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 in VM sandboxes.
///
/// A CA certificate is not secret, so unlike the credential this is staged
/// world-readable. The supervisor trusts it for the handshake with an
/// `https://` proxy and for server certificates re-signed by a
/// TLS-intercepting proxy.
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 @@ -129,9 +104,6 @@ mod tests {
VM_GUEST_SANDBOX_TOKEN_PATH,
VM_GUEST_INIT_DROPIN_DIR,
VM_GUEST_INIT_DROPIN_MANIFEST,
VM_GUEST_UPSTREAM_PROXY_AUTH_PATH,
VM_GUEST_PROXY_CA_PATH,
VM_GUEST_SUPERVISOR_ARGS_PATH,
VM_UMOCI_PATH,
VM_SANDBOX_OWNER_NORMALIZED_MARKER,
];
Expand Down
Loading
Loading