連載の第50回 、第51回 で、ID mappedマウントについて書きました。
これらの記事を書いた時点(2022年末)では、mount
コマンドでは ID mapped マウントができなかったので、記事では、ID mapped マウントの主要開発者であるChristian Brauner氏の mount-idmapped を使って説明しました。
その後、util-linux 2.39-rc1 の libmount で ID mapped マウントがサポートされ 、2.40 で mount コマンドで --map-users と --map-group オプションが追加されました 。
ID mappedマウントの必要性
User Namespaceは、図のようにホストOS環境のUID/GID と、コンテナ環境のUID/GID をマッピング させる機能です。
User Namespace
図だと、ホスト上のUID:100000がコンテナ内の0に、UID:100001がコンテナ内の1に、UID:101000がコンテナ内の1000にマッピング されます。つまり、コンテナ内でUID:0のrootユーザーは、実はコンテナの外から見るとUID:100000の一般ユーザーの権限で動いており、仮にコンテナからホストに対して悪意ある操作を行おうと思っても、UID:100000の一般ユーザー権限でできることしかできません。
さて、コンテナを起動する際は、コンテナホスト上にコンテナイメージを展開し、それをマウントしてコンテナのルートファイルシステム とします。コンテナがUID:100000の一般ユーザー権限で動いているとすると、コンテナホスト上に展開されるコンテナイメージも、UID:100000から始まるマッピング 先のUIDやGID から操作できる権限がなければいけません。
しかし、一般的には、コンテナイメージはコンテナホスト上のrootから読める前提、つまりUID/GID が0から始まるIDで作成されているでしょう。元から一般ユーザー権限で起動する前提でイメージを作れないことはないですが、そもそも各ユーザーでどのようなマッピング を行うのかは、イメージ作成時にはわかりません。
例えば、次のようにLXCコンテナをroot権限で作成したとします。
$ sudo lxc-create -t download c1 -- -d alpine -r 3 .19 -a amd64
$ sudo ls -l /var/lib/lxc/c1/rootfs
合計 68
drwxr-xr-x 2 root root 4096 6 月 14 22:00 bin
drwxr-xr-x 3 root root 4096 6 月 16 22:54 dev
drwxr-xr-x 24 root root 4096 6 月 16 22:54 etc
drwxr-xr-x 2 root root 4096 1 月 27 02:53 home
(略)
$ sudo chmod 775 /var/lib/lxc/c1 (これはこのあとの操作のために権限を緩めています)
このように、ほとんどのディレクト リーはホストのroot所有になっています。
ここで、unshare
コマンドを使い、User Namespaceを使ってコンテナを作ります。
$ unshare --user --map-root-user ls -l /var/lib/lxc/c1/rootfs/
合計 69 ,632
drwxr-xr-x 2 nobody nogroup 4 ,096 6 月 14 日 22:00 bin
drwxr-xr-x 3 nobody nogroup 4 ,096 6 月 16 日 22:54 dev
drwxr-xr-x 24 nobody nogroup 4 ,096 6 月 16 日 22:54 etc
drwxr-xr-x 2 nobody nogroup 4 ,096 1 月 27 日 02:53 home
(略)
ここでは、現在のユーザーをコンテナ(User Namespace)内のrootにマッピング して、ls -l
コマンドを実行しています。しかし、ファイルの所有権は何も触っていないので、所有者がnobody:nogroup
所有になってしまっています。これは、コンテナホスト上のUID/GID :0が、新たに作ったUser Namespace内ではマッピング されていないためです。
この変換をID mappedマウント以前は、chownなどで実行していたのですが、これは非常に効率が悪いために、コンテナからマウントする際にファイルの所有者についても、マッピング を使ってずらそうという考えで実装されたのがID mappedマウントです。
図のように、User Namespaceで使うマッピング をそのまま使い、ファイルの所有者についても変換を行ってしまおうという機能です。
ID mappedマウントの動き
mountコマンドからID mappedマウントを利用する
さて、2.40からmount
コマンドに追加されたID mappedマウントを行うためのオプションを使って、ID mappedマウントを試していきましょう。
追加されたオプションは次の2つです。2つとも使い方は同じです。いずれのオプションも複数回指定できます。このオプションとバインドマウントを組み合わせます。
オプション
説明
--map-users <id-from>:<id-to>:<id-range>
UIDのマッピング 指定
--map-groups <id-from>:<id-to>:<id-range>
GID のマッピング 指定
このオプションで指定するコロンで接続された値 <id-from>:<id-to>:<id-range>
のそれぞれで指定する値は次の通りです。
値
説明
id-from
マッピング 元、つまりmount
コマンドを実行する元のUser NamespaceでのID
id-to
マッピング 先、つまりマウント先のUser NamespaceでのID
例えば、次のようにマッピング とマウント元、マウント先のディレクト リーを指定します。
$ sudo mount --bind --map-users 1001:1002:1 --map-group 1001:1002:1 /path/to/src /path/to/dest
この例では、mount
を実行する元のNamespaceのUID/GID :1001を、マウント先ではUID/GID :1002にマッピング して、バインドマウントしています。
それでは、実際にID mappedマウントの動きを見ていきます。実行はUID/GID がいずれも1002のu1002
ユーザで実行しています。
u1002@host:~$ id
uid=1002(u1002) gid=1002(u1002) groups=1002(u1002),10(wheel)
そして、UID/GID が1001のu1001
ユーザのホームディレクト リーをu1002
ユーザでID mappedマウントしてみます。
u1002@host:~$ id u1001
uid=1001(u1001) gid=1001(u1001) groups=1001(u1001),10(wheel)
u1002@host:~$ sudo ls -l /home/u1001
合計 8
drwxr-xr-x 2 u1001 u1001 4096 5月 13 18:10 Mail
drwxr-xr-x 2 u1001 u1001 4096 5月 13 18:10 Sample
-rw-r--r-- 1 u1001 u1001 0 6月 16 21:41 u1001-file
上のように、u1001
ユーザのホームディレクト リには、u1001-file
というファイルをu1001
ユーザ権限で作成してあります。
それでは、このu1001
ユーザのホームディレクト リである/home/u1001
を、UID/GID をu1002
ユーザのIDである1002にマッピング して、/mnt
にマウントしてみます。
u1002@host:~$ sudo mount --bind --map-users 1001:1002:1 --map-group 1001:1002:1 /home/u1001 /mnt
これで/home/u1001
がIDマッピング されて、/mnt
にマウントされているはずです。/mnt
ディレクト リの所有権を確認してみましょう。
u1002@host:~$ ls -l /mnt
合計 0
-rw-r--r-- 1 u1002 u1002 0 6月 16日 21:41 u1001-file
元の/home/u1001
では、u1001
ユーザ所有だったファイルがマッピング され、u1002
ユーザ所有に見えています。
次に、/mnt
配下でファイルをu1002ユーザ(UID/GID =1002)権限で作成してみましょう。
u1002@host:~$ touch /mnt/u1002-file
u1002@host:~$ ls -l /mnt
合計 0
-rw-r--r-- 1 u1002 u1002 0 6月 16日 21:41 u1001-file
-rw-r--r-- 1 u1002 u1002 0 7月 9日 01:34 u1002-file
ファイルを問題なく作成でき、新たに作成されたファイルの所有権はu1002
ユーザになっています。
ここで、新たにID mappedマウントされたディレクト リである/mnt
で作成したu1002-file
を、マウント元の/home/u1001
で確認しておきましょう。
u1001@host:~$ ls -l /home/u1001
合計 0
-rw-r--r-- 1 u1001 u1001 0 6月 16 21:41 u1001-file
-rw-r--r-- 1 u1001 u1001 0 7月 9 01:34 u1002-file
/mnt
上でu1002
ユーザの権限で作成したu1002-file
は、元の/home/u1001
で確認すると、きちんとUID/GID が1001のu1001
ユーザ所有になっています。
さて、ここまでで util-linux
に含まれている mount
コマンドで ID mappedマウントの動きを見てきました。記事 では、このあと、ACL やケーパビリティの動きを見ていますが、マウントされてしまったあとの動きは同じですので(カーネル の同じ機能を使ってるので当たり前ですね)、そのあたりは記事を参照いただければと思います。