X11認証をホスト間で修正:1フィールドのクッキー書き換え
コンテナ内やSSH経由でX11クライアントが「Authorization required」で失敗する場合、原因はホスト名に固定されたクッキーです。sedコマンド1つでfamilyフィールドをFamilyWild(0xffff)に書き換え、クッキーをポータブルにします。
コンテナ、chroot、またはSSH経由でX11アプリを実行すると、しばしば厄介なAuthorization required, but no authorization protocol specifiedというエラーで終わります。.Xauthorityファイルはクライアントが期待する場所にマウントされているのに、Xは接続を拒否します。原因は微妙で、修正は1行のsedです。
クッキーが拒否される理由
.Xauthorityファイルには、familyとhostnameでキー付けされたクッキーが格納されています。クライアントが接続するとき、任意のクッキーではなく、自分がいるマシンのホスト名に一致するエントリを探します。コンテナ内やフォワードされていないソケット経由では、ホスト名がクッキーが発行されたときのものと異なるため、クライアントはクッキーを提示せず、Xは「no authorization protocol」にフォールバックします。
xauth listでキー付けを確認できます。先頭のmyhost/unix:0がクッキーをmyhostに固定しています。
FamilyWildの登場
XにはワイルドカードファミリーFamilyWildがあり、数値は0xffffです。このファミリーのクッキーは任意のホスト名に一致します。クライアントのホスト名をクッキーに合わせる代わりに、クッキーをすべてのホストに一致するように書き換えます。
xauth nlistを使用して、最初のフィールドが4桁の16進数ファミリーである数値形式でエントリを出力します。それをffffで上書きし、新しいファイルにマージします:
: > /tmp/portable.Xauthority && xauth nlist :0 | \
sed 's/^..../ffff/' | xauth -f /tmp/portable.Xauthority nmerge -
# :0 を自分の $DISPLAY 値に置き換えてください
変更は1フィールドのみ
.Xauthorityエントリはパックされたバイナリレコードです:2バイトのファミリー、その後に長さプレフィックス付きのアドレス、ディスプレイ番号、認証名、クッキーが続きます。元のファイルをダンプすると、最初の2バイトにファミリーが表示されます—0100、つまりFamilyLocalです。書き換え後、これら2バイトがffffになる以外はすべてバイト単位で同一です。アドレスは依然としてmyhostと綴られますが、ファミリー0xffffが無条件に一致するため、Xはもはや気にしません。
/tmp/portable.Xauthorityをクライアントにバインドマウント(またはscp)し、XAUTHORITYをそれに設定すれば、ホスト名の不一致に関係なく接続が受け入れられます。
xhost +はどうか?
xhost +はホストベースのアクセス制御を完全に無効にし、クッキーなしで任意のクライアントが接続できるようにします。シングルユーザーマシンでは無害に聞こえますが、Xにはクライアント間の分離がありません:サーバーに到達できる人は誰でもキーストロークを読み取り、ウィンドウの内容を取得し、入力を注入できます。xhost +はこれをすべてのローカルユーザーに、そしてTCPが有効な場合はネットワークに渡します。xhost +local:でさえ、ボックス上のすべてのUIDを信頼します。
FamilyWildクッキーはドアをロックしたままにします—クライアントは依然として秘密を提示する必要があります—一方でホスト名の制約のみを削除します。xhost +の代わりにこれを使用し、クッキーファイルを0600に保ってください。
1つの注意点
FamilyWildクッキーは意図的に特異性が低くなっています:Xソケットに到達してファイルを読める人は誰でもあなたのディスプレイと通信できます。必要な環境にのみ渡し、共有マシンにコピーを残さないでください。
このトリックは、特権のないLXCコンテナにX11を転送するためのより大きな設定の一部であり、著者のEnhancing x11 Application Security with LXCで詳しく説明されています。
FamilyWildクッキーはドアをロックしたままにします—クライアントは依然として秘密を提示する必要があります—一方で、邪魔になっていたホスト名の制約のみを削除します。