TenForward

技術ブログ。はてなダイアリーから移転しました

shiftfs (s_user_ns version) 試してみた

Open Source Summit Japan 2017 で聞いた話に shiftfs の話があって、興味を持ったので試してみました。この機能の前提機能なんかに関する前提知識に欠けているため、以下には間違いが含まれている可能性が大きいです。是非指摘を頂きたいと思います。試してみただけで内部の処理とかは書いてませんよw

下に関連情報を挙げておきます (こちらを直接見たほうがいいかも)。

直接関係ないけど s_user_ns のパッチも紹介しておきます。

User Namespace に関しては私の連載記事をどうぞ。

一般ユーザ権限のコンテナとイメージ

User Namespace を利用する場合、LXC では shadow に実装されている subuid, subgid を使い、そのユーザに対して使用を許可された uid/gid を使ってコンテナを作成し、起動します。

例えば $HOME/.config/lxc/default.conf

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

のように設定していれば、lxc-create を実行した時、コンテナ内の uid:0 がホスト上では uid:100000 にマッピングされ、コンテナイメージ上の root 所有のファイルについては、ホスト上で見ると uid/gid が 100000 のユーザ・グループ所有で作成されます。

例えば次のような感じです。

$ ls -l ~/.local/share/lxc/xenial01/rootfs/
合計 77,824
drwxr-xr-x  2 100000 100000 4,096  4222016年 bin/
drwxr-xr-x  2 100000 100000 4,096  4132016年 boot/
drwxr-xr-x  3 100000 100000 4,096  4222016年 dev/
  :(snip)

普段はこれで問題はないのですが、例えば public に流通している root 権限で起動するコンテナで利用する前提のイメージとか、単なる tar.gz のアーカイブとかを一般ユーザで使ったり、逆に一般ユーザ権限用のイメージを root で起動したりする際には、あらかじめ chown などで権限を変更しておく必要があります。一度きりの処理なら良いのですが、起動するたびに入れ代わり立ち代わりユーザを変えて起動したいとかだと不便です。

逆に一般ユーザ権限でコンテナイメージを作成すると、作成環境の ID のマッピング状況に依存してしましますね。

shiftfs

そこでどうやら 4.8 カーネルstruct super_blocks_user_ns という変数が導入され、ファイルシステム上のファイルやディレクトリの処理を行う際には、Namespace 上の ID を使うことができるようになったようです (ここはちゃんと調べてないので間違ってる可能性大)。

ただし、これは単なるディレクトリを bind mount して、コンテナイメージとして使う場合には使えません。なぜなら bind mount は独自の super block は持たないからです (たぶん)。

そこで、s_user_ns を使って User Namespace 内での ID を使うように、super block を使いつつ bind mount をできる shiftfs という機能が James Bottomley 氏が提案しています。現時点では色々な理由であまり受け入れられる感じではなさそうですが…

このパッチは s_user_ns より前のバージョンもあり、その際はマウントオプションで uidmap=0:1000:1 みたいにマッピングを設定していました。(以前のバージョン)

shiftfs のパッチ

どういう経緯かわからないですが、linuxkit に 4.11 用のパッチがありました。これを 4.12 に当ててみました。私の手元は aufs のパッチも当ててるので Makefile だけ適用エラーになりましたが、多分 4.12 にはそのまま当たります。

shiftfs 使わない場合

root 権限で lxc-create で作った Alpine Linux のイメージで試してみました (あらかじめコンテナディレクトリやrootfsディレクトリは一般ユーザでアクセスできるようにしてあります)。

ホスト上で確認すると、root で作成しているので、当たり前ですが root 所有のディレクトリが並びます。

# ls -ld /var/lib/lxc/alpine01/rootfs
drwxr-xr-x 1 root root 108  7520:32 /var/lib/lxc/alpine01/rootfs/
# ls -l /var/lib/lxc/alpine01/rootfs
合計 0
drwxr-xr-x 1 root root   868  5403:12 bin/
drwxr-xr-x 1 root root   130  5403:12 dev/
drwxr-xr-x 1 root root   584  62222:32 etc/
drwxr-xr-x 1 root root     0  5403:12 home/
drwxr-xr-x 1 root root   354  5403:12 lib/
drwxr-xr-x 1 root root    28  5403:12 media/
drwxr-xr-x 1 root root     0  5403:12 mnt/
drwxr-xr-x 1 root root     0  5403:12 proc/
drwx------ 1 root root    24  62220:54 root/
drwxr-xr-x 1 root root     0  5403:12 run/
drwxr-xr-x 1 root root 1,610  5403:12 sbin/
drwxr-xr-x 1 root root     0  5403:12 srv/
drwxr-xr-x 1 root root     0  5403:12 sys/
drwxrwxrwt 1 root root    36  62222:02 tmp/
drwxr-xr-x 1 root root    40  5403:12 usr/
drwxr-xr-x 1 root root    78  62220:17 var/

このコンテナイメージを、普通に User Namespace を作成して、Namespace 内から見てみましょう。

$ unshare --pid --user --map-root-user --mount --mount-proc --fork -- /bin/bash
# cat /proc/self/{u,g}id_map
         0       1000          1
         0        100          1
# ls -ld /var/lib/lxc/alpine01/rootfs
drwxrwxrwx 1 nobody nogroup 108  62222:02 /var/lib/lxc/alpine01/rootfs/
# ls -l /var/lib/lxc/alpine01/rootfs
合計 0
drwxr-xr-x 1 nobody nogroup   868  5403:12 bin/
drwxr-xr-x 1 nobody nogroup   130  5403:12 dev/
drwxr-xr-x 1 nobody nogroup   584  62222:32 etc/
drwxr-xr-x 1 nobody nogroup     0  5403:12 home/
drwxr-xr-x 1 nobody nogroup   354  5403:12 lib/
drwxr-xr-x 1 nobody nogroup    28  5403:12 media/
drwxr-xr-x 1 nobody nogroup     0  5403:12 mnt/
drwxr-xr-x 1 nobody nogroup     0  5403:12 proc/
drwx------ 1 nobody nogroup    24  62220:54 root/
drwxr-xr-x 1 nobody nogroup     0  5403:12 run/
drwxr-xr-x 1 nobody nogroup 1,610  5403:12 sbin/
drwxr-xr-x 1 nobody nogroup     0  5403:12 srv/
drwxr-xr-x 1 nobody nogroup     0  5403:12 sys/
drwxrwxrwt 1 nobody nogroup    36  62222:02 tmp/
drwxr-xr-x 1 nobody nogroup    40  5403:12 usr/
drwxr-xr-x 1 nobody nogroup    78  62220:17 var/
root@enterprise:~# 

マッピングされていないので nobody:nogroup に。このままではこのイメージ以下に書き込みできません。(なぜnobody:nogroupになるのかは私の連載に書いてます)

shiftfs の利用

s_user_ns バージョンの shiftfs は、まずマウントしたいディレクトリに印を付ける必要があるようです。まずは root で以下を実行。-o mark と mark オプションでマウントします。

# mount -t shiftfs -o mark /var/lib/lxc/alpine01/rootfs/ /var/lib/lxc/alpine01/rootfs/
# grep alpine01 /proc/self/mounts 
/var/lib/lxc/alpine01/rootfs /var/lib/lxc/alpine01/rootfs shiftfs rw,relatime,mark 0 0

この後、また一般ユーザで userns 作成して、別ディレクトリに shiftfs マウントしてみます。この時は特にマウントオプションは不要です。

$ id -u ; id -g
1000
100
$ unshare --pid --user --map-root-user --mount --mount-proc --fork -- /bin/bash
# cat /proc/self/{u,g}id_map
         0       1000          1
         0        100          1
# mount -t shiftfs /var/lib/lxc/alpine01/rootfs/ /home/karma/mnt
# grep shiftfs /proc/self/mounts
/var/lib/lxc/alpine01/rootfs /var/lib/lxc/alpine01/rootfs shiftfs rw,relatime,mark 0 0
/var/lib/lxc/alpine01/rootfs /home/karma/mnt shiftfs rw,relatime 0 0

ここでマウントしたディレクトリ以下を見てみると、

# ls -l /home/karma/mnt
合計 0
drwxr-xr-x 1 root root   868  5403:12 bin/
drwxr-xr-x 1 root root   130  5403:12 dev/
drwxr-xr-x 1 root root   584  62222:32 etc/
drwxr-xr-x 1 root root     0  5403:12 home/
drwxr-xr-x 1 root root   354  5403:12 lib/
drwxr-xr-x 1 root root    28  5403:12 media/
drwxr-xr-x 1 root root     0  5403:12 mnt/
drwxr-xr-x 1 root root     0  5403:12 proc/
drwx------ 1 root root    24  62220:54 root/
drwxr-xr-x 1 root root     0  5403:12 run/
drwxr-xr-x 1 root root 1,610  5403:12 sbin/
drwxr-xr-x 1 root root     0  5403:12 srv/
drwxr-xr-x 1 root root     0  5403:12 sys/
drwxrwxrwt 1 root root    36  62222:02 tmp/
drwxr-xr-x 1 root root    40  5403:12 usr/
drwxr-xr-x 1 root root    78  62220:17 var/

ちゃんと User Namespace 内から見ても root 所有に表示されています。