TenForward

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

LXDのシステムコールインターセプション機能を試す(失敗編)

このエントリ、ほぼやりかけのことを忘れないための自分用メモです。あまり役に立つことは書いてません(オチがないし)。まあこのブログ全体がそうですが…

みなさん LXD 使ってますか? 私は便利にお仕事で使っています。Plamo LInux 上で CentOS を使うために :-)

もうだいぶ前のリリースになりますが、LXD 3.13 の新機能の中に

というものがあります。

これは Linux カーネルだと 5.0 に対応する機能の初期実装がなされています。kernelnewbies の 5.0 の "9. Security" の項の最初 にあるのがそれだと思います。

つまりseccompの機能ですね。このあたりに解説が、

seccomp でシステムコールをトラップして、ユーザースペースに処理を委ね、その返事を見て処理を進めるかどうか決める、みたいな感じでしょうか。

LXD 3.13のアナウンスに「LXD 3.13 を 5.0 以上のカーネル、最新の libseccomp、liblxc と組み合わせると、ユーザー空間でシステムコールインターセプトして仲介できるようになりました」とあるように、libseccomp は現在リリースされていない機能を含んだ master branch のものを使う必要があるし、LXC は先日リリースされている 4.0.0 が必要になります。

LXD は現在 3.23 で、カーネルも 5.5 になっており、この seccomp の機能を使ってできることは増えているようですが、とりあえずこの 3.13 で実装されたあたりを試してみました。

  • Plamo Linux 7.1
  • kernel 5.5
  • libseccompリポジトリの master ブランチを(2020-03-30時点のもの)
  • LXC 4.0.0(試行錯誤で 2020-03-30 時点の master の状態でも試してます)
  • LXD 3.23

対応の libseccomp を入れた状態で LXC のビルドを行う必要がありました。configure 時に機能の有無を確認するからです。特にこの機能を行うためにシステム共通で行うような LXC/LXD の設定はありません。

LXD で適当なコンテナを作成します。

$ lxc launch ubuntu:18.04 c1
c1 を作成中
c1 を起動中
karma@discovery:~$ lxc shell c1
mesg: ttyname failed: No such device

LXD はデフォルトで非特権コンテナです。一応確認しておきます。

root@c1:~# cat /proc/self/uid_map 
         0     100000      65537

コンテナの root は uid:100000 のユーザーにマッピングされています。ここで、

root@c1:~# mknod test c 5 1
mknod: test: Operation not permitted

当たり前ですが、UID:1000000 の一般ユーザー権限ではこのような mknod の実行は許可されません。

ここで、先に紹介した機能を使うようにコンテナの設定を行います。

$ lxc config set c1 security.syscalls.intercept.mknod true
$ lxc config show c1 | grep syscalls
  security.syscalls.intercept.mknod: "true"

設定されました。コンテナを再起動します。

$ lxc restart c1

再起動はします。ところが…、

$ sudo tail -f /var/log/lxd/c1/lxc.log
lxc c1 20200330121030.523 ERROR    cgfsng - (path to lxc src)/src/lxc/cgroups/cgfsng.c:mkdir_eexist_on_last:1142 - File exists - Failed to create directory "/sys/fs/cgroup/cpuset//lxc.monitor.c1"
lxc c1 20200330121030.524 ERROR    cgfsng - (path to lxc src)/src/lxc/cgroups/cgfsng.c:mkdir_eexist_on_last:1142 - File exists - Failed to create directory "/sys/fs/cgroup/cpuset//lxc.payload.c1"
lxc c1 20200330121030.615 ERROR    seccomp - (path to lxc src)/src/lxc/seccomp.c:seccomp_notify_handler:1359 - Invalid argument - Failed to read seccomp notification
lxc c1 20200330121030.616 ERROR    seccomp - (path to lxc src)/src/lxc/seccomp.c:seccomp_notify_handler:1359 - Invalid argument - Failed to read seccomp notification
lxc c1 20200330121030.616 ERROR    seccomp - (path to lxc src)/src/lxc/seccomp.c:seccomp_notify_handler:1359 - Invalid argument - Failed to read seccomp notification
  : (以下同じエラー)

(最初の 2 行は置いといて)このようなエラーが大量に出て、一瞬で GB 単位のログに肥大化します。ここでコンテナ内で mknod やってもエラーにはなりません。しかし当たり前ですがこのエラーのためにコンテナはだんまりです。Invalid argument ですって (-_-;)

$ lxc shell c1
root@c1:~# mknod test c 5 1
(このままだんまり)

コンテナも強制終了する必要があります。

LXC の seccomp.c の 1359 行目は

 1357 »       ret = seccomp_notify_receive(fd, req);
 1358 »       if (ret) {
 1359 »       »       SYSERROR("Failed to read seccomp notification");
 1360 »       »       goto out;
 1361 »       }

こんな感じですね。seccomp_notify_receive()関数がエラーを返してるようです。とりあえずすぐには手に負えない感じなので :-p

LXD が生成する lxc 設定ファイル(lxc.conf)を見てみましょう。それっぽいところだけ抜き出します。

$ sudo cat /var/log/lxd/c1/lxc.conf
lxc.hook.pre-start = /proc/368/exe callhook /var/lib/lxd 28 start
lxc.hook.stop = /usr/bin/lxd callhook /var/lib/lxd 28 stopns
lxc.hook.post-stop = /usr/bin/lxd callhook /var/lib/lxd 28 stop
lxc.seccomp.profile = /var/lib/lxd/security/seccomp/c1
lxc.seccomp.notify.proxy = unix:/var/lib/lxd/seccomp.socket

コンテナ起動、終了時に何か処理してますね。何やってるのかは…知りません。また調べよう。読み込んでる seccomp プロファイルを見ると、

$ sudo cat /var/lib/lxd/security/seccomp/c1
2
blacklist
reject_force_umount  # comment this to allow umount -f;  not recommended
[all]
kexec_load errno 38
open_by_handle_at errno 38
init_module errno 38
finit_module errno 38
delete_module errno 38
seccomp errno 22 [1,2146435072,SCMP_CMP_MASKED_EQ,2146435072]
seccomp errno 22 [1,8,SCMP_CMP_MASKED_EQ,8]
mknod notify [1,8192,SCMP_CMP_MASKED_EQ,61440]
mknod notify [1,24576,SCMP_CMP_MASKED_EQ,61440]
mknodat notify [2,8192,SCMP_CMP_MASKED_EQ,61440]
mknodat notify [2,24576,SCMP_CMP_MASKED_EQ,61440]

最後の方で mknodmknodat を実行した時の処理が書かれていますね。この意味は…わかりません。また調べよう。誰か知ってたら教えてw

でもなんかそれっぽい設定で mknod なんかが呼ばれた時によしなに処理する設定がなされてる風ですね、知らんけど。

Ubuntu 20.04 LTS はこのあたり使えるようにして出てくるのかしら? (いまのところそのようなパッチが適用されてる感じではなさそうだが…)

成功編を書く日は来るのか…

(追記) なんか不審なログが lxd.log に…

=2020-03-30T21:42:08+0900 lvl=dbug msg="Handling mknod syscall" audit_architecture=3221225534 container=c1 project=default seccomp_notify_flags=0 seccomp_notify_id=11827333229505280862 syscall_args="&{cMode:8192 cDev:0 cPid:13025 path:/run/systemd/inaccessible/chr}" syscall_number=133

何この /run/systemd/inaccessible/chr って…。誰だこのパスを設定してるのは…