web-dev-qa-db-ja.com

「--net Host」なしでSSH経由で接続されたサーバー上でDockerコンテナでXアプリケーションを確実に実行する

Dockerコンテナがない場合、SSH X11転送(ssh -X)を使用してリモートサーバーでX11プログラムを実行するのは簡単です。サーバー上のDockerコンテナー内でアプリケーションを実行するときに、同じことを実行しようとしました。 -Xオプションを使用してサーバーにSSHで接続すると、X11トンネルが設定され、環境変数「$ DISPLAY」は通常「localhost:10.0」または同様の値に自動的に設定されます。 DockerでXアプリケーションを実行しようとすると、次のエラーが表示されます。

Error: GDK_BACKEND does not match available displays

私の最初のアイデアは、実際に$ DISPLAYを次のように「-e」オプションでコンテナに渡すことでした。

docker run -ti -e DISPLAY=$DISPLAY name_of_docker_image

これは役立ちますが、問題は解決しません。エラーメッセージは次のように変わります。

Unable to init server: Broadway display type not supported: localhost:10.0
Error: cannot open display: localhost:10.0

Webを検索した後、認証を修正するためにいくつかのxauthマジックを実行できることがわかりました。以下を追加しました:

SOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
chmod 777 $XAUTH
docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \ 
  -e XAUTHORITY=$XAUTH name_of_docker_image

ただし、これはdockerコマンドに「-net Host」も追加した場合にのみ機能します。

docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \ 
  -e XAUTHORITY=$XAUTH --net Host name_of_docker_image

これは、ホストネットワーク全体がコンテナから見えるようにするため、望ましくありません。

「--net Host」なしでdockerのリモートサーバーで完全に実行するために、現在何が欠けていますか?

17
Ruben

私はそれを考え出した。 SSHでコンピューターに接続し、X11転送を使用している場合、/ tmp/.X11-unixはX通信に使用されず、$ XSOCKに関連する部分は不要です。

Xアプリケーションは、$ DISPLAYのホスト名(通常は「localhost」)を使用し、TCPを使用して接続します。次に、これはSSHクライアントにトンネリングされます。 Dockerに「--net Host」を使用する場合、「localhost」はDockerコンテナとDockerホストで同じであるため、正常に機能します。

「--net Host」を指定しない場合、Dockerはデフォルトのブリッジネットワークモードを使用しています。 つまり、「localhost」はコンテナ内のホスト以外のものを意味するであり、コンテナ内のXアプリケーションは「localhost」を参照してもXサーバーを認識できません。そのため、これを解決するには、「localhost」を実際のホストのIPアドレスに置き換える必要があります。これは通常、「172.17.0.1」または同様の値です。 「docker0」インターフェイスの「ip addr」を確認します。

これは、sed交換で行うことができます。

DISPLAY=`echo $DISPLAY | sed 's/^[^:]*\(.*\)/172.17.0.1\1/'`

さらに、SSHサーバーは通常、このX11トンネルへのリモート接続を受け入れるように構成されていません。これは/ etc/ssh/sshd_config(少なくともDebianでは)を編集して設定することで変更する必要があります:

X11UseLocalhost no

次に、SSHサーバーを再起動し、「ssh -X」を使用してサーバーに再ログインします。

これでほぼ完了しましたが、1つ問題が残っています。 Dockerホストでファイアウォールが実行されている場合、X11-tunnelに関連付けられているTCPポートを開く必要があります。ポート番号はで$ DISPLAYが6000に追加されました。

TCPポート番号を取得するには、次を実行できます。

X11PORT=`echo $DISPLAY | sed 's/^[^:]*:\([^\.]\+\).*/\1/'`
TCPPORT=`expr 6000 + $X11PORT`

次に(ufwをファイアウォールとして使用する場合)、172.17.0.0サブネットのDockerコンテナー用にこのポートを開きます。

ufw allow from 172.17.0.0/16 to any port $TCPPORT proto tcp

すべてのコマンドをまとめてスクリプトに入れることができます。

XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | Sudo xauth -f $XAUTH nmerge -
Sudo chmod 777 $XAUTH
X11PORT=`echo $DISPLAY | sed 's/^[^:]*:\([^\.]\+\).*/\1/'`
TCPPORT=`expr 6000 + $X11PORT`
Sudo ufw allow from 172.17.0.0/16 to any port $TCPPORT proto tcp 
DISPLAY=`echo $DISPLAY | sed 's/^[^:]*\(.*\)/172.17.0.1\1/'`
Sudo docker run -ti --rm -e DISPLAY=$DISPLAY -v $XAUTH:$XAUTH \
   -e XAUTHORITY=$XAUTH name_of_docker_image

あなたがrootではないため、Sudoを使用する必要があると仮定します。

の代わりに Sudo chmod 777 $XAUTH、次を実行できます。

Sudo chown my_docker_container_user $XAUTH
Sudo chmod 600 $XAUTH

/tmp/.docker.authファイルを作成した対象を知っている場合、サーバー上の他のユーザーもXサーバーにアクセスできないようにします。

これにより、ほとんどのシナリオで適切に機能することを期待しています。

21
Ruben