TenForward

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

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

3 回シリーズになるとは思ってなかったこのシリーズ、ついに解決編です。

tenforward.hatenablog.com

1 度目の mknod は成功するものの、なぜか続けて mknod を実行すると失敗してしまうという謎のトラブル。ふと、LXC のコミットログを眺めていると seccomp 関連の修正があり、見てみると簡単な修正ですが、コミットログと修正内容を見てピンと来ました。

それがこれです。

github.com

新しいカーネルではバッファーをゼロクリアしておく必要があるとのこと。これだと 1 度実行して成功しても、そのバッファに前のデータが残っていたらエラーになりそう!ということで試すとバッチリ成功しました。

これだけだとなんなので、今回のシステムコールインターセプション機能についてまとめておきましょう。

システムコールインターセプション機能

5.0 kernel で Add seccomp trap to userspace. なる機能が追加されました。解説は次にあります。

これは、本来カーネル側で権限のチェックを行い実行の可否を判断するシステムコールの実行を、seccomp を使ってシステムコールをトラップし、ユーザースペースに処理を委ね、その応答を見て処理を進めるかどうかを決めるというもののようです。

この機能が LXD に入ったのは 3.13 と少し前で、その時のアナウンスには

LXD 3.13 を 5.0 以上のカーネル、最新の libseccomp、liblxc と組み合わせると、ユーザー空間でシステムコールインターセプトして仲介できるようになりました

とあります。

お試し

そういうわけで、先の失敗編エントリーのころからは少し環境を変えています。

  • Plamo Linux 7.1
  • kernel 5.6
  • libseccomp は 2020-03-30 時点の master branch を取得しビルド
  • LXC 4.0.1 + 先のパッチ
  • LXD 4.0.0

これで Alpine Linux コンテナを作成しました。

$ lxc launch images:alpine/3.11 a1
a1 を作成中
a1 を起動中
$ lxc shell a1
a1:~# whoami
root
a1:~# cat /proc/self/uid_map /proc/self/gid_map (非特権コンテナで動作していることを確認)
         0     100000      65537
         0     100000      65537
a1:~# mknod test c 5 1
mknod: test: Operation not permitted

上のように root ユーザーではありますが、ID のマッピングを見ると、ホスト環境では uid/gid: 100000 の一般ユーザー権限で User Namespace を使って動作していることがわかります。

当然一般ユーザーでは mknod する権限はありませんので失敗します。

ここで LXD の設定 security.syscalls.intercept.mknodtrue に設定して同じ操作を試してみましょう。

$ lxc config set a1 security.syscalls.intercept.mknod=true
$ lxc config show a1
architecture: x86_64
config:
  image.architecture: amd64
  image.description: Alpine 3.11 amd64 (20200402_13:00)
  image.os: Alpine
  image.release: "3.11"
  image.serial: "20200402_13:00"
  image.type: squashfs
  security.syscalls.intercept.mknod: "true"
    :(略)

このように設定が true になりました。コンテナに設定を反映させるために再起動します。

$ lxc restart a1
$ lxc shell a1
a1:~# mknod test c 5 1
a1:~# mknod test2 c 0 0
a1:~# ls -l
total 0
crw-rw-rw-    0 root     root        5,   1 Apr 10 11:53 test
crw-rw-rw-    0 root     root        0,   0 Apr 10 11:53 test2

このように、LXC にパッチを適用する前は失敗していた 2 つ目の mknod も無事成功し、キャラクターデバイスがふたつ作成できました。

ちなみにどんなデバイスでも作成できるわけではありません。LXD 側でその辺りは制御されており、問題のないデバイスのみが作成できます。作成できるデバイスのリストは公式文書にあります。

リストにないデバイスファイルは次のように失敗します。

a1:~# mknod test3 c 5 2
mknod: test3: Operation not permitted

このあたりの機能、もう少し遊んでみるつもりです。

Linux 5.6 で追加された Time Namespace(1)

Linux 5.6 の情報をなんとなく調べてたらコンテナ関連の機能増えてました。lwn.net で適当な記事を絞りきれなかったのでとりあえずそれっぽいコミットをメモ代わりに

このあたりもかな

とりあえず 5.6 kernel で起動してみました。

$ uname -r
5.6.0-plamo64
$ ls -l /proc/self/ns
合計 0
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 net -> net:[4026532008]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 pid -> pid:[4026531836]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 pid_for_children -> pid:[4026531836]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 time -> time:[4026531834]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 time_for_children -> time_for_children:[4026531834]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 user -> user:[4026531837]
lrwxrwxrwx 1 karma users 0  4月  1日  11:42 uts -> uts:[4026531838]

増えてますね!!

(2020-04-07 追記)

id:udzura さんが試してくれてます❗リリースされている util-linux ではダメですので、試したい人はリポジトリから持ってきましょう。

(2020-04-09 追記)

udzura さんによるブログエントリもリリースされました!これでこのテーマでのこのブログでの(2)はないかも?w

udzura.hatenablog.jp

LXDのシステムコールインターセプション機能を試す(ちょっと成功編)

昨日失敗した「システムコールインターセプション」機能のお試し。

tenforward.hatenablog.com

昨日追記した

t=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

でふと思いつきました。私が試している Plamo Linux 7.1 には systemd はいませんので、これはコンテナ上の systemd のお話だと仮定して、このパスについて調べました。コンテナ上に /run/systemd/inaccessible/chr ディレクトリが存在しなかったので、このディレクトリがないことによるエラーじゃないかと思い、それじゃあということで試しました。(ディレクトリがない理由はわかりません)

images.linuxcontainers.org にある alpine のイメージを使ってコンテナを作ります。

$ lxc launch images:alpine/3.11 a1
a1 を作成中
a1 を起動中                                         
$ lxc shell a1
a1:~# mknod test c 5 1
mknod: test: Operation not permitted

mknodが失敗する所までは同じ。security.syscalls.intercept.mknodtrue に設定してコンテナを再起動すると、特にエラーになることもなく起動します。

$ lxc config set a1 security.syscalls.intercept.mknod true
$ lxc restart a1
$ lxc shell a1

ここでコンテナ内で mknod すると、

a1:~# mknod test c 5 1
a1:~# ls -l
total 0
crw-rw-rw-    0 root     root        5,   1 Mar 31 09:42 test

無事デバイスファイルが作成できました。当たり前ですが、あっさりすぎるほど成功です。

コンテナログにも関連するログが出ていますね。

lxc a1 20200331094221.740 TRACE    seccomp - (path to lxc src)/src/lxc/seccomp.c:lxc_seccomp_load:1267 - Retrieved new seccomp listener fd 7
lxc a1 20200331094221.740 TRACE    attach - (path to lxc src)/src/lxc/attach.c:attach_child_main:810 - Loaded seccomp profile
lxc a1 20200331094221.740 TRACE    commands - (path to lxc src)/src/lxc/commands.c:lxc_cmd_accept:1522 - Accepted new client as fd 14 on command server fd 7
lxc a1 20200331094221.740 TRACE    commands - (path to lxc src)/src/lxc/commands.c:lxc_cmd_rsp_recv:123 - Command "seccomp_notify_add_listener" received response
lxc a1 20200331094221.740 DEBUG    commands - (path to lxc src)/src/lxc/commands.c:lxc_cmd_rsp_recv:156 - Response data length for command "seccomp_notify_add_listener" is 0
lxc a1 20200331094221.740 TRACE    commands - (path to lxc src)/src/lxc/commands.c:lxc_cmd:293 - Opened new command socket connection fd 9 for command "seccomp_notify_add_listener"
lxc a1 20200331094221.740 TRACE    commands - (path to lxc src)/src/lxc/commands.c:lxc_cmd_fd_cleanup:1434 - Closing client fd 14 for command "seccomp_notify_add_listener"

これで解決!と思ったのですが、次のように続けて別のファイルを作ろうとすると、まただんまりになります。

a1:~# mknod test c 5 1  (ちゃんと作成できる)
a1:~# mknod test2 c 0 0 (だんまり)
^C
a1:~# ls -l
total 0
crw-rw-rw-    0 root     root        5,   1 Mar 31 10:04 test

このときのコンテナログはまた

lxc a1 20200331095933.691 ERROR    seccomp - /home/karma/work/PlamoBuild/lxc_git/lxc/src/lxc/seccomp.c:seccomp_notify_handler:1359 - Invalid argument - Failed to read seccomp notification

LXDのログは

t=2020-03-31T19:04:23+0900 lvl=dbug msg="Handling mknod syscall" audit_architecture=3221225534 container=a1 project=default seccomp_notify_flags=0 seccomp_notify_id=16212327395031920484 syscall_args="&{cMode:8630 cDev:1281 cPid:31237 path:test}" syscall_number=133

うーむ、また謎が

オレ、何か mknod について勘違いしてる? 😅(続くかも)

(2020-04-09 追記)

Ubuntu 20.04 LTS の beta で試してみました。LXD は snap で 3.23 が動いていましたが、alpine コンテナで実行すると同じ(最初のmknodはうまくいくが次はうまくいかない)ような感じになりました。

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 って…。誰だこのパスを設定してるのは…

スマートロック セサミmini その後

(特に検証していない私の勝手な推測が入った記事です)

スマートロック「セサミmin」、導入以来便利に使っていてなくてはならないものになっています。

ここしばらくセサミで鍵を開閉する際のキレが鈍いようで、これはパーツがヤバい予兆かも、と思って昨晩コレを注文したんですよ。

で翌日でかけて帰ると家に入れない!(中から開けてもらいましたが)なんという神タイミング!!金具がこんな風になって壊れてました。

f:id:defiant:20191212012145j:plain

以前、鍵に遊びがあるために、物理的な鍵で閉めた時とセサミで閉めた時の角度が合わなかったとき、それを解決するために、CANDY HOUSEのサポートと UnaStrada さんのアドバイスを元に、サムターン受けにクッションを貼ってなんとか調整しようとしていました。

tenforward.hatenablog.com

このあと、金具に力がかかるのか一度金具が今回と同じように破損しました。そして、↑のブログに書いたクッションよりはもう少し柔らかめの隙間テープを片側に貼って(もう片側は↑のブログの通りのちょっと硬めのやつ)運用していました。ちなみにこの時点で、鍵とアプリの閉めた位置の角度のズレはうまく解決できずあきらめ状態でした。

これでも力がかかるのか、片側の金具が少し開いてきていました。↑の回の写真でもちょっと片側の金具が開いた感じになってるのがわかるかと思いますし、今回取り外して金具単体で撮ってみたのが次の写真です。わかりづらいですが、よく見るとセサミに取り付ける部分とサムターン受けパーツを取り付ける部分は直角であるはずですが、少し傾いているのがわかるかと(わからんかw)。

f:id:defiant:20191212001652j:plain

今回、この開いた側の金具が折れたのかと思ったら、開いた金具が反対側になるので逆側に変な力が加わったのか、逆側がボキッと折れてしまっていたわけです。根元の部分が一番弱い箇所で力がかかるのか前回もここから曲がってきて破損しました。

これは、うちの鍵に遊びがあるのと、セサミ自体も上下左右に遊びがあるために、サムターン受けの隅っこの方でサムターンの隅っこを掴んで回すような形になることがあり、力がかかるのかと思います。こんな感じ(ちょっと大げさに書いてますし、勝手な推測です)。

f:id:defiant:20191212010225j:plain

さらには、サムターンを垂直で「開」、水平で「閉」にせずに、いずれもちょっと行き過ぎた位置で位置設定をしているため、手で開閉した場合、上の写真のようにセサミのサムターンがちょっと行き過ぎた位置で止まってしまい、「開」の位置なのに更に手で開けてしまおうとしたり、「閉」の位置なのにさらに手で閉めようとしてしまったりして、変な力が加わっていることも考えられます(私は注意しているけど家族みんながそんなに注意してセサミを扱っているわけではない)。(←セサミのサムターンの角度から、今開いているのか閉まってるのかの判別がしにくいんです)

というわけで、セサミの作り上、

  • 鍵のサムターンに遊びがある場合は鍵で閉めた場合とセサミで閉めた場合のサムターン位置に差が出てしまう

というこれまでにあった問題以外にも、

  • 鍵にも遊びがある上に、セサミにも上下左右方向に遊びがあるためにサムターン受けパーツに力がかかってしまい根元で折れる

という問題があるので、鍵に遊びがある場合はちょっと気をつけた方が良さそうです。

Qurio はウェブで見る限りはサムターン受けに遊びがなさそうな気がしますので、こういう問題は起こらないのかな?

結局以前 CANDY HOUSE さんに作っていただいた、結局使っていなかったアダプターを付けることにしました。↓参照。

tenforward.hatenablog.com

このアダプターは上下方向の遊びがなく、サムターン受けの上下方向の位置が固定されるため、サムターン受けが上下に動くことによってサムターン受けに力がかかることがないかなと思ったからです。これでも本来のサムターン受けは遊びがあって動くのですが、変に力が加わることはない程度しか動かないかなと。

このアダプターをつけても、鍵で閉めた場合とアプリで閉めた場合の角度の差は埋められなかったので、鍵で閉めた場合とセサミで閉めた場合の位置の差については完全にあきらめることに。

f:id:defiant:20191212002157j:plain

要望

ついでに要望を書いておこう。以前も書いたのですが、

  • 開閉位置の設定で、「手動で鍵で閉めた場合」と「アプリで閉めた場合」の位置をそれぞれ設定できるようにしてほしい

これで鍵とアプリの位置の差は吸収できると思います。ついでに書いておきますが、

  • Android 版アプリの場合、アプリを起動してから開閉ができるようになるまでの時間がかかりすぎ。逆に iOS 版はアプリ起動から開閉できるようになるまでの時間が早い気がします。これくらいの時間になれば…

子供はこの時間が待ちきれなくてアプリでの開閉を止めましたので。アプリ自体あまりアップデートしてないけど改良してるのかな? あとは、

  • 今回破損した金具、いかにも根元に力がかかって弱そうなのでもう少し構造的にどうにかならないのか?

って思います。