pathshim gives a command best-effort bind path mappings without mount privileges:
pathshim --bind <source:guest-destination> [--cwd <guest-path>] -- <command> [args...]It is useful when several processes need a stable path such as /workspace, while their real data lives elsewhere:
pathshim \
--bind /var/lib/session-1/workspace:/workspace \
--cwd /workspace \
-- /bin/shWithin supported filesystem operations, /workspace/result.txt now refers to /var/lib/session-1/workspace/result.txt.
- A bind is a replace mapping: paths below guest
DESTresolve only below physicalSOURCE. The Pod's originalDESTtree is not a read fallback. - Binds may be repeated. When destinations overlap, the longest matching destination wins.
- Paths outside every bind retain their normal Pod filesystem semantics.
- A missing source is created recursively when its parent is writable.
DESTmust be absolute, below/, and outside/dev,/proc, and/sys.- A bind view stores no metadata. Independent pathshim invocations and external writers may share a source concurrently; visibility and write races follow the backing filesystem.
pathshim is not a security boundary or a complete bind mount emulator. Unsupported syscalls may bypass the mapping and operate on the Pod's original path.
Use -w, --cwd, or --pwd to choose the initial guest cwd. The default is /.
When a requested cwd is inside a bind, pathshim checks it in the source and creates it when missing. A non-directory or unavailable cwd emits a diagnostic and falls back to /.
pathshim \
--bind /var/lib/session-1/workspace:/workspace \
--cwd /workspace/project \
-- /bin/sh -c 'pwd; echo hello > result.txt'The command sees /workspace/project; the output is stored at /var/lib/session-1/workspace/project/result.txt.
pathshim probes the actual node instead of maintaining a kernel or distribution allowlist:
bind-viewverifies seccomp user notification, parent-to-child memory access, file descriptor injection, and a configured destination before command startup.passthroughruns the original command and arguments with inherited environment and caller cwd when bind-view cannot be installed.
The selected mode and degradation reason are written to stderr. Integrations that keep control-plane diagnostics separate from command output can use --quiet.
Command startup takes priority over mapping. Once a command has started, pathshim never restarts it in another mode because doing so could repeat side effects.
Long-running callers can test the exact bind configuration during startup without running a user command:
pathshim probe --bind /var/lib/session-probe/workspace:/workspaceThe probe performs the same seccomp listener handshake and live Projection checks used before a normal command. It prints bind-view and exits 0 when mapping is available; it prints passthrough and exits 1 when a normal invocation would degrade, with the reason on stderr. Invalid CLI input exits 2. The caller owns the probe source directory and may remove it afterward.
pathshim targets an ordinary Kubernetes Pod. It does not request:
- a privileged container;
- additional Linux capabilities;
- a custom AppArmor or seccomp profile;
/dev/fuse; or- a private mount namespace.
Full bind-view mode requires Linux kernel 5.9 or newer, procfs mounted at /proc, and a security profile that does not block seccomp user notification or parent-to-child process_vm_readv/process_vm_writev. Older kernels and restrictive startup policies automatically use passthrough. Rewriting an executable pathname stored in read-only memory additionally uses the same parent-child access through /proc/<pid>/mem; if only that operation is blocked, the mapped exec fails without disabling the rest of the active bind view.
Full mode has been exercised on Linux 5.15 and in an unprivileged Docker container with no-new-privileges and the default container security profile. If a deployment can grant a private mount namespace and needs complete filesystem semantics, use bubblewrap instead.
The Linux backend projects common operations used by shells and applications:
- opening and creating files;
- stat and access checks;
- directory creation, removal, rename, and listing;
- symlink creation and reading;
- guest
chdir,fchdir, relative paths,getcwd, and/proc/self/cwd; - executing readable ELF binaries through mapped absolute paths with
execveandexecveat; - path-based truncate, ownership, permission, and timestamp updates; and
- caller process-group inheritance, common termination signal forwarding, and signal terminal-status preservation.
The mapping works below language runtimes, so supported operations cover dynamically linked programs and static Go binaries.
- Filesystem syscall coverage is incomplete. Hard links, device/FIFO creation,
io_uringfile access, and some extended-attribute operations are not mapped yet. - Mapped absolute execution currently targets readable ELF binaries. Execute-only files cannot be injected, and shebang scripts should be invoked through their interpreter.
- The guest executable pathname must be long enough for pathshim's internal
/dev/fd/<n>alias. Normal/workspace/...paths satisfy this in ordinary process FD ranges. /dev,/proc, and/sysretain Pod semantics and cannot be bind destinations.- Rename across projections, or between mapped and unmapped paths, returns
EXDEV. Nonzerorenameat2flags are not supported yet. - Exact sharing for arbitrary
clone(CLONE_FS)combinations and exact fork-time cwd snapshots remain best effort. - Unsupported operations may observe or modify the Pod filesystem. Do not use pathshim to run untrusted code or enforce access control.
Run platform-independent validation locally:
cargo fmt --all -- --check
cargo test
cargo clippy --all-targets -- -D warningsOn Linux, cargo test also runs E2E coverage for replace semantics, mapping-external passthrough, multiple independent invocations sharing one source, external writers, guest cwd, pthread/fork cwd behavior, static Go build plus mapped execve/execveat, an installed Python runtime and child process, quiet diagnostics, and signal behavior. A skipped optional runtime is not verified coverage.
Run the Docker smoke test on each target architecture to verify bind-view and passthrough inside an unprivileged container:
./tests/docker_smoke.shUse an existing architecture-compatible image containing ffmpeg and ffprobe for the optional real-command smoke test:
PATHSHIM_FFMPEG_IMAGE=<image> ./tests/ffmpeg_smoke.shRun the capability audit to report known best-effort bypasses on a target Linux node:
./tests/capability_audit.shApache-2.0