fix(itmux): DooD credential transfer (docker exec stdin, not -v mounts)#231
fix(itmux): DooD credential transfer (docker exec stdin, not -v mounts)#231NeuralEmpowerment wants to merge 1 commit into
Conversation
…in-container (DooD) Syntropic137 runs this driver INSIDE a container (docker-out-of-docker), so `docker run -v host:container` bind mounts for credentials are wrong: a sibling `-v` is resolved by the OUTER daemon against its own filesystem, which cannot see this driver's own staging dir. The Rust port previously did exactly that (`auth::Mount` -> `-v` args in `Workspace::start`), so in production every agent started unauthenticated. Ports the Python driver's fix (driver/interactive_tmux.py PY:1493-1583, PY:1850-1869) bit-for-bit: - `auth::prepare` now returns `PreparedAuth` (staged host path -> in- container destination), not `-v` bind-mount args. - `Workspace::start` runs a bare `docker run ... sleep infinity` with no credential mounts, then calls `auth::stage_into_container` to push each staged file/dir in over `docker exec` stdin: files as base64 through `base64 -d`, directories file-by-file (mirrors Python's `os.walk` transfer exactly, not a tar stream). Credentials never appear in argv (world-readable via `ps`/`/proc/<pid>/cmdline`) - only ever in stdin. - After transfer, every staged path is chowned to 1000:1000 (the in- container `agent` user) and locked to 0600 for files (`chmod 600` via `find` for directory trees), for claude, codex, and gemini - not just the 2 claude files as before. - `docker_exec_with_stdin` added to tmux.rs (feeds stdin from a dedicated thread to avoid a pipe-buffer deadlock against `wait_with_output`). - The transfer/secure command plan is built by pure functions (`write_bytes_plan`, `transfer_dir_plan`, `secure_path_plan`, `plan_for_staged_path`) so it's assertable in tests without a docker daemon; `tests/cred_transfer.rs` covers the no-`-v` docker run argv, the staged destination shape, stdin-only payload delivery, and the chown/chmod plan for both files and directories. No new dependencies: added a small std-only base64 encoder rather than pulling in the `base64` crate for one encode call (decode happens in-container via the coreutils `base64 -d`).
There was a problem hiding this comment.
Pull request overview
This PR updates the Rust itmux interactive-tmux workspace driver to work correctly in docker-out-of-docker (DooD) setups by removing credential -v bind mounts from docker run and instead transferring credential files into the running container via docker exec -i over stdin (base64), then locking down ownership and permissions in-container to match the canonical Python driver.
Changes:
- Provision containers with a bare
docker run ... sleep infinityargv (no credential-vflags) and then push staged credentials in viadocker execstdin. - Introduce a testable “exec plan” model for credential transfer + in-container chmod/chown, plus new parity tests.
- Add a
docker_exec_with_stdinhelper in the tmux module for stdin-fed exec calls.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| providers/workspaces/interactive-tmux/driver-rs/tests/pane_tail.rs | Minor formatting-only change in an existing test. |
| providers/workspaces/interactive-tmux/driver-rs/tests/cred_transfer.rs | Adds DooD credential-transfer parity tests asserting no -v and stdin-only payload transfer/secure plan. |
| providers/workspaces/interactive-tmux/driver-rs/src/workspace.rs | Switches provisioning to a bare docker run + post-start credential push via auth::stage_into_container. |
| providers/workspaces/interactive-tmux/driver-rs/src/tmux.rs | Adds docker_exec_with_stdin for stdin-fed docker exec -i calls. |
| providers/workspaces/interactive-tmux/driver-rs/src/auth.rs | Replaces mount-based auth with staged-path + exec-transfer plan (base64 stdin), plus in-container secure step. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let out = child.wait_with_output()?; | ||
| writer | ||
| .join() | ||
| .map_err(|_| Error::other("stdin-writer thread panicked"))??; | ||
| check_output( | ||
| out, | ||
| &format!("docker exec -i {container} {}", redact_args(args)), | ||
| ) | ||
| } |
| pub fn stage_into_container(container: &str, prepared: &PreparedAuth) -> Result<()> { | ||
| for staged in prepared.iter() { | ||
| for step in plan_for_staged_path(staged)? { | ||
| run_exec_step(container, &step)?; | ||
| } | ||
| } | ||
| Ok(()) | ||
| } |
Review status: APPROVED (2 independent passes) - merge-ready, held for maintainer mergeOrchestrator review: PASS. No Independent adversarial security review: VERDICT: APPROVE. Attacked all 7 vectors (cred leakage, shell injection, base64 correctness, securing completeness, writer-thread deadlock, panics, parity drift) - no BLOCKER/MAJOR. Highlights:
MINOR (non-blocking), tracked:
NITs (EPIPE masking, Note: codex review was unavailable (headless codex ended its turn without emitting a verdict); used an independent adversarial Claude review as the cross-check. Recommend a live DooD smoke (gated in Plan 1a Task 7) before the Python driver is deleted (Plan 1b). |
What & why
itmux(Rust driver) delivered credentials viadocker run -v host:containerbind mounts. In Syntropic137's docker-out-of-docker (DooD) topology the driver runs inside a container, so a sibling-vresolves the source on the OUTER daemon and mounts empty/nonexistent paths - every agent starts unauthenticated. The canonical Python driver abandoned-vfor exactly this reason.This ports the Python driver's model (parity source
driver/interactive_tmux.py):docker run ... sleep infinity(no-vcred flags).docker exec -iwith base64 over stdin (ExecStep.stdin) - credentials never appear in argv (PY:1506-1517).chown -R 1000:1000+find -type f -exec chmod 600so the non-rootagent(uid 1000) can read them (PY:1566-1583).Part of Plan 1a (itmux -> production parity), Tasks 1+2. Prerequisite for deleting the Python driver (Plan 1b). Tracks okrs-51p.6 / okrs-o78.
Tests & gates (local - Rust not yet in CI; wired in Plan 1a Task 7)
cargo test: 54 passed (newcred_transfer.rs: 7 tests asserting no-vcred flags, stdin-only payload, chown/chmod plan - all without a docker daemon).cargo clippy --all-targets -- -D warnings: clean.cargo fmt --check: clean.Reviewer notes