😊

Linux kernel 5.11 で追加された OverlayFS の非特権マウント

2021/03/21に公開

5.11 カーネルで overlayfs に大きな変更があったようで、久々にカーネルの新しい機能を試してみました。

とは言っても、結果だけ言うとすぐに終わってしまうので、すごいことをやったように見せかけるために、復習したりして順に説明していきましょう。時間のない方は最後の方だけ見れば良いです。

OverlayFS とは

Union Filesystem の実装の1つで、ディレクトリを重ね合わせて1つのディレクトリツリーを構成できます。Docker なんかではおなじみの機能ですね。おなじみの機能とはいえ、実際に直接マウントして動きを見たことがない方も多いかと思います。そこでまずは動きを簡単に見てみましょう。

重ね合わせるということで、下層側ディレクトリ、上層側ディレクトリを重ね合わせて、マウントポイント以下に見せます。他にワーク用の workdir として指定するディレクトリが必要です。

次の例では

  • 下層用ディレクトリとして lower
  • 上昇用ディレクトリとして upper
  • workdirとして work
  • マウントポイントとして overlay

というディレクトリを準備しています。lowerupper の中にはディレクトリとファイルを作成しておき、マウント後に overlay 以下にそれらのファイル・ディレクトリが見えることが確認できます。

$ mkdir lower upper work overlay # overlayfs用のディレクトリの作成
$ mkdir lower/lowerdir upper/upperdir # 下層、上層それぞれにディレクトリ作成
$ touch lower/lowerdir/lowfile upper/upperdir/upfile # ディレクトリ内にファイル作成
$ sudo mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
$ find overlay/
overlay/
overlay/lowerdir
overlay/lowerdir/lowfile
overlay/upperdir
overlay/upperdir/upfile
$ grep overlay /proc/$$/mountinfo 
68 33 0:67 / /home/karma/tmp/overlay rw,relatime - overlay overlay rw,lowerdir=lower,upperdir=upper,workdir=work

詳しくは連載記事

や、

をご覧ください。

他に関連記事としてこんな記事も書いています。

5.11 カーネルで行われた非特権マウントのための変更と FS_USERNS_MOUNT

User Namespace内は、Namespace内では特権ユーザー、Namespace外では一般ユーザーという UID/GID のマッピングができる機能です。Namespace 内では特権を持つユーザーであっても、実際は特権を持たないユーザーでの処理がされているため、当然ながら一般的には User Namespace 内ではマウント操作はできません。

User Namespace についても、詳しくは連載記事をご覧ください。

しかし、一部のファイルシステムについては、従来から User Namespace 内でマウントできました。例えば、コンテナ内で /proc や tmpfs などをマウントする操作は普通に行われている操作ではないかと思います。

このような User Namespace 内でファイルシステムをマウントできる機能は、非常に簡単な定義を行うだけで使えます。このためのファイルシステムに定義する定数が include/linux/fs.h に定義されています。

struct file_system_type {
        const char *name;
        int fs_flags;
#define FS_REQUIRES_DEV         1 
#define FS_BINARY_MOUNTDATA     2
#define FS_HAS_SUBTYPE          4
#define FS_USERNS_MOUNT         8       /* Can be mounted by userns root */
#define FS_DISALLOW_NOTIFY_PERM 16      /* Disable fanotify permission events */
  : (snip)

この FS_USERNS_MOUNT というのがそれで、ファイルシステムを実装する際にこの値を fs_flags に設定すると、コメントにあるように User Namespace 内の root が、そのファイルシステムをマウントできるわけです。

実は LXC 方面で使っていたため、Ubuntu のカーネルにはこれまでも User Namespace 内で overlayfs をマウントするパッチが適用されていました(筆者がメンテナをつとめる Plamo Linux でも一時期適用されていたはずです)。

今回の OverlayFS の非特権マウントのパッチも非常に単純で、次のようなパッチです。これまで Ubuntu カーネルに適用されていたパッチも同じものです。

--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -2096,6 +2096,7 @@ static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
 static struct file_system_type ovl_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "overlay",
+	.fs_flags	= FS_USERNS_MOUNT,
 	.mount		= ovl_mount,
 	.kill_sb	= kill_anon_super,
 };

ovl: unprivieged mounts

今回(5.11 カーネル)の OverlayFS に対するパッチは 10 個ほどのパッチとなっていますが、「非特権マウント」のために必要な変更は上記の変更だけです。他はより安全に処理を行うための修正のようで、今回だけでなく 5.8 でも変更が行われていたようです。

5.11 カーネルでの非特権 OverlayFS マウント

それでは先の例と同じディレクトリ、ファイルを使って非特権 OverlayFS を試してみましょう。使用するカーネルは 5.11.5 です。

$ uname -r
5.11.5-plamo64

「非特権」と言っても、先に説明したとおり「User Namespace 内の root がマウントできる」ということですので、unshare コマンドで User Namespace を作成して試します。(いずれにせよ mount コマンドは root でないと実行が失敗するようになってます)

ただ、ここで User Namespace だけを作ってもマウントは失敗します。

$ unshare --user --map-root-user
# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
mount: /home/karma/tmp/overlay: permission denied. (失敗した)

これは Mount Namespace も元の Namespace とも独立している必要があるためです。

そこで次の例では unshare コマンドに --mount も指定して User/Mount Namespace を作成してみましょう。--map-root-user は unshare を実行するユーザーと User Namespace 内の root をマッピングするオプションです。次の例だと元の Namespace の UID: 1000 のユーザーと作成する Namespace 内の UID:0 をマッピングするということです。

$ id -u (現在のユーザーは UID:1000)
1000
$ unshare --user --map-root-user --mount (User Namespace と Mount Namespace を作成する)
# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
# grep overlay /proc/self/mountinfo (マウント情報を確認する)
119 109 0:62 / /home/karma/tmp/overlay rw,relatime - overlay overlay rw,lowerdir=lower,upperdir=upper,workdir=work,index=off,metacopy=off
# find overlay/ (重ね合わせた状態でマウントできている)
overlay/
overlay/lowerdir
overlay/lowerdir/lowfile
overlay/upperdir
overlay/upperdir/upfile

マウントが成功しましたね。所有権も見ておきましょう。

# ls -l overlay/
合計 0
drwxr-xr-x 1 root root 14  31421:07 lowerdir/
drwxr-xr-x 1 root root 12  31421:07 upperdir/
# ls -l overlay/*
overlay/lowerdir:
合計 0
-rw-r--r-- 1 root root 0  31421:07 lowfile

overlay/upperdir:
合計 0
-rw-r--r-- 1 root root 0  31421:07 upfile

これらのファイルは元の Namespace のユーザー(UID: 1000)権限で作成しましたので、ちゃんと User Namespace 内でマウントしてもマッピング先のユーザー(UID: 0=root)の所有権になっています。

5.11 より前のカーネルでの実行例

一応、比較のために 5.11 より前のバージョンのカーネルで非特権マウントができないことも確認しておきましょう。ちょっと古いのですが、手元にあった 5.2 カーネルの環境で試してみました。

$ uname -r
5.2.1-plamo64
$ id -u
1000
$ unshare --mount --user --map-root-user
# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
mount: /home/karma/tmp/overlay: permission denied.

同様に実行してみました。失敗しましたね。

OverlayFS の様々な機能と拡張ファイル属性

ところで OverlayFS はシンプルながらも、より進んだ使い方ができる機能やオプションが存在します。基本的な機能以上の機能を使う場合、OverlayFS は拡張ファイル属性を使います。

例えば連載第18回で説明した「opaque(不透明)ディレクトリ」機能や、以前のブログエントリーで説明したredirect_dir機能などです。

しかし拡張ファイル属性で、この trusted で始まる属性(trusted名前空間)を使う場合は特権が必要であり、当然 User Namespace 内の root ではこの属性を使用できません。このような場合、代わりに自由に定義して使える属性(名前空間)として user が存在します[1]

OverlayFS でも、この機能を使うように実装されており、trusted の代わりに user を使う場合は、マウントオプションとして userxattr を指定してマウントします。この場合、trusted.overlay.* ではなく user.overlay.* を使うようになります。

非特権の場合の拡張ファイル属性

この拡張ファイル属性の動きを連載で説明した「opaque(不透明)ディレクトリ」を使って説明しましょう。この機能を使うと lowerdir に指定した下層側のディレクトリの内容が見えなくなります。

opaqueディレクトリを使いたい場合、拡張ファイル属性 trusted.overlay.opaquey という値を入れます。非特権 OverlayFS の場合は、この代わりに user.overlay.opaque を使うわけです

この機能の動きを見るための環境を作成しましょう。

$ mkdir lower upper work overlay
$ mkdir {lower,upper}/opaquetest
$ touch lower/opaquetest/testfile_lower upper/opaquetest/testfile_upper

これまでの実行例と同様に lowerupperworkoverlay というディレクトリを作成し、lowerupper の両方のディレクトリに opaquetest というディレクトリを作成します(連載の例と同じです)。そして lowerupper 配下の opaquetest ディレクトリ内にそれぞれ testfile_lowertestfile_upper というファイルを置きます。

この環境で普通に OverlayFS マウントを行うと、次のように見えるはずです。

# tree overlay/
overlay/
└── opaquetest
    ├── testfile_lower
    └── testfile_upper

1 directory, 2 files

userxattr オプションを使わない場合

まずは userxattr オプションを指定せずに OverlayFS マウントを行い、動きを見てみましょう。当然、期待する動きはしないはずです。

# setfattr -n "user.overlay.opaque" -v "y" upper/opaquetest/ (拡張ファイル属性を設定する)
# getfattr -n "user.overlay.opaque"  upper/opaquetest/ (拡張ファイル属性が設定されたのを確認)
# file: upper/opaquetest/
user.overlay.opaque="y"

# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
# tree overlay/ (拡張ファイル属性を設定したものの、特にOverlayFSの動きに変化はない)
overlay/
└── opaquetest
    ├── testfile_lower
    └── testfile_upper

1 directory, 2 files
# umount overlay

上記の例のように user.overlay.opaque 属性を設定しても、特に先ほどの普通に OverlayFS マウントを行ったときと動きに変化はありません。

userxattr オプションを追加してマウントした場合

それでは OverlayFS マウントを行う際に userxattr オプションを追加してみましょう。

# getfattr -n "user.overlay.opaque"  upper/opaquetest/ (拡張属性が設定されているのを確認)
# file: upper/opaquetest/
user.overlay.opaque="y"

# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work,userxattr overlay overlay (userxattrオプションを指定してマウント)
# tree lower upper (lower, upper以下のopaquetestディレクトリにはそれぞれファイルが存在する)
lower
└── opaquetest
    └── testfile_lower
upper
└── opaquetest
    └── testfile_upper

2 directories, 2 files
# tree overlay/ (overlayディレクトリを見ると上層側に置いたファイルしか見えない)
overlay/
└── opaquetest
    └── testfile_upper

1 directory, 1 file

このように上層側のファイルしか見えません。opaque ディレクトリの機能が働いていることがわかります。

非特権マウントの場合に使えない機能

以上のように、非特権マウントの場合は trusted.overlay の代わりに user.overlay を使って OverlayFS 独自の機能を実現します。

しかし非特権の場合、特権を持っているケースと同じように機能を使うと危険なケースがあります(特権の取得につながるとか)。このような機能については非特権の場合には使えないようになっています。

この機能のひとつが、以前ブログで紹介したredirect_dir機能 です。

次のように、この機能を使うためにオプションを指定してマウントしようとするとエラーになります。

$ unshare --user --map-root-user --mount
# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work,userxattr,redirect_dir=on overlay overlay
mount: /home/karma/tmp/overlay: wrong fs type, bad option, bad superblock on overlay, missing codepage or helper program, or other error.

当然ですが、redirect_dir=off とするとマウントできます。

# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work,userxattr,redirect_dir=off overlay overlay
# grep overlay /proc/self/mountinfo 
119 109 0:62 / /home/karma/tmp/overlay rw,relatime - overlay overlay rw,lowerdir=lower,upperdir=upper,workdir=work,redirect_dir=off,index=off,metacopy=off

他にも非特権の場合、metacopy 機能が使えないようです(こちらの機能は私はまだ調べていないので詳細はまたの機会に)。

脚注
  1. ここで使っている名前空間は拡張ファイル属性の名前空間でコンテナで使う名前空間ではありません。 ↩︎

Discussion