LXDのシステムコールインターセプション機能を試す(解決編)
3 回シリーズになるとは思ってなかったこのシリーズ、ついに解決編です。
1 度目の mknod
は成功するものの、なぜか続けて mknod
を実行すると失敗してしまうという謎のトラブル。ふと、LXC のコミットログを眺めていると seccomp 関連の修正があり、見てみると簡単な修正ですが、コミットログと修正内容を見てピンと来ました。
それがこれです。
新しいカーネルではバッファーをゼロクリアしておく必要があるとのこと。これだと 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.mknod
を true
に設定して同じ操作を試してみましょう。
$ 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 側でその辺りは制御されており、問題のないデバイスのみが作成できます。作成できるデバイスのリストは公式文書にあります。
- システムコールのインターセプション (LXD ドキュメント翻訳プロジェクト)
リストにないデバイスファイルは次のように失敗します。
a1:~# mknod test3 c 5 2 mknod: test3: Operation not permitted
このあたりの機能、もう少し遊んでみるつもりです。
Linux 5.6 で追加された Time Namespace(1)
Linux 5.6 の情報をなんとなく調べてたらコンテナ関連の機能増えてました。lwn.net で適当な記事を絞りきれなかったのでとりあえずそれっぽいコミットをメモ代わりに
- The Time Namespace Appears To Finally Be On-Deck For The Mainline Linux Kernel (phoronix)
- It's Finally Time: The Time Namespace Support Has Been Added To The Linux 5.6 Kernel (phoronix)
- Merge tag 'timers-core-2020-01-27' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip (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 ではダメですので、試したい人はリポジトリから持ってきましょう。
はいコンテナマニアに嬉しい実験 https://t.co/WV8EvosC08 pic.twitter.com/D70Swjwq40
— Uchio Kondo 🦉⬛️ (@udzura) 2020年4月7日
(2020-04-09 追記)
udzura さんによるブログエントリもリリースされました!これでこのテーマでのこのブログでの(2)はないかも?w
LXDのシステムコールインターセプション機能を試す(ちょっと成功編)
昨日失敗した「システムコールインターセプション」機能のお試し。
昨日追記した
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.mknod
を true
に設定してコンテナを再起動すると、特にエラーになることもなく起動します。
$ 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]
最後の方で mknod
、mknodat
を実行した時の処理が書かれていますね。この意味は…わかりません。また調べよう。誰か知ってたら教えて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」、導入以来便利に使っていてなくてはならないものになっています。
ここしばらくセサミで鍵を開閉する際のキレが鈍いようで、これはパーツがヤバい予兆かも、と思って昨晩コレを注文したんですよ。
で翌日でかけて帰ると家に入れない!(中から開けてもらいましたが)なんという神タイミング!!金具がこんな風になって壊れてました。
以前、鍵に遊びがあるために、物理的な鍵で閉めた時とセサミで閉めた時の角度が合わなかったとき、それを解決するために、CANDY HOUSEのサポートと UnaStrada さんのアドバイスを元に、サムターン受けにクッションを貼ってなんとか調整しようとしていました。
このあと、金具に力がかかるのか一度金具が今回と同じように破損しました。そして、↑のブログに書いたクッションよりはもう少し柔らかめの隙間テープを片側に貼って(もう片側は↑のブログの通りのちょっと硬めのやつ)運用していました。ちなみにこの時点で、鍵とアプリの閉めた位置の角度のズレはうまく解決できずあきらめ状態でした。
これでも力がかかるのか、片側の金具が少し開いてきていました。↑の回の写真でもちょっと片側の金具が開いた感じになってるのがわかるかと思いますし、今回取り外して金具単体で撮ってみたのが次の写真です。わかりづらいですが、よく見るとセサミに取り付ける部分とサムターン受けパーツを取り付ける部分は直角であるはずですが、少し傾いているのがわかるかと(わからんかw)。
今回、この開いた側の金具が折れたのかと思ったら、開いた金具が反対側になるので逆側に変な力が加わったのか、逆側がボキッと折れてしまっていたわけです。根元の部分が一番弱い箇所で力がかかるのか前回もここから曲がってきて破損しました。
これは、うちの鍵に遊びがあるのと、セサミ自体も上下左右に遊びがあるために、サムターン受けの隅っこの方でサムターンの隅っこを掴んで回すような形になることがあり、力がかかるのかと思います。こんな感じ(ちょっと大げさに書いてますし、勝手な推測です)。
さらには、サムターンを垂直で「開」、水平で「閉」にせずに、いずれもちょっと行き過ぎた位置で位置設定をしているため、手で開閉した場合、上の写真のようにセサミのサムターンがちょっと行き過ぎた位置で止まってしまい、「開」の位置なのに更に手で開けてしまおうとしたり、「閉」の位置なのにさらに手で閉めようとしてしまったりして、変な力が加わっていることも考えられます(私は注意しているけど家族みんながそんなに注意してセサミを扱っているわけではない)。(←セサミのサムターンの角度から、今開いているのか閉まってるのかの判別がしにくいんです)
というわけで、セサミの作り上、
- 鍵のサムターンに遊びがある場合は鍵で閉めた場合とセサミで閉めた場合のサムターン位置に差が出てしまう
というこれまでにあった問題以外にも、
- 鍵にも遊びがある上に、セサミにも上下左右方向に遊びがあるためにサムターン受けパーツに力がかかってしまい根元で折れる
という問題があるので、鍵に遊びがある場合はちょっと気をつけた方が良さそうです。
Qurio はウェブで見る限りはサムターン受けに遊びがなさそうな気がしますので、こういう問題は起こらないのかな?
結局以前 CANDY HOUSE さんに作っていただいた、結局使っていなかったアダプターを付けることにしました。↓参照。
このアダプターは上下方向の遊びがなく、サムターン受けの上下方向の位置が固定されるため、サムターン受けが上下に動くことによってサムターン受けに力がかかることがないかなと思ったからです。これでも本来のサムターン受けは遊びがあって動くのですが、変に力が加わることはない程度しか動かないかなと。
このアダプターをつけても、鍵で閉めた場合とアプリで閉めた場合の角度の差は埋められなかったので、鍵で閉めた場合とセサミで閉めた場合の位置の差については完全にあきらめることに。
要望
ついでに要望を書いておこう。以前も書いたのですが、
- 開閉位置の設定で、「手動で鍵で閉めた場合」と「アプリで閉めた場合」の位置をそれぞれ設定できるようにしてほしい
これで鍵とアプリの位置の差は吸収できると思います。ついでに書いておきますが、
- Android 版アプリの場合、アプリを起動してから開閉ができるようになるまでの時間がかかりすぎ。逆に iOS 版はアプリ起動から開閉できるようになるまでの時間が早い気がします。これくらいの時間になれば…
子供はこの時間が待ちきれなくてアプリでの開閉を止めましたので。アプリ自体あまりアップデートしてないけど改良してるのかな? あとは、
- 今回破損した金具、いかにも根元に力がかかって弱そうなのでもう少し構造的にどうにかならないのか?
って思います。