Skip to content

1.1.78: sealed X servers, one shared X11 socket, Wayland security context, jail display for services - #273

Merged
click0 merged 2 commits into
mainfrom
claude/analyze-test-coverage-nCOJW
Nov 14, 2026
Merged

click0 merged 2 commits into
mainfrom
claude/analyze-test-coverage-nCOJW

Conversation

@click0

@click0 click0 commented Nov 14, 2026

Copy link
Copy Markdown
Owner

The three items 1.1.77 left open in TODO §7 (two MED, one LOW).

What changes

1. The X servers crate starts are sealed (MED).
Xvfb ran with -ac; Xephyr and Xorg had no -auth, which an X server reads as "every local client is welcome". Their sockets are in the host's /tmp/.X11-unix, so any local user or shared-mode jail could read another tenant's display.

  • Each run makes a 16-byte MIT-MAGIC-COOKIE-1 and starts its server with -auth.
  • The cookie goes into the jail as /var/run/crate-Xauthority (XAUTHORITY), to x11vnc, to the embedded VNC server and to crate gui screenshot.
  • The shared X11 mode mounts a private directory holding only the socket of the display DISPLAY names, read-only. It used to mount the host's whole /tmp/.X11-unix read-write.
  • websockify listens on gui/vnc_bind (loopback by default). It had no listen address and served noVNC on every interface. gui/vnc_bind is now accepted in every mode that can have a VNC server.

2. Wayland: a socket the compositor restricts (MED).
gui: auto / gui.mode: wayland bound the host compositor's own socket, privileged protocols included.

  • crate asks the compositor for a security context (wp_security_context_v1) and mounts that restricted socket instead.
  • No libwayland dependency: the exchange is seven requests and three kinds of events, written in lib/wayland_wire_pure and lib/wayland_security_context.
  • New key gui/wayland_security_context: auto (default; falls back to the raw socket with a warning), require (restricted or no run), off (raw on purpose).
  • crate doctor reports whether the compositor can do it; crate audit reports the restricted socket as a PASS.

3. Services and hooks see the jail's display (LOW).

  • DISPLAY, XAUTHORITY, WAYLAND_DISPLAY and XDG_RUNTIME_DIR went on the main command's env(1) line only. JailExec now carries them on both exec paths.
  • The exec_in_jail privops request gains optional fields lang, display, xauthority, wayland_display, xdg_runtime_dir. The names are fixed and the daemon checks each value's shape.
  • Jail environment values are quoted on the sh -c line.

Behaviour changes to know about

  • noVNC is on 127.0.0.1 unless gui/vnc_bind says otherwise.
  • A client needs the cookie to open a headless / nested / gpu display.
  • An x11 shared jail sees one socket, read-only; a host X server restart needs a jail restart.
  • On sway 1.9+ / KWin 6 / Mutter 46+ a gui: auto / wayland jail loses layer-shell, screencopy, virtual input and data-control. A jail meant to have them sets gui/wayland_security_context: off.

How it was checked

Check Result
Unit suite, -Werror 1668/1668
New wayland_security_context_test under ASan + UBSan 8/8
Generated cookie file against a real Xvfb no cookie and wrong cookie refused; right cookie accepted, also from another host name
websockify listen argument 127.0.0.1:port listens on loopback only; bare port listens on every interface
Security context against sway 1.9 (headless) restricted socket offers 29 of 44 globals; kill -9 of the owner stops new connections; nesting answered "not offered"

The new unit test drives the client against a stand-in compositor written in the test from the protocol text, including both passed file descriptors.

Not run on FreeBSD. Everything above is from Linux stand-ins. The on-hardware checks are in docs/on-hardware-validation.md §4t.

Left open (TODO §7)

  • The shared X11 mode still gives the jail the host's trusted cookie (an untrusted XSECURITY cookie would be an opt-in).
  • A compositor without the protocol still gets the raw socket by default; PipeWire / PulseAudio are one shared bus.
  • GUI under a rootless crate: the GUI registry in root-owned /var/run/crate looks unreachable (plausible, needs hardware; found while doing this, not fixed here).
  • The clipboard proxy runs jexec with crate's environment.

Generated by Claude Code

…text, jail display for services

The three items 1.1.77 left open in TODO §7.

X servers crate starts (Xvfb, Xephyr, Xorg) ran without access control
(-ac, or no -auth). Each run now makes a MIT-MAGIC-COOKIE-1, starts the
server with -auth and gives the cookie to the jail
(/var/run/crate-Xauthority, XAUTHORITY), x11vnc, the embedded VNC server
and `crate gui screenshot`.

The shared X11 mode mounted the host's whole /tmp/.X11-unix read-write;
it mounts a private directory with a hard link to the socket of the
display DISPLAY names, read-only.

websockify had no listen address (every interface); it listens on
gui/vnc_bind, which is now accepted in every mode with a VNC server.

gui: auto / gui.mode: wayland bound the host compositor's own socket,
privileged protocols included. crate asks the compositor for a security
context (wp_security_context_v1, hand-written wire client, no
libwayland) and mounts that restricted socket. New key
gui/wayland_security_context: auto | require | off; `crate doctor` and
`crate audit` know about it.

Services, hooks and the healthcheck get the jail's DISPLAY, XAUTHORITY,
WAYLAND_DISPLAY and XDG_RUNTIME_DIR on both exec paths; exec_in_jail
gains the optional fields lang, display, xauthority, wayland_display,
xdg_runtime_dir, each validated by the daemon. Jail env values are
quoted on the sh -c line.

Checked on Linux stand-ins (Xvfb, websockify, sway 1.9); not yet on
FreeBSD — docs/on-hardware-validation.md §4t.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
own_x_socket_dir took a Check pointer out of a temporary Report and read
it after the Report was gone (ASan: heap-use-after-free). Test only.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
@click0
click0 merged commit 79dda0a into main Nov 14, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants