Linux

Как запускать приложения с графическим интерфейсом в контейнере Linux Docker

Существуют ли образы, которые устанавливают vncserver или что-то подобное, чтобы вы могли, например, добавить дополнительную песочницу вокруг, скажем, Firefox?

 

Ответ 1

Вы можете просто установить vncserver вместе с Firefox :)

Я выложил образ vnc/firefox здесь: docker pull creack/firefox-vnc

Образ был создан с помощью этого Dockerfile:

# Firefox over VNC

#

# VERSION               0.1

# DOCKER-VERSION        0.2

 FROM    ubuntu:12.04

# Убедитесь, что репозиторий пакетов обновлен.

RUN     echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list

RUN     apt-get update

 # Установите vnc, xvfb для создания "фальшивого" дисплея и firefox

RUN     apt-get install -y x11vnc xvfb firefox

RUN     mkdir ~/.vnc

# установите пароль

RUN     x11vnc -storepasswd 1234 ~/.vnc/passwd

# Автозапуск firefox (возможно, это не самый лучший способ)

RUN     bash -c 'echo "firefox" >> /.bashrc'

Это создаст контейнер Docker с VNC с паролем 1234:

Для Docker версии 1.8 или новее:

docker run -p 5900:5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

Для Docker версии 1.3 или новее:

docker run -p 5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

Для Docker до версии 1.3:

docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create

 

Ответ 2

Xauthority становится проблемой в новых системах. Я могу либо отказаться от любой защиты с помощью xhost перед запуском моих контейнеров докеров, либо передать хорошо подготовленный файл Xauthority. Типичные файлы Xauthority зависят от имени хоста. С докером каждый контейнер может иметь другое имя хоста (устанавливается с помощью docker run -h), но даже установка имени хоста контейнера, идентичного хост-системе, в моем случае не помогла. xeyes (мне нравится этот пример) просто проигнорирует файл cookie и не передаст учетные данные серверу. Следовательно, мы получаем сообщение об ошибке «Протокол не указан. Невозможно открыть дисплей».

Файл Xauthority можно записать таким образом, чтобы имя хоста не имело значения. Нам нужно установить для семейства аутентификации значение «FamilyWild». Я не уверен, есть ли у xauth подходящая для этого командная строка, поэтому вот пример, который сочетает xauth и sed для этого. Нам нужно изменить первые 16 бит вывода nlist. Значение FamilyWild 65535 или 0xffff.

docker build -t xeyes - << __EOF__

FROM debian

RUN apt-get update

RUN apt-get install -qqy x11-apps

ENV DISPLAY :0

CMD xeyes

__EOF__

XSOCK=/tmp/.X11-unix

XAUTH=/tmp/.docker.xauth

xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

docker run -ti -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH xeyes

 

Ответ 3

С томами данных докеров очень легко открыть сокет домена xorg unix внутри контейнера.

Например, с таким Dockerfile:

FROM debian

RUN apt-get update

RUN apt-get install -qqy x11-apps

ENV DISPLAY :0

CMD xeyes

Вы могли сделать следующее:

$ docker build -t xeyes - < Dockerfile

$ XSOCK=/tmp/.X11-unix/X0

$ docker run -v $XSOCK:$XSOCK xeyes

Это, конечно, по сути, то же самое, что и X-forwarding. Он предоставляет контейнеру полный доступ к xserver на хосте, поэтому рекомендуется только в том случае, если вы доверяете тому, что находится внутри.

Примечание. Если вас беспокоит безопасность, лучшим решением будет ограничить приложение обязательным или ролевым контролем доступа. Docker обеспечивает довольно хорошую изоляцию, но он был разработан с другой целью. Используйте AppArmorSELinux или GrSecurity, которые были разработаны для решения вашей проблемы.

 

Ответ 4

OSX

Есть ответ, который сработал для меня в Ubuntu, однако в OSX докер работает внутри VirtualBox, поэтому решение не работает без дополнительной работы.

У меня это работает с этими дополнительными ингредиентами:

  1. Xquartz (OSX больше не поставляется с сервером X11)

  2. переадресация сокетов с помощью socat (brew install socat)

  3. bash скрипт для запуска контейнера

Я не уверен, что переадресация сокетов для X безопасна, но я предполагаю использовать это только для локального запуска контейнера докеров.

Кроме того, сценарий немного хрупок в том смысле, что получить IP-адрес машины непросто, поскольку она подключена к нашей локальной беспроводной сети, поэтому всегда используется случайный IP-адрес.

Скрипт BASH, который я использую для запуска контейнера:

#!/usr/bin/env bash

 CONTAINER=py3:2016-03-23-rc3

COMMAND=/bin/bash

NIC=en0

# Получите ip-адрес

IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')

DISP_NUM=$(jot -r 1 100 200)  # случайное отображение числа между 100 и 200

PORT_NUM=$((6000 + DISP_NUM)) # чтобы несколько экземпляров контейнера не мешали друг другу

socat TCP-LISTEN:${PORT_NUM},reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 2>&1 > /dev/null &

XSOCK=/tmp/.X11-unix

XAUTH=/tmp/.docker.xauth.$USER.$$

touch $XAUTH

xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

docker run \

    -it \

    --rm \

    --user=$USER \

    --workdir="/Users/$USER" \

    -v "/Users/$USER:/home/$USER:rw" \

    -v $XSOCK:$XSOCK:rw \

    -v $XAUTH:$XAUTH:rw \

    -e DISPLAY=$IPADDR:$DISP_NUM \

    -e XAUTHORITY=$XAUTH \

    $CONTAINER \

    $COMMAND

 rm -f $XAUTH

 

kill %1       # завершить задание socat, запущенное выше

Я могу заставить xeyes и matplotlib работать с этим подходом.

Windows 7+

В Windows 7+ это немного проще с MobaXterm:

  1. Установите MobaXterm для Windows

  2. Запустите MobaXterm

  3. Настройте X-сервер: Настройки -> X11 (вкладка) -> установите удаленный доступ X11 на полный

  4. Используйте этот сценарий BASH для запуска контейнера:

run_docker.bash:

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3

COMMAND=/bin/bash

DISPLAY="$(hostname):0"

USER=$(whoami)

docker run \

    -it \

    --rm \

    --user=$USER \

    --workdir="/home/$USER" \

    -v "/c/Users/$USER:/home/$USER:rw" \

    -e DISPLAY \

    $CONTAINER \

    $COMMAND

 

Ответ 5

Совместное использование отображения хоста, как указано в некоторых других ответах, имеет два недостатка:

  • Нарушается изоляция контейнера из-за некоторых утечек безопасности X. Например, кейлоггинг с помощью xev или xinput возможен и удаленное управление хост-приложениями с помощью xdotool.

  • Приложения могут иметь сбои рендеринга и ошибки плохого доступа к ОЗУ из-за отсутствия общей памяти для расширения X MIT-SHM (также можно исправить с помощью опции снижения уровня изоляции --ipc=host).

Ниже приведен пример сценария для запуска образа докера в Xephyr, который решает эту проблему.

  • Это позволяет избежать утечек безопасности X, поскольку приложения-докеры работают на вложенном X-сервере.

  • MIT-SHM отключен, чтобы избежать сбоев доступа к ОЗУ.

  • Безопасность контейнеров улучшена с помощью --cap-drop ALL --security-opt no-new-privileges. Также пользователь контейнера не является пользователем root.

  • X cookie создается для ограничения доступа к дисплею Xephyr.

Сценарий ожидает, что некоторые аргументы: сначала оконный менеджер хоста будет запущен в Xephyr, второй образ докера, и, возможно, третий будет выполнена команда изображения. Чтобы запустить среду рабочего стола в докере, используйте ":" вместо оконного менеджера хоста.

Закрытие окна Xephyr завершает работу приложений контейнера докеров. Завершение закрепленных приложений закрывает окно Xephyr.

Примеры:

  • xephyrdocker "openbox --sm-disable" x11docker/lxde pcmanfm

  • xephyrdocker : x11docker/lxde

  • xephyrdocker xfwm4 --device /dev/snd jess/nes /games/zelda.rom

 

xephyrdocker скрипт:

#! /bin/bash

#

# Xephyrdocker: Пример сценария для запуска приложений docker GUI в Xephyr.

#

# Применение:

#   Xephyrdocker WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]

#

# WINDOWMANAGER хост-менеджер окон для использования с одиночными GUI-приложениями.

# Для запуска без оконного менеджера с хоста используйте ":".

# DOCKERIMAGE докер-образ, содержащий приложения с графическим интерфейсом или рабочий стол.

# IMAGECOMMAND команда для запуска в образе

#

Windowmanager="$1" && shift

Dockerimage="$*"

 

# пользователь контейнера

Useruid=$(id -u)

Usergid=$(id -g)

Username="$(id -un)"

[ "$Useruid" = "0" ] && Useruid=1000 && Usergid=1000 && Username="user$Useruid"

 

# Найдите свободный номер дисплея

for ((Newdisplaynumber=1 ; Newdisplaynumber <= 100 ; Newdisplaynumber++)) ; do

  [ -e /tmp/.X11-unix/X$Newdisplaynumber ] || break

done

Newxsocket=/tmp/.X11-unix/X$Newdisplaynumber

# кэш-папка и файлы

Cachefolder=/tmp/Xephyrdocker_X$Newdisplaynumber

[ -e "$Cachefolder" ] && rm -R "$Cachefolder"

mkdir -p $Cachefolder

Xclientcookie=$Cachefolder/Xcookie.client

Xservercookie=$Cachefolder/Xcookie.server

Xinitrc=$Cachefolder/xinitrc

Etcpasswd=$Cachefolder/passwd

# команда для запуска docker

# --rm                                                 созданный контейнер будет отброшен.

# -e DISPLAY=$Newdisplay               установить переменную окружения для нового дисплея

# -e XAUTHORITY=/Xcookie             установить переменную окружения XAUTHORITY на предоставленный файл cookie

# -v $Xclientcookie:/Xcookie:ro          предоставить файл cookie контейнеру

# -v $NewXsocket:$NewXsocket:ro   Поделиться новым X-образным сокетом Xephyr

# --user $Useruid:$Usergid                Безопасность: избегать root в контейнере

# -v $Etcpasswd:/etc/passwd:ro         /etc/passwd файл с записью пользователя

# --group-add audio                           Разрешите доступ к /dev/snd при совместном использовании с '--device /dev/snd' 

# --cap-drop ALL                                Безопасность: отключите ненужные возможности

# --security-opt no-new-privileges     Безопасность: запретить новые привилегии

Dockercommand="docker run --rm \

  -e DISPLAY=:$Newdisplaynumber \

  -e XAUTHORITY=/Xcookie \

  -v $Xclientcookie:/Xcookie:ro \

  -v $Newxsocket:$Newxsocket:rw \

  --user $Useruid:$Usergid \

  -v $Etcpasswd:/etc/passwd:ro \

  --group-add audio \

  --env HOME=/tmp \

  --cap-drop ALL \

  --security-opt no-new-privileges \

  $(command -v docker-init >/dev/null && echo --init) \

  $Dockerimage"

 

echo "docker command: 

$Dockercommand

"

# command to run Xorg or Xephyr

# /usr/bin/Xephyr                       для xinit должен быть указан абсолютный путь к исполняемому файлу X-сервера

# :$Newdisplaynumber             первый аргумент должен быть новым дисплеем

# -auth $Xservercookie             путь к файлу cookie для X-сервера. Должен отличаться от файла cookie клиента, не знаю почему.

# -extension MIT-SHM              отключить MIT-SHM, чтобы избежать глюков рендеринга и плохого доступа к оперативной памяти (+ вместо - включает его)

# -nolisten tcp                           отключить соединения tcp в целях безопасности

# -retro                                      красивый вид в стиле ретро

Xcommand="/usr/bin/Xephyr :$Newdisplaynumber \

  -auth $Xservercookie \

  -extension MIT-SHM \

  -nolisten tcp \

  -screen 1000x750x24 \

  -retro"

 

echo "X server command:

$Xcommand

"

# создать /etc/passwd с непривилегированным пользователем

echo "root:x:0:0:root:/root:/bin/sh" >$Etcpasswd

echo "$Username:x:$Useruid:$Usergid:$Username,,,:/tmp:/bin/sh" >> $Etcpasswd

 

# создать xinitrc

{ echo "#! /bin/bash"

 

  echo "# установить переменные окружения на новый дисплей и новый cookie"

  echo "export DISPLAY=:$Newdisplaynumber"

  echo "export XAUTHORITY=$Xclientcookie"

  echo "# та же раскладка клавиатуры, что и на хосте"

  echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - :$Newdisplaynumber"

 

  echo "# создать новый файл cookie XAUTHORITY" 

  echo ":> $Xclientcookie"

  echo "xauth add :$Newdisplaynumber . $(mcookie)"

  echo "# создать подготовленный файл cookie с идентификацией localhost, отключенной по ffff,".

  echo "# необходим, если X-сокет используется совместно, а не подключается по tcp. ffff означает "familiy wild"".

  echo 'Cookie=$(xauth nlist '":$Newdisplaynumber | sed -e 's/^..../ffff/')" 

  echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'

  echo "cp $Xclientcookie $Xservercookie"

  echo "chmod 644 $Xclientcookie"

  echo "# запустить оконный менеджер в Xephyr".

  echo $Windowmanager' & Windowmanagerpid=$!'

  echo "# show docker log"

  echo 'tail --retry -n +1 -F '$Dockerlogfile' 2>/dev/null & Tailpid=$!'

  echo "# run docker"

  echo "$Dockercommand"

} > $Xinitrc

xinit  $Xinitrc -- $Xcommand

rm -Rf $Cachefolder

Этот сценарий поддерживается в x11docker.

 

 

Схожие статьи

Linux

Как записать stderr в файл при использовании «tee» с пайпом

Linux

Может ли scp рекурсивно копировать каталоги?

Linux

Как сделать так, чтобы дочерний процесс завершался после завершения родительского

Как пользоваться Wine: основы работы для начинающих и полезные советы
Linux

Как пользоваться Wine: основы работы для начинающих и полезные советы