web-dev-qa-db-fra.com

Exécutez l'application X dans un conteneur Docker de manière fiable sur un serveur connecté via SSH sans "--net Host"

Sans conteneur Docker, il est simple d'exécuter un programme X11 sur un serveur distant en utilisant le transfert SSH X11 (ssh -X). J'ai essayé de faire fonctionner la même chose lorsque l'application s'exécute à l'intérieur d'un conteneur Docker sur un serveur. Lors de la connexion SSH à un serveur avec l'option -X, un tunnel X11 est configuré et la variable d'environnement "$ DISPLAY" est automatiquement définie sur "localhost: 10.0" ou similaire. Si j'essaye simplement d'exécuter une application X dans un Docker, j'obtiens cette erreur:

Error: GDK_BACKEND does not match available displays

Ma première idée était de réellement passer $ DISPLAY dans le conteneur avec l'option "-e" comme ceci:

docker run -ti -e DISPLAY=$DISPLAY name_of_docker_image

Cela aide, mais cela ne résout pas le problème. Le message d'erreur se transforme en:

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

Après avoir cherché sur le Web, j'ai compris que je pouvais faire de la magie xauth pour corriger l'authentification. J'ai ajouté ce qui suit:

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

Cependant, cela ne fonctionne que si vous ajoutez également "- net Host" à la commande docker:

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

Ce n'est pas souhaitable car cela rend l'ensemble du réseau hôte visible pour le conteneur.

Que manque-t-il maintenant pour le faire fonctionner pleinement sur un serveur distant dans un docker sans "--net Host"?

17
Ruben

Je l'ai compris. Lorsque vous vous connectez à un ordinateur avec SSH et utilisez le transfert X11, / tmp/.X11-unix n'est pas utilisé pour la communication X et la partie liée à $ XSOCK n'est pas nécessaire.

Toute application X utilise plutôt le nom d'hôte dans $ DISPLAY, généralement "localhost" et se connecte en utilisant TCP. Ceci est ensuite retransmis en tunnel vers le client SSH. Lorsque vous utilisez "--net Host" pour le Docker, "localhost" sera le même pour le conteneur Docker que pour le Docker Host et, par conséquent, cela fonctionnera correctement.

Lorsqu'il ne spécifie pas "--net Host", le Docker utilise le mode réseau de pont par défaut. Cela signifie que "localhost" signifie autre chose à l'intérieur du conteneur que pour l'hôte, et les applications X à l'intérieur du conteneur ne pourront pas voir le serveur X en se référant à "localhost". Donc, pour résoudre ce problème, il faudrait remplacer "localhost" par l'adresse IP réelle de l'hôte. Il s'agit généralement de "172.17.0.1" ou similaire. Vérifiez "ip addr" pour l'interface "docker0".

Cela peut être fait avec un remplacement sed:

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

En outre, le serveur SSH n'est généralement pas configuré pour accepter les connexions à distance à ce tunnel X11. Cela doit ensuite être changé en éditant / etc/ssh/sshd_config (au moins dans Debian) et en définissant:

X11UseLocalhost no

puis redémarrez le serveur SSH et reconnectez-vous au serveur avec "ssh -X".

C'est presque ça, mais il reste une complication. Si un pare-feu est en cours d'exécution sur l'hôte Docker, le port TCP associé au tunnel X11 doit être ouvert. Le numéro de port est le numéro entre le : et le . dans $ DISPLAY ajouté à 6000.

Pour obtenir le numéro de port TCP, vous pouvez exécuter:

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

Ensuite (si vous utilisez ufw comme pare-feu), ouvrez ce port pour les conteneurs Docker dans le sous-réseau 172.17.0.0:

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

Toutes les commandes peuvent être regroupées dans un script:

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

En supposant que vous n'êtes pas root et que vous devez donc utiliser Sudo.

Au lieu de Sudo chmod 777 $XAUTH, vous pouvez exécuter:

Sudo chown my_docker_container_user $XAUTH
Sudo chmod 600 $XAUTH

pour empêcher d'autres utilisateurs du serveur d'accéder également au serveur X s'ils savent pour quoi vous avez créé le fichier /tmp/.docker.auth.

J'espère que cela devrait le faire fonctionner correctement pour la plupart des scénarios.

21
Ruben