TenForward

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

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

Hosting Casual Talks #5

本題に入る前に、このブログエントリのネタを発表してきた勉強会の感想を。

ホスティング業界に関わるエンジニアが カジュアルに 情報交換をするという、全員発表型の勉強会 "Hosting Casual Talks #5" に参加してきました。飲みながら技術について情報交換できる カジュアルな 勉強会ではありますが、内容が全く カジュアルじゃない!!

connpass.com

ひとりあたり10 or 15分の LT サイズの時間割り当てなのですが、皆さんちゃんと解決したい問題があり、仮説を立て、検証して、考察すると言ったストーリー性のあるスライドになっていたり、問題を解決するためにコード書きました、こういうの作りましたというような、とても LT サイズに収まるものではなく、全員持ち時間はどこへやら、ガッツリと 6 時間、ガチな内容がひたすら続くなかなか内容の濃い勉強会になりました。

で、そこで真面目に 10 分サイズで軽めに作ったネタがコレでした。一発芸でオチもなく、Linux カーネルの機能を紹介してきました。次々とすごいガチな発表がされるので、こんなネタで発表するのが徐々に辛くなって…😅


4.14 kernel はいくつかコンテナ関連の機能が追加されているのですが、その中のひとつである "Namespaced file capabilities" の紹介です。

"Namespaced file capabilities" 機能についてはこのあたりが詳しいです。

コミットはこちら

File Capability

File Capability については色々と参考になる情報がありますので、そちらをどうぞ。

ここでは File Capability の説明あるあるの ping コマンドに cap_net_raw を与えて色々試してみます。

File Capability お試し

まずは普通に。ここでは Linux 4.13.16 上の Plamo Linux 7.0 で試しています。libcap は 2.26。

通常は setuid されている ping をコピーすると setuid は落とされて ping は一般ユーザーでは実行できません。

$ id -u
1000 (一般ユーザーです)
$ ls -l /bin/ping
-rwsr-sr-x 1 root root 56,920  2月 12日  20:31 /bin/ping* (setuid されています)
$ cp /bin/ping . (コピーする)
$ ls -l ./ping   (setuid は落ちています)
-rwxr-xr-x 1 karma users 56,920  3月 23日  23:35 ./ping*
$ ./ping www.google.com (setuid されていないので権限がなく実行できません)
ping: socket: Operation not permitted

ここで file capability を設定すると一般ユーザーでも限定的な権限が与えられ、実行できるようになります。

$ sudo setcap cap_net_raw+ep ./ping (cap_net_raw を与えます)
$ sudo getcap -n ./ping (cap_net_raw が設定されています)
./ping = cap_net_raw+ep
$ sudo getfattr -d -m "security" ./ping (拡張属性領域の確認。File Capability はここに保存されています)
# file: ping
security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA=

これで一般ユーザーでも ping が実行できます。

$ ./ping -c 1 www.google.com
PING www.google.com(kix05s01-in-x04.1e100.net (2404:6800:400a:808::2004)) 56 data bytes
64 bytes from kix05s01-in-x04.1e100.net (2404:6800:400a:808::2004): icmp_seq=1 ttl=51 time=7.08 ms

--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.087/7.087/7.087/0.000 ms

コンテナ内で File Capability を設定してみる(4.13.16 kernel)

これと同じ操作を一般ユーザー権限で実行しているコンテナ内で行ってみます。次のように root が uid: 200000 のユーザーにマッピングされているコンテナがあります。

karma@plamo71:~$ lxc-info c3
Name:           c3
State:          RUNNING
PID:            20569
IP:             10.0.3.122
CPU use:        0.17 seconds
BlkIO use:      12.45 MiB
Memory use:     16.59 MiB
KMem use:       1.89 MiB
Link:           vethBXNXMY
 TX bytes:      1.55 KiB
 RX bytes:      9.86 KiB
 Total bytes:   11.41 KiB
karma@plamo71:~$ sudo cat /proc/20569/{u,g}id_map
         0     200000      65536
         0     200000      65536
karma@plamo71:~$ ps auxf 20569
  : (snip)
200000   20569  0.0  0.0   4500  1600 pts/1    Ss+  18:38   0:00  \_ init [3]
200000   20772  0.0  0.1 172404  2488 ?        Ssl  18:38   0:00      \_ /sbin/rsyslogd -i /run/rsyslogd.pid
  : (snip)

このコンテナ内に入り、一般ユーザー権限で ping をコピーしてみます。当然実行はできません。

$ lxc-attach c3
# su - karma
$ id
uid=1000(karma) gid=100(users) groups=100(users),10(wheel),26(audio),28(dialout),29(video),32(cdrom),36(kvm),38(pulse),39(pulse-access),44(mlocate),47(libvirt),50(input)
$ uname -a
Linux c3 4.13.16-plamo64 #1 SMP PREEMPT Wed Dec 5 15:00:28 JST 2018 x86_64 GNU/Linux
$ cp /bin/ping .
$ ls -l ./ping 
-rwxr-xr-x 1 karma users 56,920  2月 20日  19:02 ./ping*
$ ./ping -c 1 www.google.com
ping: socket: Operation not permitted

ここで capability を設定してみましょう。

$ sudo setcap cap_net_raw+ep ./ping 
Failed to set capabilities on file `./ping' (Operation not permitted)
usage: setcap [-q] [-v] [-n <rootid>] (-r|-|<caps>) <filename> [ ... (-r|-|<capsN>) <filenameN> ]

 Note <filename> must be a regular (non-symlink) file.

エラーで setcap コマンドが実行できません。

4.14 以降のカーネルの コンテナ内で File Capability を設定してみる(4.20.10 kernel)

勉強会のときは 5.0.0 カーネルで試したデモを披露しましたが、ここでは 4.20.10 で試した結果です。

ちなみに 4.14 より前のカーネルを試そうと思って Ubuntu 16.04 の 4.4 kernel なら試せる!と思ったりすると、普通にこの機能はバックポートされていますのでご注意ください。つまりUbuntu 16.04 の 4.4 kernel でもこの機能を試せます。(←ハマった人)(2019-03-24 追記)

$ uname -a
Linux plamo71 4.20.10-plamo64 #5 SMP PREEMPT Mon Feb 18 16:16:45 JST 2019 x86_64 GNU/Linux
$ lxc-start c3
$ lxc-ls -f c3
NAME STATE   AUTOSTART GROUPS IPV4       IPV6 UNPRIVILEGED 
c3   RUNNING 0         -      10.0.3.122 -    true         
$ lxc-info c3
Name:           c3
State:          RUNNING
PID:            4817
IP:             10.0.3.122
CPU use:        0.17 seconds
BlkIO use:      12.55 MiB
Memory use:     16.97 MiB
KMem use:       2.16 MiB
Link:           vethH3LNCN
 TX bytes:      1.28 KiB
 RX bytes:      9.31 KiB
 Total bytes:   10.59 KiB
$ sudo cat /proc/4817/{u,g}id_map
         0     200000      65536
         0     200000      65536
$ ps auxf | less
  : (snip)
200000    4817  0.0  0.0   4500  1632 pts/1    Ss+  16:24   0:00  \_ init [3]
200000    5019  0.0  0.1 172404  2472 ?        Ssl  16:24   0:00      \_ /sbin/rsyslogd -i /run/rsyslogd.pid
  : (snip)

カーネルのバージョンが違う以外は同じコンテナを実行しています。

$ lxc-attach c3
# su - karma
$ id
uid=1000(karma) gid=100(users) groups=100(users),10(wheel),26(audio),28(dialout),29(video),32(cdrom),36(kvm),38(pulse),39(pulse-access),44(mlocate),47(libvirt),50(input)
$ uname -a
Linux c3 4.20.10-plamo64 #5 SMP PREEMPT Mon Feb 18 16:16:45 JST 2019 x86_64 GNU/Linux
$ cp /bin/ping .
$ ls -l ./ping
-rwxr-xr-x 1 karma users 56,920  2月 21日  16:30 ./ping*
$ ./ping www.google.com
ping: socket: Operation not permitted

当然ですが、ここまでは 4.13 カーネルのときと同じです。ここで 4.13 では失敗した setcap を試してみましょう。

$ sudo setcap cap_net_raw+ep ./ping
$ sudo getcap -n ./ping 
./ping = cap_net_raw+ep [rootid=200000]
$ sudo getfattr -d -m "security" ./ping 
# file: ping
security.capability=0sAQAAAwAgAAAAAAAAAAAAAAAAAABADQMA

4.13 では失敗した setcap が成功しています。ここで getcap-n を与えている所がミソで、これは結果のうち [rootid=200000] を表示させるために使っています。この 200000 は、コンテナの rootマッピングされている uid ですね。

つまり user namespace 内で setcap を実行すると、user namespace 内の root の uid の情報が設定されます。そして、その user namespace 内でのみ、その File capability が有効になり、権限が与えられた状態でコマンドが実行できます。

$ ./ping -c 1 www.google.com
PING www.google.com (216.58.197.4) 56(84) bytes of data.
64 bytes from kix06s02-in-f4.1e100.net (216.58.197.4): icmp_seq=1 ttl=48 time=1.30 ms

--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.303/1.303/1.303/0.000 ms

問題なく実行できていますね。

上記のファイルをコンテナの外で確認してみましょう。

$ id
uid=1000(karma) gid=100(users) groups=100(users),10(wheel),26(audio),28(dialout),29(video),32(cdrom),36(kvm),38(pulse),39(pulse-access),44(mlocate),47(libvirt),50(input)
$ sudo getcap -n ~/.local/share/lxc/c3/rootfs/home/karma/ping
/home/karma/.local/share/lxc/c3/rootfs/home/karma/ping = cap_net_raw+ep [rootid=200000]
$ ~/.local/share/lxc/c3/rootfs/home/karma/ping -c 1 www.google.com
ping: socket: Operation not permitted

コンテナ外で確認しても getcap の情報は変わりません。そしてコンテナ(user namespace)外から実行すると、設定したはずの capability は有効ではなく、ping は実行できません。

(つづく)

tenforward.hatenablog.com