ユーザー名前空間は、Linux カーネルに実装されている機能。ユーザー名前空間内では、ホストOSとは独立した UID,GID を持つことができる機能になっている。Docker コンテナを立ち上げたときにデフォルトでは、コンテナ内は root ユーザーになっているが、この root ユーザーはホストOSの root ユーザーとは別のユーザーである。ユーザー名は一緒だがホストOSからは独立した Docker コンテナ内で定義された root ユーザーになっており、また、ユーザー名前空間内の UID,GID とホストOS上の UID,GID の間はマッピングによるひもづけが行われている。
ホストOSの root と Docker コンテナの root が別であることを手を動かしながら確かめて、ユーザー名前空間という機能を見てみる。
環境
- ubuntu 18.04 (aws ec2 instance)
Docker の環境も整える。
$ sudo apt-get update
$ sudo apt-get install docker.io
$ sudo systemctl enable docker
$ docker --version
Docker version 19.03.6, build 369ce74a3c
手を動かして確かめてみる
ホストOSの root ユーザーで作成したファイルは、Docker コンテナの root ユーザーとユーザー名は一緒でも違う root ユーザーなので、Docker コンテナの root ユーザーからは見えないはずである。
# root ユーザーにチェンジ
$ sudo su -
$ id
uid=0(root) gid=0(root) groups=0(root)
$ echo "i am from host" > /tmp/file.txt
$ ls -l /tmp/file.txt
-rw-r--r-- 1 root root 15 Apr 27 03:05 /tmp/file.txt
dokcer デーモンは、--userns-remap=default
を指定する。
注意
ここで--userns-remap
を指定なしとした場合は、ホストOSの root ユーザーが作成するユーザー名前空間のユーザーと Docker コンテナ内の root ユーザーがマッピングされ、別のユーザーではあるが、わかりづらく、今回の確認方法では権限上は差異もないので、マッピングするユーザーを root 以外にしている。
$ sudo dockerd --userns-remap=default &
default
が指定された場合のユーザーは、dockremap
となる。
このユーザー dockremap が Docker コンテナを立ち上げた場合は、dockremap が作成したユーザー名前空間内の root が Docker コンテナの root となる。
$ sudo docker pull alpine:3.9.6
$ sudo docker run -itd -v /tmp/:/shared/ --name tmp alpine:3.9.6
$ sudo docker exec -it tmp sh
$ id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
$ cat /tmp/file.txt
$ cat: can't open '/tmp/file.txt': No such file or directory
Docker コンテナでのid
は root であり、uid=0
となっている。しかし、これは ホストOSの root とは別で、あくまでもユーザー名前空間内での root になっている。/shared
は、ホストOSにマウントしているため、Docker コンテナとホストOSの root が同一であれば、参照はできるが、違うユーザーなのでNo such file or directory
としてエラーが出ている。
Docker コンテナの root ユーザーは何?
dockremap
がユーザー名前空間内に作成したUID=231072
のユーザーであり、このユーザーが Docker コンテナ内では root にマッピングされている。
# ホストOSで /etc/subuid を見てみる
$ cat /etc/subuid
lxd:100000:65536
root:100000:65536
ubuntu:165536:65536
dockremap:231072:65536
/etc/subuid
には、ユーザー名前空間を作成したときに、割り振る UID が書いてある。
ユーザー名:UID開始番号:範囲
となっている。例えば、root ユーザーがユーザー名前空間を作成したときは、100000 ~ 165536 の範囲で UID が振られる。一番最初に振られる UID は、UID開始番号なので、さっき作成した Docker コンテナの root ユーザーは、231072 が振られている。
# ホストOSで ps コマンドをするとユーザー231072にたてられた sh が見える
$ ps aux | grep 231072
231072 5880 0.0 0.1 1596 1060 pts/0 Ss+ 03:24 0:00 /bin/sh
ホストOSでは、UID は231072だが、ユーザー名前空間内では root であり、UID は 0になる。このホストOSの UID と Docker コンテナ内の UID のマッピングまで、ユーザー名前空間の機能である。
まとめ
Docker は難しい、終わり!
参考
https://gihyo.jp/admin/serial/01/linux_containers/0016?page=1
https://knowledge.sakura.ad.jp/5118/
https://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html
Hacking and Securing Docker Containers (Udemy)