プロセスやファイルのケーパビリティを確認できるツールを再発明しつつLinux capabilitiesの理解を進めている

最近社内でLinux capabilitiesの話題がでていて「そういえばちゃんと理解していないな」と思ったので、夜ちょこちょこと技術エントリとかmanとかを読んでいました。

結構な頻度で同僚のエントリがヒットするのが良いです。エントリを見つけたら「わからなければ社内でカジュアルに聞ける」という福利厚生付き。

manをPro契約しているDeepLで翻訳して一気に読んでやろうと思ったら Capability Permitted Effective などが日本語に訳されてしまってむしろ混乱してしまうという出来事もありしました。辞書設定すればいけるのかな。

プロセスやファイルのCapabilitiesを確認する方法

プロセス(スレッド)のケーパビリティを確認する方法は getpcaps コマンドを、 ファイルのケーパビリティを確認する方法は getcap コマンドを利用するのが良いでしょう。

Ubuntuならlibcap2-bin パッケージに入っているので、

$ sudo apt install libcap2-bin

で手に入れることができます。

使い方も

$ getpcaps [PID]
$ getcap [PATH]

とわかりやすいです。

capv

getpcapsgetpcap を使うのが一番良いのですが、ケーパビリティセットの表記が +ep となっていたり、ファイルに(あまり意味はないが)Effectiveのケーパビリティだけをつけると

root@cd406362eb55:/go# touch /tmp/setcap_test
root@cd406362eb55:/go# setcap all+e /tmp/setcap_test
root@cd406362eb55:/go# getcap /tmp/setcap_test
/tmp/setcap_test =
root@cd406362eb55:/go#

となっていたり*1と、cap_from_text(3)形式になっていて、私はまだ慣れません。

まだわからなかったことがあるので、理解もかねてプロセスやファイルのケーパビリティを確認できるツールを作っています。

github.com

使い方は

$ capv -p [PID]
$ capv -f [PATH]

といった感じであまり変わらないのですが、表示だけ自分が理解をしやすいように変更しています。

root@cd406362eb55:/go# capv -p 1
P(permitted)
  [CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_CHROOT CAP_MKNOD CAP_AUDIT_WRITE CAP_SETFCAP]
P(inheritable)
  [CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_CHROOT CAP_MKNOD CAP_AUDIT_WRITE CAP_SETFCAP]
P(effective)
  [CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_CHROOT CAP_MKNOD CAP_AUDIT_WRITE CAP_SETFCAP]
P(bounding)
  [CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_CHROOT CAP_MKNOD CAP_AUDIT_WRITE CAP_SETFCAP]
P(ambient)
  []

先ほどのEffectiveのケーパビリティだけたてたファイルも

root@cd406362eb55:/go# capv -f /tmp/setcap_test
F(permitted)
  []
F(inheritable)
  []
F(effective)
  1

とEffectiveがついていることがわかるようになっています(ファイルケーパビリティのEffectiveだけ0か1)。

あと、これも私が理解のために実装している機能なのですが、 -p-f の両方を指定すると、そのプロセスでファイルを実行(execve(2))したあとのスレッドのケーパビリティセットを表示できるようにしています。

root@cd406362eb55:/go# capv -p 1 -f /tmp/setcap_test
P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding)) | P'(ambient)
  []
P'(inheritable) = P(inheritable)
  [CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_CHROOT CAP_MKNOD CAP_AUDIT_WRITE CAP_SETFCAP]
P'(effective) = F(effective) ? P'(permitted) : P'(ambient)
  []
P'(bounding) = P(bounding)
  [CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_CHROOT CAP_MKNOD CAP_AUDIT_WRITE CAP_SETFCAP]
P'(ambient) = (file is privileged) ? 0 : P(ambient)
  []

ただ、私が理解のために実装しているだけなので正しいかどうかは全く保証できません*2

残り、securebitsとバウンディングセットについて、まだわかっていないので引き続き理解を進めようと思います。

*1:正確にはコンテナ内でのファイルケーパビリティセットなのでNamespaced file capabilitiesで説明しています。挙動が異なるのかもしれませんがまだ調べていません

*2:スレッドケーパビリティの確認にシステムコールではなくprocfsをパースしていたりなど