LXC 1.0: コンテナーの中でGUI [9/10]

## この文書について

この文書は、連載記事「[LXC 1.0: Blog post series](https://www.stgraber.org/2013/12/20/lxc-1-0-blog-post-series/)」の一つである以下の記事を翻訳したものです。連載の目次や注意点は[こちら](http://qiita.com/m-shibata/items/ee9b295a2596824da819)を参照してください。

* https://www.stgraber.org/2014/02/09/lxc-1-0-gui-in-containers/

この文書のライセンスは原文と同じく、[Creative Commons BY-NC-SA 2.5](http://creativecommons.org/licenses/by-nc-sa/2.5/)のもとに提供されています。

![CC-BY-NC-SA 2.5](http://i.creativecommons.org/l/by-nc-sa/2.5/88x31.png)

---------

## 最初にいくつかの注意点

この記事は[非特権コンテナー](https://www.stgraber.org/2014/01/17/lxc-1-0-unprivileged-containers/)（[日本語版記事](http://qiita.com/m-shibata/items/698d92d67c7a76612009)）を使用しています。それは必須ではありませんが、GUIアプリケーションを使うなら納得がいく話でしょう。ところで、この連載記事を読んでいるなら、その設定は済んでいると思って問題ありませんね？

私はGoogle Chromeを、ソフトウェアの「敵」の一部であるGoogle TalkやAdobe Flashプラグインと一緒に使っており、私はこれらを私のPC上で直接使用したくないと考えています。理由は次のとおりです：

* これらはバイナリであり、ソースを持っていません。
  （これだけでもアプリケーションをコンテナーの中で動かす理由としては充分です）
* 外部の（Ubuntuではない）リポジトリからやってきて、システムに何かをインストールします。
  （APT PININGである程度制限することは可能です）
* リポジトリやGPG鍵を何らかの理由で削除した時、再度追加するためのデイリーのcronjobをインストールします
  （dist-upgradeのあとに再度リポジトリが追加されていることからも明らかです）
* サンドボックスを設定するためにsetuidのラッパーを使用しています。まだ広く使われていない名前空間やユーザー名前空間に移行しないことは理解できます。
  （これはサンドボックスをオフにすることで回避可能です。サンドボックスのコードはChromiumプロジェクトからも利用可能ではありますが、バイナリをビルドした時に何も追加していないという保証はどこにもありません）
* 私の仕事環境やFlashからはまだ完全に逃れられないので、私はそれらのソフトウェアを依然として使う必要があります。

以下では、私の意見を基にした数多くのセキュリティ設定について説明しますが、いまだ最良といえるものではなく、ソフトウェアを動作させるためにいくつかの妥協が含まれています。基本的に利用できることは次のとおりです：

* pulseaudio：おそらく録音できます
* X11：おそらくキーのログや画面の撮影は可能です
* dri、サウンドデバイス：最初の二つ以外のものは思いつきませんが、私よりも想像力豊かな人が何か思いつくだろうと思います

できることは限られていますが、それでも素晴らしい機能を備えているので、センシティブな作業を行っている時はコンテナーを実行しない方が良いでしょう（訳注：非特権コンテナーにより機能制限を行っているもののソースのないバイナリを動かすことに対して注意喚起を行っている、ように読み取りました）。


## コンテナーの中でGoogle Chromeを動かす

実際にやってみましょう。計画はごくごくシンプルなものです。安定的に動作し、十分サポートされたバージョンのUbuntuのコンテナーを用意し、その中でGoogle Chromeと必要なプラグインをインストールし、デスクトップに導入します。

まず初めに、多くのソフトウェアのベンダーにサポートされているUbuntu 12.04 i386のコンテナーを用意しましょう。

```
lxc-create -t download -n precise-gui -- -d ubuntu -r precise -a i386
```

さらに `~/.local/share/lxc/precise-gui/config` にちょっとした設定を追加します（ `USERNAME` の部分は適切な値に変更してください）：

```
lxc.mount.entry = /dev/dri dev/dri none bind,optional,create=dir
lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir
lxc.mount.entry = /tmp/.X11-unix tmp/.X11-unix none bind,optional,create=dir
lxc.mount.entry = /dev/video0 dev/video0 none bind,optional,create=file

lxc.hook.pre-start = /home/USERNAME/.local/share/lxc/precise-gui/setup-pulse.sh
```

同じ設定ファイルの中出、いくつかの設定を置き換える必要があるでしょう：

```
lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536
```

次のように変更します（ここではあなた自身のユーザーのUID/GIDを1000/1000であると仮定しています）：

```
lxc.id_map = u 0 100000 1000
lxc.id_map = g 0 100000 1000
lxc.id_map = u 1000 1000 1
lxc.id_map = g 1000 1000 1
lxc.id_map = u 1001 101001 64535
lxc.id_map = g 1001 101001 64535
```

このマッピングの意味は、コンテナーの0から65535までの65536個のUIDをマッピングするということです。これらは一つの例外を除いてホスト上の10000から165535までにマッピングされます。UIDとGIDが1000は変換されません。これは、コンテナーの中のユーザーがXのソケットやpulseaudioのソケット、DRIやサウンドデバイスに同じようにアクセスするために必要な方法です（これによりホスト上での設定の量が減ります）。

設定ファイルはできたので、 `setup-pulse.sh`スクリプトを作成しましょう：

```sh
#!/bin/sh
PULSE_PATH=$LXC_ROOTFS_PATH/home/ubuntu/.pulse_socket

if [ ! -e "$PULSE_PATH" ] || [ -z "$(lsof -n $PULSE_PATH 2>&1)" ]; then
    pactl load-module module-native-protocol-unix auth-anonymous=1 \
        socket=$PULSE_PATH
fi
```

このファイルには実行権限を付けてください。そうしないLXCはこれを無視してしまいます！

スクリプト自体はとてもシンプルです。コンテナーの中の `/home/ubuntu/.pulse_socket` が既に設定されていないかどうかを確認した上で、それをバインドするようホスト上のpulseaudioに伝えています。

最後に、コンテナーのホームディレクトリのパーミッションを設定します：

```
sudo chown -R 1000:1000 ~/.local/share/lxc/precise-gui/rootfs/home/ubuntu
```

LXC側の設定はこれですべてです。コンテナーを起動し、Google ChromeとGoogle Talkプラグインをインストールしましょう：

```
lxc-start -n precise-gui -d
lxc-attach -n precise-gui -- umount /tmp/.X11-unix
lxc-attach -n precise-gui -- apt-get update
lxc-attach -n precise-gui -- apt-get dist-upgrade -y
lxc-attach -n precise-gui -- apt-get install wget ubuntu-artwork dmz-cursor-theme ca-certificates pulseaudio -y
lxc-attach -n precise-gui -- wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb -O /tmp/chrome.deb
lxc-attach -n precise-gui -- wget https://dl.google.com/linux/direct/google-talkplugin_current_i386.deb -O /tmp/talk.deb
lxc-attach -n precise-gui -- dpkg -i /tmp/chrome.deb /tmp/talk.deb
lxc-attach -n precise-gui -- apt-get -f install -y
lxc-attach -n precise-gui -- sudo -u ubuntu mkdir -p /home/ubuntu/.pulse/
echo "disable-shm=yes" | lxc-attach -n precise-gui -- sudo -u ubuntu tee /home/ubuntu/.pulse/client.conf
lxc-stop -n precise-gui
```

この時点でコンテナーに必要なものはすべてインストールされています。さらに簡単にするために、起動スクリプトも作成しましょう。「 `start-chrome` 」という名前のこれは、コンテナーの設定ディレクトリ（ `config` や `setup-pulse.sh` と同じところ）に保存します：

```sh
#!/bin/sh
CONTAINER=precise-gui
CMD_LINE="google-chrome --disable-setuid-sandbox $*"

STARTED=false

if ! lxc-wait -n $CONTAINER -s RUNNING -t 0; then
    lxc-start -n $CONTAINER -d
    lxc-wait -n $CONTAINER -s RUNNING
    STARTED=true
fi

PULSE_SOCKET=/home/ubuntu/.pulse_socket

lxc-attach --clear-env -n $CONTAINER -- sudo -u ubuntu -i \
    env DISPLAY=$DISPLAY PULSE_SERVER=$PULSE_SOCKET $CMD_LINE

if [ "$STARTED" = "true" ]; then
    lxc-stop -n $CONTAINER -t 10
fi
```

このスクリプトは実行権限がついていないと、次のステップが上手く動きません。このスクリプトは、コンテナーが起動中かどうか確認し、起動していなければ起動します（そしてその状態を記憶します）。そして適切な環境変数を設定した上で（さらに不透明ないくつかの理由やそれがユーザー名前空間ではないという理由から組み込みのサンドボックス機能を無効化した上で）google-chromeを起動し、google-chromeが修了すればコンテナーを停止しています。

より便利にするために、次のような内容の `~/.local/share/applications/google-chrome.desktop` も作成しましょう：

```
[Desktop Entry]
Version=1.0
Name=Google Chrome
Comment=Access the Internet
Exec=/home/USERNAME/.local/share/lxc/precise-gui/start-chrome %U
Icon=/home/USERNAME/.local/share/lxc/precise-gui/rootfs/opt/google/chrome/product_logo_256.png
Type=Application
Categories=Network;WebBrowser;
```

二つあるファイルパスが正しくなるように、 `USERNAME` の部分は自分自身のユーザー名に変更することを忘れないでください。

これで完了です！ デスクトップ環境のどこか（メニューやDash、その他）にGoogle Chromeがあらわれていることでしょう。それをクリックすると、Chromeがスタートしてほとんどいつも通りに使えるはずです。終了すると、コンテナーがシャットダウンします。さらにシンボリックリンクやbind-mountの設定を行なえば、Downloadsフォルダーに簡単にアクセスできるようになります。これはコンテナーで何をしたいかに依存します。

![Chrome in LXC](https://qiita-image-store.s3.amazonaws.com/0/29198/083153f1-2a2a-5959-909e-d664f380016e.png)


もちろん、他の多くのソフトウェアも同様に設定できます。私は今、特権コンテナーではなく非特権コンテナーで使えるようsteam-lxcパッケージを更新しているところです。また、Skypeや他の私が実行する機会のあるバイナリについても、precise-guiコンテナーを使用しています。

## Skype

何名かから同じコンテナーの中でSkypeを動かす方法について尋ねられました。Skypeを動かすために必要なことはChromeのときと99%と同じなので、手順を一つずつ説明したガイドは作成しません。

しかしながら、Skypeがちゃんと動くためには二つほど作業が必要です：

* 「 `QT_X11_NO_MITSHM` 」を「 `1` 」に設定する
  （さもないと、共有メモリーを使おうとして真っ暗なウィンドウが現れてしまいます）
* 「 `GNOME_DESKTOP_SESSION_ID` 」を「 `this-is-deprecated` 」に設定する
  （さもないと、見た目の悪いQtテーマになってしまいます）

これら二つをSkype用に作成したであろう起動スクリプトの「 `env` 」の後ろに追加してください。

どうやらNVidiaの環境では、追加で次の環境変数も設定する必要があるようです（おそらくSkypeだけではないでしょう）： `LD_PRELOAD=/usr/lib/i386-linux-gnu/mesa/libGL.so.1`

## Steam

最後に、もう一つよく尋ねられるSteamについて。

これは環境の中で特別何かする必要はありません。ただ `.deb` ファイルを取得し、コンテナーの中にインストールし、残りの依存パッケージをインストールするために「 `apt-get -f install` 」を実行して、起動スクリプトと `.desktop` ファイルを作成すれば完了です。私自身、いくつかのゲームを特に問題なく楽しむことができました（UbuntuとDebian開発者すべてにゲームを提供してくれたValveに感謝を）。

---------

## （訳注）日本語化について

今回作成したGoogle Chrome用のコンテナーで日本語を表示したい場合やChromeのUIを日本語にしたい場合は、Google Chromeをインストールしている箇所で、コンテナーを終了する前に以下のコマンドを実行してください：

```
lxc-attach -n precise-gui -- apt-get install fonts-ipaexfont language-pack-{gnome-,}ja-base -y
lxc-attach -n precise-gui -- update-locale LANG=ja_JP.UTF-8
```