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
14 changes: 7 additions & 7 deletions doc/acceptance-case-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,18 @@ The adapter's README said what each proved.
| Scenario | Fate | Coverage, or why it retires |
| --- | --- | --- |
| `a_launch_takes_the_bindings_that_the_store_stages_for_its_images` | rewritten | `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_distro_binding`; `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_scratch_binding`. The adapter's staging store retires |
| `staging_verifies_the_image_and_binds_the_scratch` | rewritten | `nvx-rs/src/host/sandbox/distro/tests.rs::verification_binds_the_layer_its_manifest_and_the_policy`; `nvx-rs/src/host/sandbox/scratch/tests.rs::binds_refuse_what_is_not_a_scratch_image`; `nvx-rs/tests/security_checklist.rs::a_staged_image_that_was_replaced_fails_its_check` |
| `staging_verifies_the_image_and_binds_the_scratch` | rewritten | `nvx-rs/src/host/sandbox/distro/tests.rs::verification_binds_the_layer_its_manifest_and_the_policy`; `nvx-rs/src/host/sandbox/scratch/tests.rs::binds_refuse_what_is_not_a_scratch_image`; `nvx-rs/src/host/sandbox/distro/tests.rs::a_staged_image_that_was_replaced_fails_its_check` |
| `the_host_program_stages_images_in_the_store` | retired | The adapter's `stage` command and its record |
| `the_store_records_the_staged_images` | retired | The adapter's record of its staged images |

### `layered_launch` (16)

| Scenario | Fate | Coverage, or why it retires |
| --- | --- | --- |
| `a_launch_claims_its_scratch_image_until_it_releases_its_checked_files` | rewritten | `nvx-rs/tests/security_checklist.rs::a_scratch_image_is_claimed_by_one_launch_and_checked_again_after_a_release`; `nvx-rs/src/host/sandbox/scratch/tests.rs::a_claim_lasts_while_the_vmm_holds_the_image` |
| `a_launch_claims_its_scratch_image_until_it_releases_its_checked_files` | rewritten | `nvx-rs/src/host/sandbox/scratch/tests.rs::a_scratch_image_is_claimed_by_one_launch_and_checked_again_after_a_release`; `nvx-rs/src/host/sandbox/scratch/tests.rs::a_claim_lasts_while_the_vmm_holds_the_image` |
| `a_launch_reads_only_the_networks_that_the_plan_takes` | rewritten | `nvx-rs/tests/cli.rs::network_policies_that_the_planner_refuses_fail_before_launch`; `nvx-rs/src/host/network/tests.rs::invalid_rules_are_rejected_with_the_policy_compilers_messages` |
| `a_launch_records_what_it_launched` | retired | The adapter's launch records. Callers keep `SavedVm`: `nvx-rs/src/host/vm/tests/reattach.rs::verifying_saved_values_checks_runtime_and_process_without_opening_an_endpoint` |
| `a_launch_refuses_images_that_are_not_staged_or_changed_since` | rewritten | `nvx-rs/tests/security_checklist.rs::a_staged_image_that_was_replaced_fails_its_check`; `nvx-rs/src/host/sandbox/scratch/tests.rs::checks_fail_without_reading_when_the_image_changes`; `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_distro_binding` |
| `a_launch_refuses_images_that_are_not_staged_or_changed_since` | rewritten | `nvx-rs/src/host/sandbox/distro/tests.rs::a_staged_image_that_was_replaced_fails_its_check`; `nvx-rs/src/host/sandbox/scratch/tests.rs::checks_fail_without_reading_when_the_image_changes`; `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_distro_binding` |
| `a_launch_refuses_shares_that_it_cannot_export` | rewritten | `nvx-rs/src/host/filesystem/tests.rs::policy_paths_hide_show_and_narrow_writes`; `nvx-rs/tests/security_checklist.rs::an_export_refuses_a_read_only_path_inside_a_writable_directory` |
| `a_launch_renders_each_element_of_its_network_through_the_plan` | rewritten | `nvx-rs/src/host/config/tests.rs::cold_boot_arguments_lower_layered_roots_networks_and_exports_by_path`; `nvx-rs/src/host/network/tests.rs::proxy_urls_name_the_gateway_as_the_guest_sees_it` |
| `a_launch_shares_host_paths_inside_the_authorized_directories` | rewritten | `nvx-rs/src/host/filesystem/tests.rs::policy_paths_hide_show_and_narrow_writes`; `nvx-rs/src/host/config/tests.rs::cold_boot_arguments_lower_layered_roots_networks_and_exports_by_path` |
Expand All @@ -151,21 +151,21 @@ The adapter's README said what each proved.

| Scenario | Fate | Coverage, or why it retires |
| --- | --- | --- |
| `a_launch_that_the_hook_ends_after_its_checks_leaves_no_launch` | rewritten | A launch that fails after its checks leaves nothing of its own: `nvx-rs/src/host/vm/tests.rs::creation_rolls_back_its_log_and_never_removes_preexisting_outputs_or_inputs`; `nvx-rs/tests/security_checklist.rs::a_scratch_image_is_claimed_by_one_launch_and_checked_again_after_a_release`. The step hook retires |
| `a_launch_that_the_hook_ends_after_its_checks_leaves_no_launch` | rewritten | A launch that fails after its checks leaves nothing of its own: `nvx-rs/src/host/vm/tests.rs::creation_rolls_back_its_log_and_never_removes_preexisting_outputs_or_inputs`; `nvx-rs/src/host/sandbox/scratch/tests.rs::a_scratch_image_is_claimed_by_one_launch_and_checked_again_after_a_release`. The step hook retires |
| `images_replaced_after_the_checks_never_reach_the_vm` | retired | The checked-file handoff leaves (SL6.3, #1122): a launch names its images by path, so what takes an image's path after its check is what OpenVMM opens. Callers stage their images in a directory that only their own account can write, as the docs of `host::sandbox::distro`, `host::sandbox::scratch` and `host::seal` require, so that no other account can replace a checked image |
| `the_hook_pauses_a_launch_at_each_of_its_steps` | retired | The adapter's step hook |

### `vm_layered` (43)

| Scenario | Fate | Coverage, or why it retires |
| --- | --- | --- |
| `a_changed_image_is_refused_before_spawn` | rewritten | `nvx-rs/tests/security_checklist.rs::a_staged_image_that_was_replaced_fails_its_check`; `nvx-rs/src/host/sandbox/distro/tests.rs::the_staged_check_fails_without_reading_when_the_layer_changes`; `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_distro_binding` |
| `a_changed_image_is_refused_before_spawn` | rewritten | `nvx-rs/src/host/sandbox/distro/tests.rs::a_staged_image_that_was_replaced_fails_its_check`; `nvx-rs/src/host/sandbox/distro/tests.rs::the_staged_check_fails_without_reading_when_the_layer_changes`; `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_distro_binding` |
| `a_denied_network_lets_no_probe_through` | rewritten | A deny-all network now keeps its NIC, which lets nothing through: `nvx-rs/src/host/config/tests.rs::an_explicit_deny_all_network_keeps_its_nic`; `test-microvm --scenario directional-network-policy` |
| `a_failed_mapping_tears_the_vm_down` | rewritten | The guest's refusal, which keeps the mappings before it, latches the table and refuses the start: `nvx-rs/src/guest/mappings/table/tests.rs::a_failed_mapping_keeps_those_before_it_and_latches_the_table`; `nvx-rs/src/guest/managed_agent/dispatch/tests.rs::a_failed_mapping_keeps_refusing_workloads_through_dispatch`. The stop that the guest confirms, with a successful outcome and no record of the VM: `nvx-rs/tests/cli.rs::a_mapping_that_the_guest_refuses_leaves_no_usable_start`. The teardown of the root and its mappings: `nvx-rs/src/guest/root/tests.rs::a_teardown_unmounts_the_layers_top_first_and_reports_every_failure`; `nvx-rs/src/guest/mappings/mapper/tests/privileged.rs::a_teardown_unmounts_each_mapping_and_reports_a_busy_one` |
| `a_forward_reaches_its_guest_port` | rewritten | `test-microvm --scenario host-loopback-policy` |
| `a_proxy_only_network_lets_only_its_proxy_through` | rewritten | `test-microvm --scenario sandbox-proxy-only` |
| `a_read_only_share_inside_a_writable_one_is_refused_before_launch` | rewritten | `nvx-rs/tests/security_checklist.rs::an_export_refuses_a_read_only_path_inside_a_writable_directory` |
| `a_replaced_scratch_is_refused_before_spawn` | rewritten | `nvx-rs/tests/security_checklist.rs::a_scratch_image_is_claimed_by_one_launch_and_checked_again_after_a_release`; `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_scratch_binding` |
| `a_replaced_scratch_is_refused_before_spawn` | rewritten | `nvx-rs/src/host/sandbox/scratch/tests.rs::a_scratch_image_is_claimed_by_one_launch_and_checked_again_after_a_release`; `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_scratch_binding` |
| `a_share_inside_a_reserved_tree_is_refused_before_launch` | rewritten | `nvx-rs/tests/security_checklist.rs::an_export_refuses_guest_targets_that_are_not_canonical_or_that_a_root_reserves`; `nvx-rs/src/host/filesystem/tests.rs::reserved_targets_depend_on_the_root` |
| `a_share_over_a_reserved_tree_is_refused_before_launch` | rewritten | `nvx-rs/tests/security_checklist.rs::an_export_refuses_guest_targets_that_are_not_canonical_or_that_a_root_reserves`; `nvx-rs/src/host/filesystem/tests.rs::reserved_targets_depend_on_the_root` |
| `accountless_workload_identity_fails_its_stage` | rewritten | `test-microvm --scenario workload-identity`; `nvx-rs/src/guest/init/boot/tests.rs::account_creation_replaces_only_the_requested_unusable_identity_once` |
Expand Down Expand Up @@ -201,7 +201,7 @@ The adapter's README said what each proved.
| `published_listeners_refuse_binding_and_raw_sockets` | retired | The capability allowlist of the old guest entry leaves with the exec engine (#1087): nvx-init runs each execution as a plain process |
| `recovery_required_layer_fails_the_assembly` | rewritten | `nvx-rs/src/guest/root/tests.rs::a_layer_that_is_not_ext4_or_whose_journal_needs_a_recovery_is_refused_before_any_mount` |
| `root_profiles_enter_the_root` | retired | Per-execution namespaces, root profiles and capability sets leave with the exec engine (#1087) |
| `unstaged_images_are_refused_before_spawn` | rewritten | `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_distro_binding`; `nvx-rs/tests/security_checklist.rs::a_staged_image_that_was_replaced_fails_its_check` |
| `unstaged_images_are_refused_before_spawn` | rewritten | `nvx-rs/tests/cli.rs::start_refuses_a_configuration_without_a_valid_distro_binding`; `nvx-rs/src/host/sandbox/distro/tests.rs::a_staged_image_that_was_replaced_fails_its_check` |

### `vm_template` (8)

Expand Down
72 changes: 26 additions & 46 deletions nvx-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,14 @@ changes only when the module's purpose does.
| [`nvx::host::snapshot`](#nvxhostsnapshot) | Strict private snapshots, post-exit capture finishing, verified metadata, VM compatibility, captured NIC addresses, and time eligibility |
| [`nvx::host::config`](#nvxhostconfig) | `VmConfig` and `CaptureConfig`: pure configuration, cold-boot kernel/OpenVMM arguments, portable length checks, and capture identity |
| [`nvx::host::restore`](#nvxhostrestore) | A caller-driven restore to Ready without I/O or a clock, and its peer-verified restore-ready listener |
| [`nvx::host::artifacts`](#nvxhostartifacts) | Locating the OpenVMM executable and the guest artifacts that a VM runs, and their runtime identity |
| [`nvx::host::network`](#nvxhostnetwork) | The one lowering of a network posture into OpenVMM options, for the CLI and external orchestrators |
| [`nvx::host::network::policy`](#nvxhostnetworkpolicy) | Egress policy files and their compilation into OpenVMM's native rules |
| [`nvx::host::filesystem`](#nvxhostfilesystem) | The planner of the host paths that every NVX caller exports to the guest through OpenVMM's virtio-fs device |
| [`nvx::host::nvxs`](#nvxhostnvxs) | Compatibility paths for the host NVXS connection and its retained cause details |
| [`nvx::host::transport`](#nvxhosttransport) | A verified VM endpoint connection and the blocking host NVXS driver, with the existing timed transport |
| [`nvx::host::process`](#nvxhostprocess) | Verified references to OpenVMM processes, through which callers wait for them and end them |
| [`nvx::host::seal`](#nvxhostseal) | `FileSeal`: what the host reports about an open file from its metadata alone, without reading it |
| [`nvx::host::platform`, `report`](#nvxhostplatform-report) | Linux and Windows primitives, and OpenVMM's outcome report |
| [`nvx::host::sandbox`](#nvxhostsandbox) | The launch profile of `nvx-rs sandbox`: its layer, live shares, option checks, and bindings |
| [`nvx::host::sandbox`](#nvxhostsandbox) | The launch profile of `nvx-rs sandbox`: its live shares and option checks; its layer and bindings are private to the CLI |
| [`nvx::guest`](#nvxguest) | The programs that run in Linux guests; see [Guest binaries](#guest-binaries) |
| [`nvx::guest::mappings`](#nvxguestmappings) | `Mapping` and `MappingTable`: host mappings, and the account of a host's mapping table, for any guest init or agent |
| [`nvx::guest::nvxs`](#nvxguestnvxs) | `GuestConnection`: the guest's role of the NVXS session over a byte stream, such as the control console |
Expand Down Expand Up @@ -921,19 +919,6 @@ namespaces, it uses passwordless `sudo` only for that isolated helper, not the
test suite or build. If neither route works, the test fails; it never changes
host namespace policy or skips the identity assertions.

### `nvx::host::artifacts`

`ArtifactDir` and `Guest`: every artifact of the guests that `nvx.py` runs, the
OpenVMM executable, the kernel, the Alpine, Ubuntu, and Azure Linux initramfs
images and their package manifests, and the Ubuntu distro layer and its
manifest, in a checkout's `build/` and `openvmm/target/release/` or a release's
`guest/` and `bin/`, checked with the messages of `nvx.py`. `ArtifactPaths`
lists them all and serializes as JSON.

`ArtifactDir::runtime_identity` reads the `RuntimeIdentity` of the OpenVMM
executable, kernel, and Alpine initramfs from the install metadata, a release's
`SHA256SUMS` or a checkout's build provenance, without hashing an artifact.

### `nvx::host::network`

`NetworkPolicy` and `openvmm_arguments`, the one lowering of a network posture
Expand Down Expand Up @@ -1046,14 +1031,6 @@ holds what the process's children read once it reaped them, as the kernel
accounts them to their parent, while Windows counts the process's own reads
alone.

### `nvx::host::seal`

`FileSeal`: what the host reports about an open file from its metadata alone,
without reading it: the platform scheme, the device or volume serial number, the
file ID, the kind, the length, the modification time, and on Linux the change
time, with a versioned serde form. A seal detects accidental change, not
tampering; see [Distro bindings](#distro-bindings).

### `nvx::host::platform`, `report`

Linux and Windows primitives, and OpenVMM's outcome report. The authenticated
Expand Down Expand Up @@ -1086,8 +1063,16 @@ it, and NVX does not depend on an asynchronous I/O runtime.
### `nvx::host::sandbox`

The launch profile of `nvx-rs sandbox`, with native input validation: the
distro `SandboxLayer`, an EROFS layer or a GPT disk, the one layer that a
sandbox takes, and the workload identity that `--workload-user` names.
workload identity that `--workload-user` names, the live shares, and the checks
of the options. The image staging of the profile is private to the crate, for
the CLI (feature `cli`): `layer`'s `SandboxLayer`, the distro layer of a
sandbox, an EROFS layer or a GPT disk, the one layer that a sandbox takes;
`distro`'s `DistroBinding` of the layer, which explicit verification makes, and
its check from metadata alone; `scratch`'s `ScratchBinding` of a scratch image,
its identity, kind, and length, and its check from metadata alone, which claims
the image for one sandbox at a time; and the seals of `nvx::host`'s private
`seal`, which they record. See [Distro bindings](#distro-bindings) and
[Images by path](#images-by-path).

`mount`: `SandboxMount` requests of live shares of host directories and files,
their guest targets and the reserved ones, their access policies and owner, and
Expand All @@ -1100,14 +1085,6 @@ to shares, the systemd entrypoint check and the explicit verification of the
distro layer, the targets of `nvx-rs run` shares, and the managed exec
environment; see [Sandbox input contract](#sandbox-input-contract).

`distro`: the `DistroBinding` of a distro layer, an EROFS layer or a GPT disk,
which explicit verification makes, and its check from metadata alone, which
returns the layer open; see [Distro bindings](#distro-bindings).

`scratch`: the `ScratchBinding` of a scratch image, its identity, kind, and
length, and its check from metadata alone, which claims the image for one
sandbox at a time and returns it open.

### `nvx::guest`

The programs that run in Linux guests; see [Guest binaries](#guest-binaries).
Expand Down Expand Up @@ -1661,7 +1638,8 @@ file, that each refusal names, a fragment of its reason, and its error code.
## Distro bindings

Three separate checks guard the distro layer of a sandbox, and only explicit
verification reads it:
verification reads it. The bindings and the seals that they record are private
to the crate, for the CLI:

- `options::validate_sandbox_entrypoint` refuses the entrypoints of systemd,
which the sandbox security profile does not run. Every launch applies it,
Expand Down Expand Up @@ -1730,9 +1708,9 @@ scratch image at the paths that the VM's configuration holds. Their checks,
unchanged from its metadata alone and return it open, but what takes an image's
path after its check is what OpenVMM opens. Callers therefore stage their
images in a directory that only their own account can write, as
[Distro bindings](#distro-bindings) and the docs of `nvx::host::sandbox::distro`,
`nvx::host::sandbox::scratch`, and `nvx::host::seal` require, so that no other
account can replace a checked image. On Windows, a checked distro layer also
[Distro bindings](#distro-bindings) and the docs of the crate's private
`host::sandbox::distro`, `host::sandbox::scratch`, and `host::seal` require, so
that no other account can replace a checked image. On Windows, a checked distro layer also
denies other opens for writing or deleting the layer while it is held, and the
scratch image's guard denies deleting and renaming the image. `nvx-rs sandbox
start` and `run` check the images just before they create the VM, and hold the
Expand Down Expand Up @@ -2823,14 +2801,16 @@ python3 ../scripts/nvx.py test-guest-binaries \
The [security checklist](tests/security_checklist.rs) checks, through the
public API alone and without a VM, each security requirement that the host's
documentation states: the guest targets of an export and a read-only path
inside a writable one, the checks of a staged image and the claim of a scratch
image, which callers keep in directories that only their own account can write,
the exported objects that each start checks again, a VM's private directory and
its new private log, the exit proof of a saved VM, the owner of its endpoint,
and its recovery from the saved values, and a snapshot's strict directory. An
idle copy of the test binary stands in for a VM's process. The checks that NVX
makes of a restore need a captured snapshot, so the unit tests of
`nvx::host::vm`, `nvx::host::snapshot`, and `nvx::host::restore` check them.
inside a writable one, the exported objects that each start checks again, a
VM's private directory and its new private log, the exit proof of a saved VM,
the owner of its endpoint, and its recovery from the saved values, and a
snapshot's strict directory. An idle copy of the test binary stands in for a
VM's process. The checks that NVX makes of a restore need a captured snapshot,
so the unit tests of `nvx::host::vm`, `nvx::host::snapshot`, and
`nvx::host::restore` check them. The checks of a staged image and the claim of
a scratch image, which the CLI keeps in directories that only its own account
can write, are private to the crate, so the unit tests of its distro and
scratch bindings check them.

Two examples compile the documented workflows through the public API alone,
and nothing runs them: [`host-workflows`](examples/host-workflows.rs), a cold
Expand Down
Loading
Loading