Linux 4.14 で導入された Namespaced file capabilities(1)
Hosting Casual Talks #5
本題に入る前に、このブログエントリのネタを発表してきた勉強会の感想を。
ホスティング業界に関わるエンジニアが カジュアルに 情報交換をするという、全員発表型の勉強会 "Hosting Casual Talks #5" に参加してきました。飲みながら技術について情報交換できる カジュアルな 勉強会ではありますが、内容が全く カジュアルじゃない!!
ひとりあたり10 or 15分の LT サイズの時間割り当てなのですが、皆さんちゃんと解決したい問題があり、仮説を立て、検証して、考察すると言ったストーリー性のあるスライドになっていたり、問題を解決するためにコード書きました、こういうの作りましたというような、とても LT サイズに収まるものではなく、全員持ち時間はどこへやら、ガッツリと 6 時間、ガチな内容がひたすら続くなかなか内容の濃い勉強会になりました。
で、そこで真面目に 10 分サイズで軽めに作ったネタがコレでした。一発芸でオチもなく、Linux カーネルの機能を紹介してきました。次々とすごいガチな発表がされるので、こんなネタで発表するのが徐々に辛くなって…😅
4.14 kernel はいくつかコンテナ関連の機能が追加されているのですが、その中のひとつである "Namespaced file capabilities" の紹介です。
"Namespaced file capabilities" 機能についてはこのあたりが詳しいです。
- Unprivileged File Capabilities (brauner's blog)
- Namespaced file capabilities (lwn.net)
コミットはこちら
File Capability
File Capability については色々と参考になる情報がありますので、そちらをどうぞ。
- capabilities(7)
- ケーパビリティ で権限を少しだけ与える (いますぐ実践! Linux システム管理)
- 明日使えない Linux の capabilities の話 (@nojima's blog)
- Linux Capability - ケーパビリティについての整理 (ローファイ日記)
- (F15)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
は実行できません。
(つづく)