通过 FamilyWild 在不同主机间共享 X11 服务器
Sharing an X11 Server Across Hosts with FamilyWild

原始链接: https://dobrowolski.dev/article/sharing-an-x-server-across-hosts-with-familywild/

在容器或通过 SSH 运行 X11 应用程序时,经常会出现“no authorization protocol”(无授权协议)错误,这是因为 `.Xauthority` cookie 绑定到了特定的主机名。当容器的主机名与宿主机不同时,X 服务器会拒绝该 cookie。 虽然很多人建议使用 `xhost +` 来绕过此问题,但该命令会完全禁用安全性,允许任何本地用户截获你的按键和窗口数据。 一种更好、更安全的方法是使用 **`FamilyWild`** (0xffff) 属性。它将 cookie 修改为与主机名无关,从而允许 X 服务器接受来自任何来源的客户端。 你可以使用以下命令创建可移植的 cookie 文件: ```bash : > /tmp/portable.Xauthority && xauth nlist :0 | \ sed 's/^..../ffff/' | xauth -f /tmp/portable.Xauthority nmerge - ``` 通过挂载该文件并设置 `XAUTHORITY` 环境变量,你可以在保持基于 cookie 的身份验证完整性的同时,授予客户端必要的权限。请确保该文件安全(模式 0600)且仅提供给受信任的环境,因为它移除了密钥 cookie 上的主机名限制。

Hacker News 最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 在多台主机间共享 X11 服务器 (dobrowolski.dev) 9 分,由 shirozuki 于 1 小时前发布 | 隐藏 | 过往 | 收藏 | 1 条评论 帮助 calvinmorrison 15 分钟前 [–] 我大部分时间都用 xrdp,它工作得非常完美。 回复 考虑申请 YC 2026 年秋季批次!申请截止日期为 7 月 27 日。 准则 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

2026-08-02

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

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


Why the cookie is rejected

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

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

You can see the family/hostname keying for yourself:

$ xauth list
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 in this family matches any hostname. So instead of trying to make the client's 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 4-hex-digit family. Overwrite it with ffff and merge the result into a fresh file:

: > /tmp/portable.Xauthority && xauth nlist :0 | \
    sed 's/^..../ffff/' | xauth -f /tmp/portable.Xauthority nmerge -
# 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 binary record: a 2-byte family, then length-prefixed address, display number, auth name, and the cookie itself. Dump the original file and the family sits in the very first two bytes — 0100, i.e. FamilyLocal:

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

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

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

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

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


What about xhost +?

You'll often see xhost + suggested as the "just make it work" answer, and it does — by turning host-based access control off entirely. Every client from 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 clients: anyone who can reach the server can read your keystrokes, grab the contents of any window, and inject synthetic input. xhost + hands that capability to every local user and, if your server listens on TCP, to the 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 secret — while removing only the hostname constraint that was getting in the 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: anyone who can both reach your X socket and read this file can talk to your display. Hand it only to the environments that actually need it, and don't leave copies lying around on shared machines.

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

联系我们 contact @ memedata.com