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

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

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

プロセスのケーパビリティを操作する

実際にケーパビリティによって必要な特権のみを持って動く例を見ましたので,この後は実行中のプロセスが持っているケーパビリティを操作した場合の例を見てみましょう。

特権を持っていないプロセスがいきなり特権を取得できませんので,実行中のプロセスが自身のケーパビリティを操作する場合は,持っているケーパビリティを減らしていくことになります。

ケーパビリティを持っていなければ,たとえrootであってもコマンドは実行できません。試してみましょう。libcapに含まれるcapshコマンドで簡単に試せます。ここで指定するケーパビリティは,マニュアルやcapability.hで定義されているケーパビリティを指定します(小文字でも構いません⁠⁠。

# id
uid=0(root) gid=0(root) groups=0(root)
# capsh --drop="cap_net_raw" -- -c "ping -c 1 127.0.0.1"
ping: socket: Operation not permitted
(cap_net_rawを削除して実行したのでrootユーザなのにpingが実行できない)

cap_net_rawケーパビリティを削除したときにケーパビリティがどのようになっているのかを確認してみましょう。まずはrootで普通に実行しているbashが持つケーパビリティです。

# grep Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

サポートされているケーパビリティすべてが有効になっているのがわかります。ここでcap_net_rawを削除すると次のようになります。

# capsh --drop="cap_net_raw" --
(cap_net_rawを削除してbashを実行)
# grep Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000003fffffdfff
CapEff: 0000003fffffdfff
CapBnd: 0000003fffffdfff
CapAmb: 0000000000000000

/usr/include/linux/capability.hを見ると,CAP_NET_RAWは13ですので,13ビットシフトした下位から14ビット目が0になっているのがわかります。

ファイルケーパビリティ

プロセスは,先に述べたようにケーパビリティセットを持っています。実行中にケーパビリティを変更できますが,今持っていないケーパビリティをいきなりプロセス実行中に増やすことはできません。基本的にケーパビリティは実行中に操作すると減っていくだけです。

セキュリティ的には不要な特権が減っていくわけですから意味があるわけですが,最初から特定の特権を与えておきたいケースには対応できません。先のpingコマンドに設定されていたsetuidのようなケースです。

そこでsetuidのように,あらかじめファイルにケーパビリティを設定しておくことができます。これがファイルケーパビリティです。

ファイルケーパビリティも,プロセスのケーパビリティと同様にPermittedInheritableEffectiveという3つのケーパビリティセットがあります。ただし,プロセスのケーパビリティセットと違い,ファイルケーパビリティのEffectiveケーパビリティは0 or 1の単一の値です。

次の例は CentOS 8にインストールされているpingコマンドです。

$ ls -l /bin/ping
-rwxr-xr-x. 1 root root 69160 May 11 23:22 /bin/ping
(setuidされていない)
$ id -u
1000 (一般ユーザで実行)
$ ping -c 1 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.021 ms

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

このようにsetuidされていませんがpingコマンドは実行できています。これはファイルケーパビリティが設定されているからです。

ファイルケーパビリティを確認するにはlibcapに含まれるgetcapコマンドを使います。

$ getcap /bin/ping
/bin/ping = cap_net_admin,cap_net_raw+p

これは,/bin/pingにはファイルケーパビリティのPermittedケーパビリティとして,cap_net_admincap_net_rawケーパビリティが設定されているという意味です(※1)

ファイルケーパビリティは安全のためにコピーすると設定が外れます(※2)

$ cp /bin/ping .
$ getcap ./ping
(ファイルケーパビリティが何も設定されていない)
$ ./ping 127.0.0.1
ping: socket: Operation not permitted (権限がないので実行できない)

このようにファイルケーパビリティが設定されていないので,コピーしたpingコマンドは一般ユーザーでは実行できません。

ここでコピーしたpingコマンドを一般ユーザ権限で実行できるようにファイルケーパビリティでcap_net_rawを付与してみましょう。同じくCentOS 8で実行しています。

$ sudo setcap cap_net_raw=p ./ping (permittedをオン)
$ getcap ./ping 
./ping = cap_net_raw+p (cap_net_rawが設定された)
$ ./ping -c 1 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.023 ms

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

ファイルケーパビリティを設定するには特権が必要ですので,上の例でもsetcapコマンドだけはroot権限で実行しています。

※1)
ケーパビリティのアルゴリズムをご存知の方は,PermittedだけでなくEffectiveも設定されていないとダメではないかと思われるかもしれません。iputilsのpingコマンドでは,ファイルケーパビリティのpermissiveが有効かどうかをチェックして,自身で実行中にEffectiveを設定する処理を行っています。アルゴリズムについては次回説明します。
※2)
Arch Linuxの最新の環境ではcap_net_rawケーパビリティがなくてもpingが実行できます。これはnet.ipv4.ping_group_range = 0 2147483647のように設定されているためです。詳しくはman icmpをご覧ください。またcap_setfcapケーパビリティがあれば,ケーパビリティを保持したままファイルコピーが可能です。

まとめ

今回は,プロセスのケーパビリティについて簡単に説明しました。実際にケーパビリティを確認する方法を紹介し,ケーパビリティを削除したときにコマンドの実行が失敗することを確認しました。

また,最初から特定のケーパビリティを与えてコマンドを実行したいときのためのファイルケーパビリティについて説明しました。ファイルケーパビリティを設定することで,一般ユーザでも特権が必要なコマンドが実行できました。

実は今回の記事は,udzuraさんの記事ができるまでのつなぎとして,カーネルの新機能に関する軽い話題で1回記事を書こうかなと考えて書き始めた記事でした。

その前提知識として,ファイルケーパビリティについて軽く触れる必要あったので書き始めたのですが,必要と思うことを加えていくにつれ量が増えていき,軽く触れるだけでは済まない量になってしまいました。そこで,ケーパビリティについてきちっとまとめてみようと思ったわけです。

次回まででケーパビリティ全体について説明を終わらせ,3回目で最初に書こうと思ったケーパビリティ関連の新機能について書く予定です。

著者プロフィール

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

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

Plamo Linuxメンテナ

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