Sharing an X11 Server Across Hosts with FamilyWild

shirozuki3 pts0 comments

Sharing an X Server Across Hosts with FamilyWild · dobrowolski.dev

2026-08-02

Sharing an X Server Across Hosts with FamilyWild

Ever tried running an X11 application from inside a container, a chroot, or over<br>ssh with a bind-mounted .Xauthority — only to be greeted by the ever-helpful<br>Authorization required, but no authorization protocol specified?

The file is right there, mounted read-only where the client expects it, and yet<br>X refuses the connection. The reason is subtle, and the fix is a single line of<br>sed.

Why the cookie is rejected

An .Xauthority file is a list of cookies, and every entry is keyed by a<br>family and a hostname. When a client connects, it doesn't just grab the<br>first cookie it finds — it looks for the entry whose hostname matches the<br>machine the client believes it's running on.

That's exactly what breaks the moment the client runs somewhere other than where<br>the cookie was minted. Inside a container the hostname is different; over an<br>un-forwarded socket the client resolves a different name entirely. The cookie is<br>present and valid, but its hostname doesn't match, so the client never offers it<br>and X falls back to "no authorization protocol."

You can see the family/hostname keying for yourself:

$ xauth list<br>myhost/unix:0 MIT-MAGIC-COOKIE-1 a1b2c3d4e5f6...

That leading myhost/unix:0 is the problem — it pins the cookie to myhost.

FamilyWild to the rescue

X has a wildcard family, FamilyWild, whose numeric value is 0xffff. A cookie<br>in this family matches any hostname. So instead of trying to make the client's<br>hostname match the cookie, we rewrite the cookie to match every host.

xauth nlist prints entries in a numeric format where the first field is the<br>4-hex-digit family. Overwrite it with ffff and merge the result into a fresh<br>file:

: > /tmp/portable.Xauthority && xauth nlist :0 | \<br>sed 's/^..../ffff/' | xauth -f /tmp/portable.Xauthority nmerge -<br># Replace :0 with your $DISPLAY value

The change is a single field

It's worth seeing just how small the edit is. An .Xauthority entry is a packed<br>binary record: a 2-byte family, then length-prefixed address, display number,<br>auth name, and the cookie itself. Dump the original file and the family sits in<br>the very first two bytes — 0100, i.e. FamilyLocal:

00000000: 0100 0006 6d79 686f 7374 0001 3000 124d ....myhost..0..M<br>00000010: 4954 2d4d 4147 4943 2d43 4f4f 4b49 452d IT-MAGIC-COOKIE-<br>00000020: 3100 10a1 b2c3 d4e5 f607 1829 3a4b 5c6d 1..........):K\m<br>00000030: 7e8f 90 ~..

Now dump the FamilyWild version we just built. Everything is byte-for-byte<br>identical — same myhost address, same cookie — except those first two bytes,<br>now flipped to ffff:

00000000: ffff 0006 6d79 686f 7374 0001 3000 124d ....myhost..0..M<br>00000010: 4954 2d4d 4147 4943 2d43 4f4f 4b49 452d IT-MAGIC-COOKIE-<br>00000020: 3100 10a1 b2c3 d4e5 f607 1829 3a4b 5c6d 1..........):K\m<br>00000030: 7e8f 90 ~..

That one field is the whole trick: the address bytes still spell myhost, but X<br>no longer cares, because family 0xffff matches unconditionally.

Bind-mount (or scp) /tmp/portable.Xauthority wherever the client runs, point<br>XAUTHORITY at it, and the connection is accepted regardless of the hostname<br>mismatch.

What about xhost +?

You'll often see xhost + suggested as the "just make it work" answer, and it<br>does — by turning host-based access control off entirely. Every client from<br>every host can then connect to your display without any cookie at all.

On a single-user machine that sounds harmless, but X has no isolation between<br>clients: anyone who can reach the server can read your keystrokes, grab the<br>contents of any window, and inject synthetic input. xhost + hands that<br>capability to every local user and, if your server listens on TCP, to the<br>network. Even the narrower xhost +local: still trusts every UID on the box.

The FamilyWild cookie keeps the door locked — a client still has to present the<br>secret — while removing only the hostname constraint that was getting in the<br>way. Reach for it instead of xhost +, and keep the cookie file at 0600.

One caveat

A FamilyWild cookie is deliberately less specific than the one it replaces:<br>anyone who can both reach your X socket and read this file can talk to your<br>display. Hand it only to the environments that actually need it, and don't leave<br>copies lying around on shared machines.

This trick is one small piece of a larger setup — I use it to forward X into<br>unprivileged LXC containers, which I wrote up in detail in<br>Enhancing x11 Application Security with LXC.

cookie client hostname familywild family xauthority

Related Articles