TenForward

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

Linux 4.14 で導入された Namespaced file capabilities(2)

User Namespace 外から Namespaced file capability を設定する

前回、

tenforward.hatenablog.com

では、非特権ユーザで起動したコンテナ内の 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 付近)

この permittedinheritable に必要な 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 が追加された構造体が追加されています。そう、これが setcapgetcap で追加された -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;

となるわけですね(たぶん)。

(つづく)

tenforward.hatenablog.com