TenForward

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

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]

増えてますね!!

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 について勘違いしてる? 😅(続くかも)

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 版はアプリ起動から開閉できるようになるまでの時間が早い気がします。これくらいの時間になれば…

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

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

って思います。

ICMP sockets

連載の第42回

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

なんてことを書いてますが、同僚から「Arch Linux でやるとなぜか ping が実行できてしまう」という情報が。確かに Arch でやると実行できてしまいます。

$ lsb_release -d
Description:    Arch Linux
$ cp /usr/bin/ping .
$ getcap ./ping 
$ ./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.025 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.025/0.025/0.025/0.000 ms

strace とかで見てみるとどうも SOCK_RAW でなく SOCK_DGRAM を使ってる模様。どうやらこれらしい。hayajo さんからも同じ情報を頂いて自信を持って、これを手がかりにググってみると、

icmp(7) にも記載が。えー、2.6.39 から?

$ man icmp
    :(snip)
       ping_group_range (two integers; default: see below; since Linux 2.6.39)
              Range of the group IDs (minimum and maximum  group  IDs,  inclu‐
              sive) that are allowed to create ICMP Echo sockets.  The default
              is "1 0", which means no group is allowed to  create  ICMP  Echo
              sockets.

いやー、Linux 奥が深いですね。こんなの知りませんでした。Arch を確認してみると確かに設定されています。

$ sysctl -a 2>/dev/null | grep ping_group_range 
net.ipv4.ping_group_range = 0    2147483647

Plamo だと

$ /sbin/sysctl -a 2>/dev/null | grep ping
net.ipv4.ping_group_range = 1    0

Arch の値はやりすぎ感あるのでとりあえず 0〜65534 とすると、

$ sudo sysctl -w net.ipv4.ping_group_range="0 65534"
net.ipv4.ping_group_range = 0 65534
$ /sbin/sysctl -n net.ipv4.ping_group_range
0  65534

設定できたので、

$ cp /bin/ping .
$ /sbin/getcap ./ping 
$ ./ping -c1 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) バイトのデータ
64 バイト応答 送信元 127.0.0.1: icmp_seq=1 ttl=64 時間=0.013ミリ秒

--- 127.0.0.1 ping 統計 ---
送信パケット数 1, 受信パケット数 1, パケット損失 0%, 時間 0ミリ秒
rtt 最小/平均/最大/mdev = 0.013/0.013/0.013/0.000ミリ秒

実行できました。

ちなみに「安全のためにコピーしたら外れる」とか書きましたが、cp コマンドでも CAP_SETFCAP ケーパビリティがあれば --preserve=xattr とかやればファイルケーパビリティ保存したままコピーされますね。

(補足・2019-11-29) Twitter で systemd がそのように設定していると教えていただきました。ありがとうございます。

        * This release enables unprivileged programs (i.e. requiring neither
          setuid nor file capabilities) to send ICMP Echo (i.e. ping) requests
          by turning on the "net.ipv4.ping_group_range" sysctl of the Linux
          kernel for the whole UNIX group range, i.e. all processes. This
          change should be reasonably safe, as the kernel support for it was
          specifically implemented to allow safe access to ICMP Echo for
          processes lacking any privileges. If this is not desirable, it can be
          disabled again by setting the parameter to "1 0".