User Namespace 外から Namespaced file capability を設定する
前回、
では、非特権ユーザで起動したコンテナ内の root
ユーザーで File capability を設定しました。
ここで、自作コンテナ派の皆様が気になるのは、この capability をコンテナ外から設定するのはどうしたらいいのだろう? というところではないかと思います。コンテナランタイム側からコンテナ内の capability を設定する必要があるかもしれません。
もちろん、これも簡単にできます。先に getcap
コマンドに -n
オプションを付与しましたが、これと同じオプションが setcap
にも存在します。
setcap -n 200000 cap_net_raw+ep ./ping
と言ったように、User Namespace 内の root
の uid を指定して capability を指定します。
まず、コンテナ内からアクセスできるように、ホスト環境上で ping
コマンドをコンテナの rootfs 内にコピーします。LXC の非特権コンテナは次のようなディレクトリ以下に rootfs が置かれますので、ここにコピーします。ping2
という名前でコピーしました。
$ sudo cp /bin/ping ~/.local/share/lxc/c3/rootfs/home/karma/ping2
次にコピーした ping2
バイナリに対して setcap
します。
$ sudo setcap -n 200000 cap_net_raw+ep ~/.local/share/lxc/c3/rootfs/home/karma/ping2 (-nオプションでroot uidを指定) $ sudo getcap -n ~/.local/share/lxc/c3/rootfs/home/karma/ping2 /home/karma/.local/share/lxc/c3/rootfs/home/karma/ping2 = cap_net_raw+ep [rootid=200000] $ sudo getfattr -d -m "security" ~/.local/share/lxc/c3/rootfs/home/karma/ping2 getfattr: Removing leading '/' from absolute path names # file: home/karma/.local/share/lxc/c3/rootfs/home/karma/ping2 security.capability=0sAQAAAwAgAAAAAAAAAAAAAAAAAABADQMA
設定されましたね。
再度コンテナ内に入ってみましょう。
$ lxc-attach c3 # su - karma $ ls -l ./ping* -rwxr-xr-x 1 karma users 56,920 2月 21日 16:30 ./ping* -rwxr-xr-x 1 nobody nogroup 56,920 2月 21日 16:39 ./ping2*
ホスト側の権限でコピーしていますので、ファイルの所有権が nobody:nogroup
になっていますね。
$ sudo getcap -n ./ping2 ./ping2 = cap_net_raw+ep [rootid=200000]
capability を確認すると、設定されているのがわかります。では、実行してみましょう。
$ ./ping2 -c 1 www.google.com PING www.google.com (216.58.196.228) 56(84) bytes of data. 64 bytes from kix06s01-in-f228.1e100.net (216.58.196.228): icmp_seq=1 ttl=49 time=1.22 ms --- www.google.com ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 1.226/1.226/1.226/0.000 ms
実行できました。
Namespace 外からでも問題なく設定できますね。
kernel のデータ構造の変更
ここからは素人なので間違ってる可能性あります。詳しくはご自分でどうぞ。
4.13 までは include/uapi/linux/capability.h
を見ると、以下のような構造体がありました。
#define VFS_CAP_FLAGS_EFFECTIVE 0x000001 : (snip) #define VFS_CAP_REVISION_2 0x02000000 struct vfs_cap_data { __le32 magic_etc; /* Little endian */ struct { __le32 permitted; /* Little endian */ __le32 inheritable; /* Little endian */ } data[VFS_CAP_U32]; };
(https://elixir.bootlin.com/linux/v4.13/source/include/uapi/linux/capability.h#L53 付近)
この permitted
と inheritable
に必要な capability を設定するわけですね。effective セットは file capability の場合はオンオフだったと思いますので、magic_etc
に設定します。
effective セットを設定する場合、これ(4.13)までは、
struct vfs_cap_data xattr;
xattr.magic_etc = VFS_CAP_REVISION_2 | VFS_CAP_FLAGS_EFFECTIVE;
のように設定したようです。
4.14 以降で Namespaced file capability が導入されたあとは、
#define VFS_CAP_REVISION_2 0x02000000 #define VFS_CAP_U32_2 2 #define XATTR_CAPS_SZ_2 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_2)) #define VFS_CAP_REVISION_3 0x03000000 #define VFS_CAP_U32_3 2 #define XATTR_CAPS_SZ_3 (sizeof(__le32)*(2 + 2*VFS_CAP_U32_3)) #define XATTR_CAPS_SZ XATTR_CAPS_SZ_3 #define VFS_CAP_U32 VFS_CAP_U32_3 #define VFS_CAP_REVISION VFS_CAP_REVISION_3 struct vfs_cap_data { __le32 magic_etc; /* Little endian */ struct { __le32 permitted; /* Little endian */ __le32 inheritable; /* Little endian */ } data[VFS_CAP_U32]; }; /* * same as vfs_cap_data but with a rootid at the end */ struct vfs_ns_cap_data { __le32 magic_etc; struct { __le32 permitted; /* Little endian */ __le32 inheritable; /* Little endian */ } data[VFS_CAP_U32]; __le32 rootid; };
(https://elixir.bootlin.com/linux/v4.14/source/include/uapi/linux/capability.h#L64 付近)
というように struct vfs_ns_cap_data
という、vfs_cap_data
の最後に rootid
が追加された構造体が追加されています。そう、これが setcap
、getcap
で追加された -n
オプションの正体ですね(たぶん)。
それと同時に、従来の Namespaced されていない file capability なのか、Namespaced な file capability なのかは、magic_etc
に指定する file capability の Revision で指定します。
先のコードと対応する例だと、
struct vfs_ns_cap_data xattr;
xattr.magic_etc = VFS_CAP_REVISION_3 | VFS_CAP_FLAGS_EFFECTIVE;
となるわけですね(たぶん)。
(つづく)