fix(ssh): persist sandbox host identities - #4094
quocanh261997 wants to merge 4 commits into
Conversation
Store each sandbox's Ed25519 host key in the gateway credential store and deliver it only to the supervisor. Preserve identity across restarts, delete owned credentials with the sandbox, and expose the public SHA256 fingerprint through sandbox and SSH-session APIs and client SDKs. Cover credential ownership, cancellation, deletion retries, client compatibility, and pinned SSH connections through lifecycle transitions. Closes NVIDIA#3835 Signed-off-by: Mike Nguyen <miken@nvidia.com>
Local verification evidenceThe saved local run passed all six Docker CLI conformance scenarios and the SSH identity E2E test. This report covers the working-tree implementation subsequently committed as Environment: macOS on ARM64, Docker Desktop. Live test run: October 1, 2026. The excerpts below come from the saved test output. What the live SSH test verified
The test source at this commit contains these assertions. The gateway restart branch ran; its skip message is absent from the saved output. Actual SSH test output: Supporting checks
Actual SSH credential test outputReproduction and scopeThe standard focused Docker command is: OPENSHELL_E2E_DOCKER_TEST=ssh_host_identity mise run e2e:dockerOn this Mac, the stock launcher's container-side if [ "$(uname -s)" = "Darwin" ]; then
GATEWAY_BIND_IP="0.0.0.0"
SUPERVISOR_GATEWAY_HOST="host.docker.internal"
fiThe gateway still used mTLS. The successful local invocation was: LIBRARY_PATH=/opt/homebrew/lib \
OPENSHELL_E2E_DOCKER_TEST=ssh_host_identity \
OPENSHELL_CONFORMANCE_BIN="$PWD/target/debug/openshell-conformance" \
mise exec -- bash plans/3835-with-docker-gateway-macos.sh \
bash e2e/rust/e2e-docker.sh __openshell_run_docker_e2eThe launcher adjustment is excluded from the PR. This evidence covers local Docker verification; Kubernetes pod rescheduling and other driver E2E lanes were not exercised. GitHub runner validation is still pending. |
drew
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Thanks @quocanh261997 for documenting the current-head Docker and SSH verification. I checked the full patch and the creation/cleanup paths those tests exercise; one PR-owned failure path can leave a sandbox record and its host-key credential behind after create reports an error.
Action required: make every post-commit identity failure either fully remove the sandbox and key or durably leave the sandbox in a retryable deletion state.
Blocking findings:
GATOR-e5de0a89-01: see the inline Warning on the post-commit identity preparation call.
Carried findings:
- None
Gator metadata
- Validation: project-valid through linked, validated issue #3835
- Docs: sandbox identity behavior is documented under
docs/; related operating skills are updated - Checks: DCO and vouch gates are green; required branch workflows have not been dispatched for this head
- E2E:
test:e2eis required for sandbox lifecycle and credential-flow changes, but dispatch waits until blocking review feedback is resolved - Head SHA:
e5de0a892813408bf08a36d93ab33b66a9929d18 - Base SHA:
76cfd0e31d5e1633db7ccd86ad9023ef7a2461b2 - Merge base SHA:
021400be8af471f8669369e679de3e18cf0bd672 - Patch ID:
accf65c51eed8be52b1848efb2eb9bfbfe089f99 - Gator payload:
10 - Review mode:
initial - Previous reviewed SHA: none
- Review budget exhausted: no
- Maintainer decision required: no
- Next state:
gator:in-review
Signed-off-by: Mike Nguyen <miken@nvidia.com>
Local verification after the failed-create fixTested the working tree now committed as The six new regression tests all passed. They inject failures to check that:
Full E2E Test AttestationGateway mode: Docker. Every live test/scenario executed by the focused launcher passed:
The SSH test checks the published fingerprint against the key actually presented, workload read restrictions, a pinned OpenSSH connection after stop/start and a managed gateway restart, and a different key after delete/recreate. The gateway restart branch ran; no SSH test was skipped. Command used: LIBRARY_PATH=/opt/homebrew/lib \
OPENSHELL_E2E_DOCKER_TEST=ssh_host_identity \
OPENSHELL_CONFORMANCE_BIN="$PWD/target/debug/openshell-conformance" \
mise exec -- bash plans/3835-with-docker-gateway-macos.sh \
bash e2e/rust/e2e-docker.sh __openshell_run_docker_e2eAs in the earlier evidence, the ignored launcher copy uses |
|
Label |
|
/ok to test a645d9f |
drew
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Thanks @quocanh261997. I checked your cleanup update against the prior failed-create finding: post-commit identity failures and driver-create rejections now share compensation that removes the sandbox and key when possible, retains a durable Deleting record when cleanup must retry, and continues cleanup after caller cancellation. The added regressions cover the requested credential-store and key-deletion failures. No blocking code-review findings remain.
Blocking findings:
- No blocking findings remain
Carried findings:
GATOR-e5de0a89-01: resolved by this head; the Gator-owned thread has been closed
Required test dispatch is not complete yet. Gator applied test:e2e and posted /ok to test for the current head; the contributor mirror must be created before the E2E label can be re-applied and the required workflows confirmed queued.
Gator metadata
- Validation: project-valid through linked, validated issue #3835
- Docs: sandbox identity behavior is documented under
docs/; no additional docs change is required for this cleanup-only delta - Checks: DCO is green; required current-head branch workflows are awaiting contributor-mirror dispatch
- E2E:
test:e2eis required; mirror creation was requested with/ok to test a645d9fd5d61993ed8c8f3d848955e38f0e81be5and workflow queue confirmation remains pending - Head SHA:
a645d9fd5d61993ed8c8f3d848955e38f0e81be5 - Base SHA:
76cfd0e31d5e1633db7ccd86ad9023ef7a2461b2 - Merge base SHA:
021400be8af471f8669369e679de3e18cf0bd672 - Patch ID:
3b6a5b4b21488f22e5a5a68154f7db415e0e635c - Gator payload:
10 - Review mode:
follow_up - Previous reviewed SHA:
e5de0a892813408bf08a36d93ab33b66a9929d18 - Review budget exhausted: no
- Maintainer decision required: no
- Next state:
gator:in-review
|
/ok to test a645d9f |
Signed-off-by: Mike Nguyen <miken@nvidia.com>
|
Updated the branch in commit The failed merge-queue run hit a russh callback signature change. The SSH pinning test now accepts Live verification caught and fixed a cleanup integration problem: the payload can contain an older record version than the database row. Cleanup now uses the authoritative version, so a sandbox does not remain listed after its key is removed. The gateway timeout test uses its existing normalization helper to handle wrapped macOS error text. Verification of this commit:
Live checks used the Docker driver on macOS with the previously documented, ignored launcher adjustment ( |
drew
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Thanks @quocanh261997. I checked your October 4 current-main integration update, including the conflict resolutions around provisioning ownership, cancellation-safe failed-create cleanup, authoritative resource versions, and the updated russh host-key callback. The prior cleanup obligation remains resolved, and the merge-resolution delta introduces no new blocking findings.
Blocking findings:
- No blocking findings remain
Carried findings:
GATOR-e5de0a89-01: remains resolved; this integration preserves the compensation and retryable deletion behavior
Required test dispatch is not complete for this head. test:e2e remains required, and the contributor mirror still points at the previous head; Gator will request a current-head mirror and wait for the required workflows to queue before entering pipeline watch.
Gator metadata
- Validation: project-valid through linked, validated issue #3835
- Docs: the stable host-identity contract is documented under
docs/how-it-works/sandboxes/overview.mdx; no additional docs change is required for this integration-only delta - Checks: DCO is green; current-head branch, Helm, Trivy, and E2E gates have not completed
- E2E:
test:e2eis required; the contributor mirror is stale and current-head dispatch is pending - Head SHA:
6ec9c0a975e3334bc97e646b1ee6df69bb8fd9c2 - Base SHA:
71c3cd957abef062eb7f37010056717cd49f2ed3 - Merge base SHA:
71c3cd957abef062eb7f37010056717cd49f2ed3 - Patch ID:
c893ec58b812d4be42e12fd6381b58c83cfb6134 - Gator payload:
10 - Review mode:
follow_up - Previous reviewed SHA:
a645d9fd5d61993ed8c8f3d848955e38f0e81be5 - Review budget exhausted: no
- Maintainer decision required: no
- Next state:
gator:in-review
|
/ok to test 6ec9c0a |
Signed-off-by: Mike Nguyen <miken@nvidia.com>
|
Fixed the rootless Podman SSH test in The failed job reached delete/recreate, then failed with Verification:
This update changes only the E2E test. Rootless Podman execution of the updated test still needs GitHub CI; it was not run locally on macOS. GitHub Branch Checks passed on the preceding commit. The PR description now separates the latest verification from the earlier results. GitHub currently reports |
drew
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Thanks @quocanh261997. I checked your October 4 deletion-polling update against the prior cleanup obligation and the test harness behavior. Waiting for the original sandbox to disappear before same-name recreation fixes the observed rootless Podman race, the earlier failed-create finding remains resolved, and this test-only delta introduces no new Critical defect.
Blocking findings:
- No blocking findings remain
Carried findings:
GATOR-e5de0a89-01: remains resolved; this delta does not alter the compensation or retryable deletion implementation
Required test dispatch is not complete for this head. test:e2e remains required, and Gator will request the current-head contributor mirror before entering pipeline watch.
Gator metadata
- Validation: project-valid through linked, validated issue #3835
- Docs: the stable host-identity contract is documented under
docs/how-it-works/sandboxes/overview.mdx; no additional docs change is required for this test-only delta - Checks: DCO is green; current-head Branch Checks, Helm, Trivy, and E2E workflows are awaiting contributor-mirror dispatch
- E2E:
test:e2eis required; current-head mirror creation and workflow queue confirmation are pending - Head SHA:
7800f2f3fd34deccbd7dab62533e48779acbcd19 - Base SHA:
71c3cd957abef062eb7f37010056717cd49f2ed3 - Merge base SHA:
71c3cd957abef062eb7f37010056717cd49f2ed3 - Patch ID:
2cc204fdaa30f3122895ef858cb7524e04f99c26 - Gator payload:
10 - Review mode:
critical_only - Previous reviewed SHA:
6ec9c0a975e3334bc97e646b1ee6df69bb8fd9c2 - Review budget exhausted: yes
- Maintainer decision required: no
- Next state:
gator:in-review
|
/ok to test 7800f2f |
Summary
Sandbox SSH host keys currently change when the supervisor restarts, so clients cannot reliably recognize the same sandbox. Give each sandbox ID one Ed25519 host key and expose its public SHA256 fingerprint. Keep the private key in the configured gateway credential store and deliver it only to the supervisor.
Related Issue
Closes #3835
Replaces #4027, which the vouch check closed before contributor approval. The contributor is now vouched.
Changes
maininto the branch, resolve the compute and schema conflicts, and update the SSH handshake test for russh 0.63. Preserve provisioning ownership and cancellation-safe failed-create cleanup. Use the database record version when deleting sandbox-owned credentials and the parent record.Testing
Latest SSH E2E test update:
ssh_host_identity— passed.mise run pre-commit— passed.GitHub Branch Checks passed on the preceding commit
6ec9c0a97. The only failed E2E job was the rootless Podman SSH identity test addressed above.Earlier local verification of
6ec9c0a97after mergingmainat71c3cd957:mise run pre-commit— passed, including formatting, lint, and license checks.mise run ci— ran; Python (190 tests), Go checks, TypeScript (142 tests), Rust lint and compile checks passed. The workspace run stopped at one unchanged network test:proxy::tests::mediated_connect_keeps_workload_bytes_read_with_the_synthesized_header, which timed out waiting for a local TCP request. The same failure reproduces when that test runs alone. The full CI command is not green locally.cargo test -p openshell-server --features test-support— passed: 1,949 unit tests, 8 ignored, and all integration tests. Includes the failed-create, ownership, deadline, cancellation, and watch/delete tests.cargo test -p openshell-supervisor-process -p openshell-tui -p openshell-vfio— passed: 102, 92, and 59 tests respectively. The SSH tests exercise the updated russh callback and pinned keys.The imported gateway timeout assertion now uses its existing diagnostic-normalization helper so macOS line wrapping does not cause a false failure; all eight preflight tests passed. The failed-create fixture explicitly reports an absent compute resource, preserving the separate test for asynchronous deletion.
Rust checks used
LIBRARY_PATH=/opt/homebrew/libfor Homebrew Z3 on macOS. The live Docker checks use an ignored copy of the launcher withhost.docker.internalas the supervisor endpoint and the temporary mTLS gateway bound on all interfaces. This local launcher adjustment is excluded from the PR. Kubernetes pod rescheduling and other compute-driver lanes have not been exercised locally.Checklist