0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

安全にDockerからChromeをホストPCに表示する方法:xhost +を使わないアプローチ

Last updated at Posted at 2024-08-16

はじめに

xhost +を使うことなしに、安全にDockerで起動したChromeをホストPCに表示する方法をまとめる。

実現方法のまとめ

「ホストPCのユーザID」と「DockerでChromeを実行するユーザID」を揃えることで.Xauthorityを使いDockerのChromeをxhost +なしでホストPCに表示する

1.適当なディレクトリに Dockerfile を作成する

Dockerfile
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive
ENV DISPLAY=:0
ENV PULSE_SERVER=unix:/run/user/1000/pulse/native

# 必要なパッケージをインストール
RUN apt-get update && \
    apt-get install -y wget pulseaudio socat alsa-base \
    libcanberra-gtk-module libcanberra-gtk3-module \
    fonts-ipafont-gothic fonts-ipafont-mincho fonts-noto-cjk \
    language-pack-ja dbus-x11 && \
    wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \
    apt-get install -y ./google-chrome-stable_current_amd64.deb --no-install-recommends && \
    apt-get install -y x11-apps mesa-utils sudo &&\
    apt-get clean && rm -rf /var/lib/apt/lists/*

RUN echo "#!/bin/bash\n\
set -e\n\
sudo /etc/init.d/dbus start || { echo 'Failed to start dbus'; exit 1; }\n\
export XDG_RUNTIME_DIR=/run/user/\$(id -u)\n\
sudo mkdir -p \$XDG_RUNTIME_DIR || { echo 'Failed to create XDG_RUNTIME_DIR'; exit 1; }\n\
sudo chmod 700 \$XDG_RUNTIME_DIR || { echo 'Failed to chmod XDG_RUNTIME_DIR'; exit 1; }\n\
sudo chown \$(id -un):\$(id -gn) \$XDG_RUNTIME_DIR || { echo 'Failed to chown XDG_RUNTIME_DIR'; exit 1; }\n\
export DBUS_SESSION_BUS_ADDRESS=unix:path=\$XDG_RUNTIME_DIR/bus\n\
dbus-daemon --session --address=\$DBUS_SESSION_BUS_ADDRESS --nofork --nopidfile --syslog-only &\n\
exec \"\$@\"" > /start.sh && chmod +x /start.sh

# ホストのUIDとGIDを利用してユーザーを作成する
ARG USER_ID
ARG GROUP_ID

# 既存のグループを確認して、なければ作成
RUN if ! getent group $GROUP_ID >/dev/null; then \
        groupadd -g $GROUP_ID hostgroup; \
    fi

# 既存のUIDに対応するユーザーがいない場合、新規ユーザーを作成して、PWなしでsudoを許可
RUN USER_NAME=$(getent passwd $USER_ID | cut -d: -f1) && \
    if [ -z "$USER_NAME" ]; then \
        USER_NAME="hostuser"; \
        useradd -u $USER_ID -g $GROUP_ID -m $USER_NAME; \
    fi && \
    echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# google-chrome を $USER_ID で実行
USER $USER_ID
CMD ["/start.sh", "google-chrome", "--disable-dev-shm-usage"]

2.Dockerイメージを作成する

ubuntu24_chrome3という名前でDockerイメージを作成する

ホストPCのユーザIDとグループIDを指定してDockerイメージを作成する
docker build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) -t ubuntu24_chrome3 .

3.Dockerコンテナを起動する

Chromeのサンドボックスに対応するため特権コンテナとして実行し、Chromeが起動することを確認する

docker run -it --rm \
  --name ubuntu24_chrome3 \
  --env="DISPLAY=$DISPLAY" \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  --env="PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native" \
  --volume="$HOME/.Xauthority:/home/ubuntu/.Xauthority:rw" \
  --volume="${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native" \
  --volume="/etc/machine-id:/etc/machine-id:ro" \
  --volume="/run/user/$(id -u)/pulse:/run/user/$(id -u)/pulse" \
  --network=host \
  --device /dev/dri \
  --cap-add=SYS_ADMIN --security-opt seccomp=unconfined \
  ubuntu24_chrome3

youtube2024.gif


xhost +の危険性

xhost +がよく使われる理由

前回までの記事では xhost +を使い手軽にDockerでGUIアプリをホストPCに表示させた。コンテナからホストのXサーバーに接続する場合、xhost +は、この接続を簡単に許可できる。これを使えば、設定なしでどんなコンテナからもアプリを表示できます。手軽さから、開発やテストでよく使われる

xhost +の危険性

xhost +は便利だが、セキュリティに大きな穴を開ける。全ての接続を許可するため、誰でもホストPCの画面にアクセスできる。

  • セッション乗っ取りのリスク
  • データの盗難
  • システムの破壊

これらのリスクを避けるため、開発やテスト以外ではxhost +は使わない方が良い。


xhost +を使わずGUIアプリをホストPCで表示する

前回の記事でxhost +を使わない場合

前回の記事のDockerファイルを利用して作成したイメージを実行した場合、xhost +を使わないと下記のように、X serverが見つからないとのエラーが発生する

Missing X server or $DISPLAY
[1:1:0816/051448.624023:ERROR:ozone_platform_x11.cc(244)] Missing X server or $DISPLAY

解決策1: xhost +local:

ホストPCで xhost + の代わりに、xhost +local:を実行する。リスクは残るがシンプルな解決策である。

ローカルマシン上のユーザに限ってアクセスを許可
xhost +local:

xhost +local:は、ローカルマシン上のユーザー間でXサーバーにアクセスを許可する場合に使用される。ネットワーク経由でのアクセスが制限されるため、リスクはxhost +よりも低いが、ローカルマシンの他のユーザーがXサーバーにアクセスできるリスクは残る。

xhost +local: を取り消す
xhost -local:

解決策2: .Xauthorityを利用する

.Xauthorityを利用しする場合、コンテナ内のユーザーとホストPCのユーザーの設定が一致しなければならない。

用意する Dockerfile

FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive
ENV DISPLAY=:0
ENV PULSE_SERVER=unix:/run/user/1000/pulse/native

# 必要なパッケージをインストール
RUN apt-get update && \
    apt-get install -y wget pulseaudio socat alsa-base \
    libcanberra-gtk-module libcanberra-gtk3-module \
    fonts-ipafont-gothic fonts-ipafont-mincho fonts-noto-cjk \
    language-pack-ja dbus-x11 && \
    wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \
    apt-get install -y ./google-chrome-stable_current_amd64.deb --no-install-recommends && \
    apt-get install -y x11-apps mesa-utils sudo &&\
    apt-get clean && rm -rf /var/lib/apt/lists/*

RUN echo "#!/bin/bash\n\
set -e\n\
sudo /etc/init.d/dbus start || { echo 'Failed to start dbus'; exit 1; }\n\
export XDG_RUNTIME_DIR=/run/user/\$(id -u)\n\
sudo mkdir -p \$XDG_RUNTIME_DIR || { echo 'Failed to create XDG_RUNTIME_DIR'; exit 1; }\n\
sudo chmod 700 \$XDG_RUNTIME_DIR || { echo 'Failed to chmod XDG_RUNTIME_DIR'; exit 1; }\n\
sudo chown \$(id -un):\$(id -gn) \$XDG_RUNTIME_DIR || { echo 'Failed to chown XDG_RUNTIME_DIR'; exit 1; }\n\
export DBUS_SESSION_BUS_ADDRESS=unix:path=\$XDG_RUNTIME_DIR/bus\n\
dbus-daemon --session --address=\$DBUS_SESSION_BUS_ADDRESS --nofork --nopidfile --syslog-only &\n\
exec \"\$@\"" > /start.sh && chmod +x /start.sh

# ホストのUIDとGIDを利用してユーザーを作成する
ARG USER_ID
ARG GROUP_ID

# 既存のグループを確認して、なければ作成
RUN if ! getent group $GROUP_ID >/dev/null; then \
        groupadd -g $GROUP_ID hostgroup; \
    fi

# 既存のUIDに対応するユーザーがいない場合、新規ユーザーを作成して、PWなしでsudoを許可
RUN USER_NAME=$(getent passwd $USER_ID | cut -d: -f1) && \
    if [ -z "$USER_NAME" ]; then \
        USER_NAME="hostuser"; \
        useradd -u $USER_ID -g $GROUP_ID -m $USER_NAME; \
    fi && \
    echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# google-chrome を $USER_ID で実行
USER $USER_ID
CMD ["/start.sh", "google-chrome", "--disable-dev-shm-usage"]

Dockerfileの変更点

項目 前回 今回 変更理由と影響
追加パッケージ google-chrome-stablepulseaudioなどの基本パッケージをインストール 追加でx11-appsmesa-utilssudoをインストール。 - x11-appsmesa-utilsの追加により、X11およびOpenGLアプリケーションのデバッグや動作確認が可能にした
- sudoの追加で、ユーザーが管理者権限でコマンドを実行可能にした。
DBus設定スクリプトの改良 Chrome実行時のエラー解決の設定 Chromeの実行ユーザがrootでない場合に対応 sudoの使用により、ルート権限が必要な操作を安全に実行できる
ホストのUID/GIDに基づくユーザー作成 n/a ホストのUID/GIDを使用してコンテナ内にユーザーを作成 .Xauthorityを用いてXサーバーに接続するため
google-chromeの実行ユーザ root ホストのUID - .Xauthorityを用いてXサーバーに接続するため 
- Chromeを-no-sandboxなしで実行する

Dockerイメージの作成

ubuntu24_chrome3という名前をつけてイメージを作成する

ホストPCのユーザIDとグループIDを指定してDockerイメージを作成する
docker build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) -t ubuntu24_chrome3 .

Dockerコンテナの起動の失敗

DockerでChromeを前回と同様に起動しようとするとエラーが発生する。

Chromeの起動はエラー
docker run -it --rm \
  --name ubuntu24_chrome3 \
  --env="DISPLAY=$DISPLAY" \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  --env="PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native" \
  --volume="$HOME/.Xauthority:/home/ubuntu/.Xauthority:rw" \
  --volume="${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native" \
  --volume="/etc/machine-id:/etc/machine-id:ro" \
  --volume="/run/user/$(id -u)/pulse:/run/user/$(id -u)/pulse" \
  --network=host \
  --device /dev/dri \
  ubuntu24_chrome3
エラー内容
 * Starting system message bus dbus [ OK ] 
Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted
[1:1:0816/055842.050671:FATAL:zygote_host_impl_linux.cc(201)] Check failed: . : Operation not permitted (1)

Dockerコンテナの起動失敗の原因の推察

Chromeは、セキュリティのために「サンドボックス」機能を使用しており、プロセスを分離するために名前空間を使用する。しかし、Dockerコンテナ内では、これらの操作に必要な権限が制限されているため、Chromeのサンドボックス機能が適切に動作せず、エラーが発生しているものと考えられる。
Chromeの起動するユーザをrootからホストPCのユーザと同一のユーザIDに変更した際に、DockerfileのCMDで--no-sandboxを削除したことが原因だと考えられる。

Xeyesなら起動は成功

Xeyesは起動する
docker run -it --rm \
  --name ubuntu24_chrome3 \
  --env="DISPLAY=$DISPLAY" \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  --env="PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native" \
  --volume="$HOME/.Xauthority:/home/ubuntu/.Xauthority:rw" \
  --volume="${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native" \
  --volume="/etc/machine-id:/etc/machine-id:ro" \
  --volume="/run/user/$(id -u)/pulse:/run/user/$(id -u)/pulse" \
  --network=host \
  --device /dev/dri \
  ubuntu24_chrome3 /usr/bin/xeyes

image.png

特権をつけてDockerコンテナを起動

Chromeのサンドボックスに対応するため特権コンテナとして実行し、Chromeが起動することを確認する

docker run -it --rm \
  --name ubuntu24_chrome3 \
  --env="DISPLAY=$DISPLAY" \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  --env="PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native" \
  --volume="$HOME/.Xauthority:/home/ubuntu/.Xauthority:rw" \
  --volume="${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native" \
  --volume="/etc/machine-id:/etc/machine-id:ro" \
  --volume="/run/user/$(id -u)/pulse:/run/user/$(id -u)/pulse" \
  --network=host \
  --device /dev/dri \
  --privileged \
  ubuntu24_chrome3

youtube2024.gif

まとめ

DockerでGUIアプリを使うとき、xhost +は便利なのだが、非常に危険でもある。ある程度のリスクが許容できるなら xhost +local:を選択、そうでない場合には /.Xauthority を使うことを考えると良い。

ただし、Chromeのように特権が必要な場合には、コンテナ内で悪意のあるコードが実行された場合、ホストシステム全体に影響を及ぼす可能性があり、別のセキュリティの懸念が発生する。

--privilegedの代わりに--cap-add=SYS_ADMIN --security-opt seccomp=unconfinedを指定することで、部分的な特権付与でセキュリティの懸念を減じるほうが良いだろう。

蛇足

--volume="$HOME/.Xauthority:/home/ubuntu/.Xauthority:rw"でマウントするユーザのホームディレクトリ(/home/ubuntu/)は下記のようにして確認できる

docker run -it --rm \
  --name ubuntu24_chrome3 \
  ubuntu24_chrome3 /bin/bash -c 'echo $HOME'
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?