LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第44回 Linuxカーネルのケーパビリティ[3]

この記事を読むのに必要な時間:およそ 5.5 分

名前空間外から名前空間内で使うファイルケーパビリティを設定する

ここまでは,ユーザ名前空間内でファイルケーパビリティを設定していました。ユーザ名前空間の外から名前空間内のファイルケーパビリティを操作したり確認したりもできます。

まずはgetcapです。getcap-nオプションはコンテナ外からも同じように使えます。

$ /sbin/getcap -n ~/.local/share/lxc/c1/rootfs/home/gihyo/ping
(コンテナ外でgetcapコマンドを実行)
/home/gihyo/.local/share/lxc/c1/rootfs/home/gihyo/ping = cap_net_raw+p [rootid=200000]

setcap-nオプションで,コンテナ外からあらかじめコンテナ内のファイルにファイルケーパビリティを設定できます。コンテナ内でこのオプションを指定するとエラーになります。setcapコマンドで-nオプションを使って,rootidにUIDを設定できます。

ここまでの例と同様にUID:200000とマッピングされるコンテナ向けにpingコマンドをコピーし,コンテナ内のrootとマッピングされるUID:200000を指定してsetcapコマンドを実行します。

# cd /home/karma/.local/share/lxc/c1/rootfs/home/karma/
(コンテナ外でコンテナの/に移動)
# cp ../../bin/ping .
# /sbin/getcap -n ./ping
(ケーパビリティは設定されていない)
# /sbin/setcap -n 200000 cap_net_raw+p ./ping
(UID:200000を指定してファイルケーパビリティを設定)
# /sbin/getcap -n ./ping
./ping = cap_net_raw+p [rootid=200000]
(設定された)

このようにコンテナ外から準備をした状態でコンテナを起動し,コンテナ内でファイルケーパビリティを設定したpingコマンドを実行してみましょう。

$ lxc-start c1
$ lxc-attach c1
root@c1:~# /sbin/getcap -n ./ping
./ping = cap_net_raw+p [rootid=200000]
(コンテナ外で設定した通りにコンテナ内でも見える)
root@c1:~# su - gihyo
gihyo@c1:~$ id -u
1000 (一般ユーザ)
gihyo@c1:~$ ./ping -c1 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.029 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.029/0.029/0.029/0.000 ms
(pingが実行できた)

コンテナ外で設定したrootid=200000がコンテナ内でも設定できていることがgetcapコマンドで確認できました。そして一般ユーザ権限でpingコマンドが実行できており,コンテナ外で設定したファイルケーパビリティが機能していることがわかります。

コンテナイメージを作成する際はコンテナ起動前のホストOS上で実行しますので,このようにコンテナ外からコンテナ内で有効なファイルケーパビリティが設定できることは重要です。

それでは,このUID:200000のユーザ権限で,コンテナ外でファイルケーパビリティが設定されたpingコマンドが実行できるでしょうか? 試してみましょう。

$ id
uid=200000(gihyo) gid=200000(gihyo) groups=200000(gihyo)
(ホスト上でUID:200000のgihyoユーザになっている)
$ ls -l ./ping
-rwxr-xr-x 1 gihyo gihyo 183,872 12月  1日  22:49 ./ping
$ /sbin/getcap -n ./ping
./ping = cap_net_raw+p [rootid=200000]
(rootid=200000でファイルケーパビリティが設定されている)
$ ./ping -c1 127.0.0.1
./ping: socket: Operation not permitted
(実行できない)

ファイルケーパビリティは設定されているものの,ユーザ名前空間内にいないのでコマンドの実行はエラーになっています。

もうひとつ,rootidにコンテナのrootにマッピングされていないUIDを設定して,コンテナ内でファイルケーパビリティが機能していないことを確認しておきましょう。皆さん,結果は想像できますね。

$ sudo setcap -n 100000 cap_net_raw+p .local/share/lxc/c1/rootfs/home/gihyo/ping
(コンテナ外からrootid=100000でファイルケーパビリティを設定)
$ /sbin/getcap -n .local/share/lxc/c1/rootfs/home/gihyo/ping
.local/share/lxc/c1/rootfs/home/gihyo/ping = cap_net_raw+p [rootid=100000]
(設定されている)

このようにrootidを100000に設定してみました。

$ id -u
1000 (一般ユーザ)
$ /sbin/getcap -n ./ping
./ping = cap_net_raw+p [rootid=100000]
(rootid=100000でファイルシステムケーパビリティが設定されている)
$ ./ping 127.0.0.1
./ping: socket: Operation not permitted
(rootidが名前空間にマッピングされているUIDと異なるので実行できない)

以上のように,必要なチェックを行った上でファイルケーパビリティが機能するようになっています。このときの条件は次のような条件です。

  • ユーザ名前空間内で実行されている
  • ユーザ名前空間内のrootとマッピングされている名前空間外のUIDが,ファイルケーパビリティに設定されているrootidと一致している

まとめ

今回は,4.14カーネルから非特権コンテナ内でファイルケーパビリティが設定できるようになったことを紹介しました。

このような名前空間内で機能するファイルケーパビリティは,名前空間内でのみ機能するようになっています。

そして,ファイルケーパビリティには名前空間内のrootとマッピングされているUIDの情報が記録されており,実行時には名前空間のrootにマッピングされているUIDとファイルケーパビリティに記録されているUIDが照合されます。

このように,非特権コンテナ内でも,ファイルケーパビリティを使って限定的に特権を与えてファイルが実行できるようになりました。

参考文献

著者プロフィール

加藤泰文(かとうやすふみ)

2009年頃にLinuxカーネルのcgroup機能に興味を持って以来,Linuxのコンテナ関連の最新情報を追っかけたり,コンテナの勉強会を開いたりして勉強しています。英語力のない自分用にLXCのmanページを日本語訳していたところ,あっさり本家にマージされてしまい,それ以来日本語訳のパッチを送り続けています。

Plamo Linuxメンテナ

Twitter:@ten_forward
技術系のブログ:http://tenforward.hatenablog.com/