openai / openai/codex

[Windows Remote SSH] Desktop pre-creates app-server-control with inherited ACL, conflicting with #42326 socket validation

Open
#44,581 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app app-server bug Linux remote windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of the Codex App are you using?

Codex GUI 26.901.51231 on Gentoo Linux (AppImage).

Remote Codex CLI: codex-cli 0.154.0 (Scoop, native Windows binary).

What platform is your computer?

Client:

  • Gentoo Linux on a ThinkPad
  • Codex GUI 26.901.51231

Remote SSH host:

  • Native Windows, 10.0.26200
  • Windows OpenSSH
  • MSYS/zsh login environment
  • Codex CLI 0.154.0
What issue are you seeing?

Codex Desktop Remote SSH authenticates successfully and can invoke the remote codex CLI, but the GUI fails with socket hang up because its SSH bootstrap pre-creates $CODEX_HOME/app-server-control using POSIX mkdir -p.

On native Windows through MSYS, that creates the directory with an inherited Windows DACL. Codex CLI 0.154.0 then rejects the existing directory under the Windows control-socket hardening introduced by #42326:

Error: socket directory is not private to the current user

The conflict is deterministic:

Desktop creates a directory which the CLI it immediately launches is required to reject.

The current GUI bundle was inspected directly (app.asar). Its Remote SSH bootstrap contains the equivalent of:

umask 077
mkdir -p -- "${CODEX_HOME:-$HOME/.codex}/app-server-control"
codex -c features.code_mode_host=true app-server --listen unix://

In this build there is no chmod 700 before launching the app-server.

On Windows, umask 077 does not produce the protected, current-user-only DACL required by the CLI's Windows socket validation.

Controlled A/B/C reproduction

I ran a controlled comparison against the same host, account, CLI, socket path, and SSH environment.

Case Directory creator ACL state Result
Original failure Desktop/MSYS Protected=false, 4 allow ACEs app-server rejects directory; GUI socket hang up
A native codex.exe Protected=true, 1 allow ACE (current user, full control) HTTP 101 + app-server initialize succeeds; proxy exits 0
B Desktop Remote SSH bootstrap Protected=false, 4 allow ACEs same socket directory is not private error
C native CLI creates directory first; Desktop then reconnects protected single-ACE ACL remains unchanged Desktop starts its own app-server and reaches connected, initialized=true

The four allow ACEs produced in the failing Desktop-created directory were:

  • current user
  • SYSTEM
  • Administrators
  • CodexSandboxUsers
A — CLI creates the directory
  1. Stop any app-server/proxy using the default control socket.
  2. Move the existing app-server-control directory aside so the target path does not exist.
  3. Start native Windows Codex directly with the same effective app-server arguments:
codex.exe -c features.code_mode_host=true app-server --listen unix://
  1. The CLI creates $CODEX_HOME/app-server-control with:
Protected=true
Entries=1
  1. Through SSH, run codex app-server proxy and send a real WebSocket Upgrade request.
  2. Receive HTTP 101 Switching Protocols.
  3. Send app-server initialize; receive a valid response reporting the Windows platform, correct codexHome, and version 0.154.0.
  4. Close the WebSocket normally; proxy exits 0.

This establishes that the native Windows app-server/socket/proxy path works when the CLI creates its own rendezvous directory.

B — Desktop creates the directory
  1. Stop the A app-server.
  2. Move the CLI-created directory aside so the target path again does not exist.
  3. Click Connect in the same Codex GUI.
  4. Desktop creates app-server-control before the CLI starts.
  5. The new directory has:
Protected=false
Entries=4
  1. The new app-server.log immediately contains:
Error: socket directory is not private to the current user
  1. Subsequent proxy attempts fail with Windows connection refused (10061) and the GUI reports socket hang up.

The failing DACL is materially the same as the original directory's DACL.

C — retain the CLI-created private directory and let Desktop launch the server

To separate "Desktop can only attach to a manually started server" from the directory-creation bug:

  1. Remove the Desktop-created failing directory.
  2. Let native CLI create a correct protected single-ACE directory.
  3. Stop the manually started app-server but leave that directory in place.
  4. Allow Desktop to reconnect/retry normally.
  5. Desktop's mkdir -p sees the existing directory and does not alter its DACL.
  6. Desktop then starts a new remote app-server itself.
  7. The GUI reaches:
connected
initialized=true
connectionError=null
  1. A separate WebSocket + initialize probe against that GUI-started server also succeeds.

The final directory remains Protected=true, Entries=1.

This demonstrates that preserving the CLI-created Windows ACL is sufficient for the current Desktop Remote SSH flow to work; the blocker is the bootstrap's pre-creation of the directory.

Source-code match

Codex 0.154.0 Windows socket validation:

https://github.com/openai/codex/blob/rust-v0.154.0/codex-rs/uds/src/windows_socket_validation.rs

PR #42326 explicitly changed Windows control-socket handling to:

  • create socket directories with a protected, inheritable, current-user-only DACL;
  • reject existing directories with broader permissions rather than repairing them.

https://github.com/openai/codex/pull/42326

That security behavior is reasonable on its own: repairing an already-existing broader directory cannot revoke access through handles that may already have been opened.

The integration problem is that Desktop Remote SSH creates the rendezvous directory first using a POSIX bootstrap, preventing the Windows CLI from creating it with the required DACL.

Expected behavior

Desktop Remote SSH should not pre-create the Windows control-socket directory in a form the Windows CLI is required to reject.

Possible fixes include:

  1. Let codex app-server --listen unix:// create $CODEX_HOME/app-server-control itself.
  2. Detect native Windows remotes and use a Windows-aware bootstrap that creates the directory with the same protected DACL as codex.exe.
  3. Move ownership of rendezvous-directory creation entirely into the CLI/app-server so Desktop does not need to duplicate platform-specific security semantics.

The CLI should retain the fail-closed validation introduced by #42326.

Workaround

A working workaround is to let native codex.exe create app-server-control once, then keep that directory. The current Desktop bootstrap's mkdir -p preserves the already-correct ACL, after which Desktop can start and connect to the app-server normally.

Deleting/recreating app-server-control reintroduces the problem if Desktop wins the creation race.

Additional observation (separate from the proven ACL root cause)

The SSH bootstrap also intermittently logs an MSYS fork failure involving 0xC0000142 / Resource temporarily unavailable, and one cold-start bootstrap attempt timed out after 60 seconds.

I have not established the cause of that error. It is not required for the ACL failure: in case C, the GUI reached initialized=true despite an MSYS fork error being logged. I am therefore treating it as a separate observation rather than part of this issue's root cause.

Related issues
  • #29015 — native Windows Remote SSH needs a Windows-aware app-server lifecycle/bootstrap path
  • #22965 — native Windows + Git Bash reaches app-server/proxy but Remote SSH fails later
  • #24090 — earlier native Windows unix:// / proxy failure on 0.131
  • #20636 — historical evidence that Desktop itself pre-creates app-server-control in the SSH bootstrap
  • #42326 — Windows control-socket hardening that now intentionally rejects broad existing ACLs

This report is narrower than #29015 and differs from #22965/#24090 because the failure on 0.154.0 has a deterministic ACL cause and an A/B/C reproduction.

Additional information

The experiments did not modify global Codex configuration, SSH configuration, CLI installation, or project files. The original failing directory and the A/B experiment artifacts were preserved. I can provide sanitized SDDL values, timestamps, Desktop log extracts, the extracted bootstrap source snippet, and the WebSocket/initialize probe results if useful.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with codex-rs/uds/src/windows_socket_validation.rs and the Remote SSH bootstrap that runs umask 077 and mkdir -p before codex app-server --listen unix://. Reproduce the native Windows ACL difference described in cases A and B, then trace the bootstrap entry point. Done means Desktop no longer creates a directory the CLI rejects and the connection reaches initialized=true.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, shell
Domain
cli, networking, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.