以前、Docker コンテナ内で Docker ホストと同じユーザを使う方法として、以下のような記事を書いた。
ちょっと強引だけど /etc/passwd
と /etc/group
をコンテナからマウントすることで不整合をなくしてしまう、というもの。
ただ、上記のやり方だと不都合が出る場合もあったので、別のやり方についても試してみた。 どちらかというと、今回のやり方の方が後々の面倒がなくて良いかもしれない。 方法というのは、コンテナを起動するタイミングでコンテナ内にユーザを使って各種 ID などを引き継がせてしまう、というもの。
使った環境は次の通り。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=18.04 DISTRIB_CODENAME=bionic DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS" $ uname -r 4.15.0-65-generic
もくじ
ユーザ情報の書かれたファイルを読み込み専用でマウントする方法の問題点について
Docker のインストール方法については前述した記事の中でも扱っているので省略する。
先のエントリでは、次のようにして /etc/passwd
と /etc/group
を読み込み専用でマウントすることで同じユーザを使えるようにしていた。
$ sudo docker run \ --rm \ -u "$(id -u $(whoami)):$(id -g $(whoami))" \ -v $(pwd):/mnt/working \ -v /etc/passwd:/etc/passwd:ro \ -v /etc/group:/etc/group:ro \ -v /home:/home \ -it ubuntu:18.04
この方法を使うと、たしかに Docker コンテナ内で Docker コンテナと同じユーザが使えるようになる。
$ whoami vagrant $ id uid=1000(vagrant) gid=1000(vagrant)
ただ、この方法には問題がないわけでもない。 例えば、次のようにして一旦 root ユーザで入り直す。
$ sudo docker run \ --rm \ -v $(pwd):/mnt/working \ -v /etc/passwd:/etc/passwd:ro \ -v /etc/group:/etc/group:ro \ -v /home:/home \ -it ubuntu:18.04
そして、試しにユーザのパスワードハッシュを次のようにして usermod(8)
で書き換えてみる。
# usermod -p python3 -c "import crypt; from getpass import getpass; print(crypt.crypt(getpass(), crypt.METHOD_SHA512))" vagrant
usermod: cannot open /etc/passwd
しかし、エラーになってしまった。
パスワードハッシュは、現在では一般的に /etc/shadow
に書き込まれるようになっている。
しかし、後方互換性を考えて /etc/passwd
に書かれていても動作する。
そのため、結局のところ /etc/passwd
にも書き込み権限が必要とされるらしい。
このように、読み込み専用でマウントしてしまうことは、後々面倒な問題を招きやすいようだ。
動的にユーザを追加・編集する
そこで、もう一つのやり方を試してみることにする。 具体的には、コンテナを起動するタイミングでユーザを追加して ID などを編集してしまう、というもの。
まずは Supervisord の設定ファイルを用意しておく。 これはコンテナが終了しないようにするためだけのもの。 何らか継続するプロセスであれば別のものを使っても良い。
$ cat << 'EOF' > supervisord.conf [supervisord] nodaemon=true EOF
続いて Dockerfile を用意する。
必要なパッケージなどをインストールして設定ファイルをコピーしているだけなので、そんなに見るべきところはない。
コンテナが起動するときは docker-entrypoint.sh
というシェルスクリプトを実行している。
この中でユーザを追加・編集することになる。
$ cat << 'EOF' > Dockerfile FROM ubuntu:18.04 # Use mirror repository RUN sed -i.bak -e "s%http://[^ ]\+%mirror://mirrors.ubuntu.com/mirrors.txt%g" /etc/apt/sources.list # Install prerequisite packages RUN apt update \ && apt -yq dist-upgrade \ && apt install -yq --no-install-recommends \ sudo \ supervisor \ && apt clean \ && rm -rf /var/lib/apt/lists/* # Boot process COPY supervisord.conf /etc COPY docker-entrypoint.sh /var/tmp CMD bash -E /var/tmp/docker-entrypoint.sh EOF
続いてが肝心のコンテナが起動するときに呼び出されるシェルスクリプト。 この中では変数を元にユーザを追加して ID の類を編集している。 そして、最終的に Supervisord が起動するようになっている。
$ cat << 'EOF' > docker-entrypoint.sh #!/usr/bin/env bash set -Ceux assert_env_defined () { if [ ! -v $1 ]; then echo "please define '$1' environment variable" exit 1 fi } : "Add non administrative user" && { : "Add user" && { assert_env_defined USER_NAME set +e # avoid error check temporarily id ${USER_NAME} # existence check if [ $? -ne 0 ]; then useradd -s /bin/bash -m ${USER_NAME} fi set -e # enable error check } : "Set home directory" && { export HOME=/home/${USER_NAME} } : "Change user id" && { assert_env_defined USER_ID if [ $(id -u ${USER_NAME}) -ne ${USER_ID} ]; then usermod -u ${USER_ID} ${USER_NAME} fi } : "Change group id" && { assert_env_defined GROUP_ID if [ $(id -g ${USER_NAME}) -ne ${GROUP_ID} ]; then usermod -g ${GROUP_ID} ${USER_NAME} fi } : "Add common groups" && { usermod -aG sudo ${USER_NAME} } } : "Start supervisord" && { supervisord -c /etc/supervisord.conf } EOF
上記のコンテナを起動するための Docker Compose の設定ファイルを用意しておこう。
$ cat << 'EOF' > docker-compose.yml version: "3" services: override: image: example/override-ids build: . container_name: override environment: - USER_NAME - USER_ID - GROUP_ID volumes: - ./:/mnt/working EOF
まずはコンテナイメージをビルドする。
$ sudo docker-compose build
そして、次のように現在のユーザ名や ID の類をシェル変数ごしに教えてやりながらコンテナを起動する。
$ USER_NAME=$(whoami) \ USER_ID=$(id -u) \ GROUP_ID=$(id -g) \ sudo -E docker-compose up ... override | + : 'Start supervisord' override | + supervisord -c /etc/supervisord.conf override | 2019-10-02 14:54:43,036 CRIT Supervisor running as root (no user in config file) override | 2019-10-02 14:54:43,038 INFO supervisord started with pid 14
起動したコンテナにログインする。
このとき su(1)
を使って追加されたユーザにログインし直す。
$ sudo docker exec -it override su - $(whoami)
すると、次のようにユーザ名や ID が引き継がれたように見える状態でコンテナが起動する。
$ whoami vagrant $ id uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant),27(sudo)
試しに、何かファイルを作ってみよう。
$ cd /mnt/working/ $ echo "Hello, World" > greet-docker.txt
ちゃんと Permission Denied にならずにファイルが作られた。
Docker ホストのファイルも、以下のようにユーザがちゃんと状態で表示されている。 これは、コンテナとホストでユーザ名や識別子が一致しているため。
$ ls -lF total 20 -rw-rw-r-- 1 vagrant vagrant 466 Oct 2 14:39 Dockerfile -rw-rw-r-- 1 vagrant vagrant 215 Oct 2 14:51 docker-compose.yml -rw-rw-r-- 1 vagrant vagrant 978 Oct 2 14:46 docker-entrypoint.sh -rw-rw-r-- 1 vagrant vagrant 13 Oct 2 14:53 greet-docker.txt -rw-rw-r-- 1 vagrant vagrant 28 Oct 2 14:39 supervisord.conf
ちなみに、このやり方なら Docker for Mac でも使える。 めでたしめでたし。
- 作者:もみじあめ
- 発売日: 2020/02/29
- メディア: Kindle版