fix(sandbox): stop ssh_server_init from overwriting /tmp to 0700#8
Merged
fix(sandbox): stop ssh_server_init from overwriting /tmp to 0700#8
Conversation
The SSH server initialization in ssh.rs was setting the parent directory
of the SSH socket path (`/tmp`) to mode 0700 after prepare_filesystem
had already ensured it was 1777. Since the default socket path is
`/tmp/openshell-ssh.sock`, this effectively locked out the sandbox user
from /tmp entirely, causing Claude Code to fail with:
EACCES: permission denied, mkdir '/tmp/claude-998/...'
Root cause: ssh_server_init called set_permissions(parent, 0o700) on
the socket's parent directory to prevent the sandbox user from
connecting to the socket. But when the socket lives in /tmp, this
clobbers the world-writable sticky bit that other processes depend on.
Fix: Remove the parent directory permission restriction. The socket file
itself is already set to 0600, which is sufficient to prevent the
sandbox user from connecting.
Defense-in-depth (lib.rs):
- Call ensure_sandbox_process_identity() on the proto policy in
load_policy before converting to Rust types, so prepare_filesystem
and drop_privileges always see run_as_user/run_as_group even when
the server hasn't normalized the policy yet.
- Add root-fallback in prepare_filesystem: when no user/group is
configured but euid is root, fall back to sandbox:sandbox — matching
the same logic in drop_privileges.
Signed-off-by: Paolo Dettori <pdettori@gmail.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
In CI environments (containerized runners), /proc/{pid}/exe may be
unreadable for the forked child process, causing resolve_process_identity
to fail with "failed to resolve peer binary" before reaching the
"ambiguous shared socket ownership" check. Both error paths correctly
deny the connection — accept either in the test assertion.
Signed-off-by: Paolo Dettori <pdettori@gmail.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ssh_server_initinssh.rswas setting the parent directory of the SSH socket path (/tmp) to mode0700afterprepare_filesystemhad already ensured it was1777. This locked the sandbox user out of/tmp, causing Claude Code to fail withEACCES: permission denied, mkdir '/tmp/claude-998/...'.ssh_server_init. The socket file itself is already0600, which prevents the sandbox user from connecting.ensure_sandbox_process_identity()before proto→Rust conversion soprepare_filesystemalways sees the correct user/group; add root-fallback inprepare_filesystemmatchingdrop_privilegeslogic.Root Cause Analysis
The supervisor startup sequence runs:
prepare_filesystem— sets/tmpto1777with correct ownershipssh_server_init— creates the SSH socket at/tmp/openshell-ssh.sockand overwrites/tmpto0700drop_privileges+exec— spawns the sandboxed process assandboxuserStep 2 undid step 1. The previous fix (PR #7) hardened
prepare_filesystembut didn't addressssh_server_initrunning afterward.Changes
crates/openshell-sandbox/src/ssh.rsset_permissions(parent, 0o700)— rely on socket's0600insteadcrates/openshell-sandbox/src/lib.rsensure_sandbox_process_identity()inload_policybefore conversioncrates/openshell-sandbox/src/lib.rsprepare_filesystem(mirrorsdrop_privileges)Test plan
/tmpis1777 root:root0600 root:root(sandbox user cannot connect)mkdir /tmp/test-fixsucceeds as sandbox usersandbox(UID 998)