Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Linux コンテナのリブートとセキュリティ / SosaiLT 38th

Avatar for tenforward tenforward
October 20, 2023

Linux コンテナのリブートとセキュリティ / SosaiLT 38th

総関西サイバーセキュリティLT大会(第38回)の LT 資料です。
参考となる情報にはPDF中からリンクをしていますが、資料中のリンクは Speaker Deck 上ではクリックできないので PDF をダウンロードしてご覧ください。

Avatar for tenforward

tenforward

October 20, 2023
Tweet

More Decks by tenforward

Other Decks in Technology

Transcript

  1. 自己紹介   加藤泰文(かとうやすふみ) • X: @ten_forward • LXC で学ぶコンテナ入門 -軽量仮想化環境を実現

    する技術 (gihyo.jp, 2014 年〜) • 趣味でコンテナやってます。コンテナの主にカーネ ル周辺の実装に興味があります • 技術書典でコンテナ技術を紹介する同人本出して います(サークル名: lxc-jp) • コンテナの勉強会を主宰しています • 仕事はセキュリティの部署にいます 2/12
  2. リブートシステムコールを呼ぶコンテナ リブートする機能を持つコンテナを起動する(コンテナ作った瞬間 reboot(2) システムコール実行) 。   static int do_reboot(void

    *arg) { :(略) /* reboot システムコールを呼ぶ */ if (reboot(*cmd)) printf("failed to reboot(%d): %m\n", *cmd); } int test_reboot(int cmd, int sig) { :(略) /* clone(2) でコンテナを作成 */ ret = clone(do_reboot, stack, CLONE_NEWPID | SIGCHLD, &cmd); :(略) } int main(int argc, char *argv[]) { :(略) /* 再起動コマンドを指定してリブート */ status = test_reboot(LINUX_REBOOT_CMD_RESTART, SIGHUP); :(略) /* ホストがリブートするとここにはこない */ printf("reboot(LINUX_REBOOT_CMD_RESTART) succeed\n"); :(略) }   5/12
  3. 古い distro でさきほどのプログラムを実行する(Ubuntu 10.04)   $ virsh console ubuntu10.04

    ドメイン 'ubuntu10.04' に接続しました エスケープ文字は ^] です (Ctrl + ]) Ubuntu 10.04 LTS ubuntu1004 ttyS0 ubuntu1004 login: karma(←コンソールログイン) Password: karma@ubuntu1004:~$ gcc -o reboot reboot.c(←さきほどのプログラムをコンパイル) karma@ubuntu1004:~$ sudo ./reboot(←実行) [sudo] password for karma: [ 34.102303] Restarting system.(←システム再起動!!) fsck from util-linux-ng 2.17.2 fsck from util-linux-ng 2.17.2 /dev/vda1 was not cleanly unmounted, check forced. /dev/mapper/ubuntu1004-root: clean, 50106/482384 files, 259133/1926144 blocks (check after next mount) /dev/vda1: 203/124496 files (2.0% non-contiguous), 32786/248832 blocks mountall: fsck /boot [373] terminated with status 1 * Starting AppArmor profiles [ OK ] Ubuntu 10.04 LTS ubuntu1004 ttyS0 ubuntu1004 login:(←再起動してログインプロンプトが出た)   実演:https://asciinema.org/a/614800 6/12
  4. 少し新しいバージョンで実行する(Ubuntu 12.04) Linux 3.4(2017 年 12 月リリース)以降のカーネルで実行すると(Ubuntu 12.04 はバック ポートされている)

    。   $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 12.04 LTS Release: 12.04 Codename: precise $ sudo ./reboot reboot(LINUX_REBOOT_CMD_RESTART) succeed   コンテナ内で処理が済んでいる。 7/12
  5. 新しいカーネルでは(Linux 3.4 以降) Linux 3.4 で、コンテナ内で reboot(2) を実行したときの処理が追加されました。 どのような処理かというと、reboot(2) システムコールの処理内で、

    1. 自分はコンテナ内にいるかどうか? 2. コンテナ内にいる場合、指定されたコマンドに応じて • 再起動コマンドの場合は SIGHUP を PID 1 のプロセスに発行 • 停止コマンドの場合は SIGINT を PID 1 のプロセスに発行 3. コンテナ内にいない場合、通常の処理を続行 となりました→コンテナ内でリブート・停止しても、コンテナ内のプロセスにシグナルが送 られるだけになりました。 8/12
  6. reboot システムコール内の処理 コンテナ内から reboot(2) システムコールを発行したときの処理が追加されたあとのコー ド(kernel/reboot.c 700 行目(6.1kernel) ) 。

      SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { /* 現在の PID Namespace を取得 */ struct pid_namespace *pid_ns = task_active_pid_ns(current); /* reboot() を実行する権限があるかチェック */ if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) return -EPERM; :(略) /* PID Namespace 内のリブート処理 */ ret = reboot_pid_ns(pid_ns, cmd); :(略) /* ホストのリブート処理 */ :(略)   (コメントは私が入れています) 9/12
  7. reboot システムコール内の処理(コンテナ内の処理) コンテナ内でのリブート・停止処理(kernel/pid_namespace.c 300 行目(6.1kernel) )   int reboot_pid_ns(struct

    pid_namespace *pid_ns, int cmd) { /* コンテナ内でなければ return */ if (pid_ns == &init_pid_ns) return 0; switch (cmd) { /* リブートの場合は SIGHUP シグナルをセット */ case LINUX_REBOOT_CMD_RESTART2: case LINUX_REBOOT_CMD_RESTART: pid_ns->reboot = SIGHUP; break; /* 停止の場合は SIGINT シグナルをセット */ case LINUX_REBOOT_CMD_POWER_OFF: case LINUX_REBOOT_CMD_HALT: pid_ns->reboot = SIGINT; break; :(略) } /* シグナルの送信 */ send_sig(SIGKILL, pid_ns->child_reaper, 1); :(略) do_exit(0); :(略) }   10/12