aimdevel’s blog

勉強したことを書きます

Linuxセキュリティ機能を盛り込んだラズパイのアップデート手順

初めに

前回の記事で作成したLinuxのセキュリティ機能を盛り込んだラズパイをアップデートできるようにしていく。 aimdevel.hatenablog.com 細かい話は長くなりそうなのでこの記事では概要と動かし方だけを説明し、設定値の説明は別の記事にまとめる。

環境

  • ubuntu 24.04 on wsl
  • yocto kirkstone
  • raspberrypi4B

作ったもの

AB面アップデートのシステムを構築する。
AB面アップデートとは、起動面と待機面を持ちアップデート処理時に待機面に新しいデータを書き込み、次の起動時はアップデートした待機面を起動面として使用するような方式である。
このAB面方式をとる利点は以下がある。

  • システムが止まる時間を短くできる
  • アップデート失敗した場合にも、アップデート前の面で起動することができる

アップデートツール

今回はraucを使用する。
raucとは組み込みLinuxのためのアップデートツールである。詳細は以下。 https://rauc.readthedocs.io/en/latest/

概要図

以下の図のように、linux kernelとrootfsを更新し、ユーザ固有のデータが格納された領域はそのまま残すようにアップデートする。

ユーザデータを格納するパーティションはアップデートで更新しないことを表す。 u-bootでどちらの面で起動するかを判定する。

パーティション構成

name size format description
mmcblk0p1 256MB vfat ブートパーティション。u-bootを格納
mmcblk0p2 128MB vfat linuxを格納。アップデートA面
mmcblk0p3 128MB vfat linuxを格納。アップデートB面
mmcblk0p4 none none 拡張パーティション
mmcblk0p5 500MB none rootfs。この領域をdm-verityで検証する。アップデートA面
mmcblk0p6 500MB none rootfs。この領域をdm-verityで検証する。アップデートB面
mmcblk0p7 500MB ext4 書き込み用暗号化領域。初回起動時にdm-cryptで暗号化する。アップデート後もデータが保持される領域として使用する。
mmcblk0p8 20MB ext4 dm-crypt用ファイル置場。キーファイルや初回起動フラグなど。

ブート~アップデート~再起動の流れ

  1. u-bootを起動
  2. u-bootが環境変数とboot.scrを使ってA面を起動面に選択。
  3. A面用のパーティションに格納されたLinux+initramfsで起動
  4. initramfs内で以下の流れでパーティションをマウント
    1. dm-veriryでrootfsを検証、マウント
    2. dm-cryptで暗号化領域をマッピング、マウント
    3. overlayfsで暗号化領域をrootfsの上(?)にマウント
  5. switch_rootで検証したrootfsで起動
  6. raucでアップデートを以下の手順で実行
    1. アップデート用のファイルを書き込み可能領域に格納
    2. B面にデータを書き込むコマンドを実行
  7. 再起動
  8. u-bootを起動
  9. u-bootが環境変数とboot.scrを使ってB面を起動面に選択
  10. B面用のパーティションに格納されたLinux+initramfsで起動
  11. この後はA面の起動と同じ

上記を目指して作ったものが以下。
https://github.com/aimdevel/meta-my-raspberrypi/tree/20241117
ざっくりと内容を説明する。

使い方

以下のようにkasを使ってビルドできるはず。

$ mkdir ~/workspace && cd ~/workspace
$ git clone https://github.com/aimdevel/meta-my-raspberrypi.git -b 20241117
$ kas build --target core-image-base meta-my-raspberrypi/kas-my-security.yml 
$ kas build --target update-bundle meta-my-raspberrypi/kas-my-security.yml 

ビルドに成功すると、 ~/workspace/build/tmp/deploy/images/raspberrypi4-64/ディレクトリにcore-image-base-raspberrypi4-64.wic.bz2update-bundle-raspberrypi4-64.raucbができている。 core-image-base-raspberrypi4-64.wic.bz2をsdカードに書き込んでラズパイを起動できる。
update-bunble.raucbはアップデートの際に使用するrauc用のファイル。
起動後は、ユーザ名rootでログインできる。

動作確認

  • 起動時の面管理の状態をraucコマンドで確認する。
    rootfs.0がactiveになっていることを確認できる。
$ rauc status
=== System Info ===
Compatible:  raspberrypi4-64
Variant:
Booted from: rootfs.0 (A)

=== Bootloader ===
Activated: rootfs.0 (A)

=== Slot States ===
o [rootfs.1] (/dev/mmcblk0p6, raw, inactive)
        bootname: B
        boot status: good
    [kerneldir.1] (/dev/mmcblk0p3, vfat, inactive)

x [rootfs.0] (/dev/mmcblk0p5, raw, booted)
        bootname: A
        boot status: good
    [kerneldir.0] (/dev/mmcblk0p2, vfat, active)
  • linux起動後にワークフォルダにraucbファイルを配置する sshサーバが起動しているはずなので、scpなどでraucbファイルを送り込む。
    ここでは/home/root/に配置したものとする。
  • rauc installでアップデートする
$ rauc install update-bundle-raspberrypi4-64.raucb
  • reboot
  • 起動後のマウント状態をraucコマンドで確認する。rootfs.1がactiveに変わっていることが確認できるはず。
$ rauc status
=== System Info ===
Compatible:  raspberrypi4-64
Variant:
Booted from: rootfs.1 (B)

=== Bootloader ===
Activated: rootfs.1 (B)

=== Slot States ===
x [rootfs.1] (/dev/mmcblk0p6, raw, booted)
        bootname: B
        boot status: good
    [kerneldir.1] (/dev/mmcblk0p3, vfat, active)

o [rootfs.0] (/dev/mmcblk0p5, raw, inactive)
        bootname: A
        boot status: bad
    [kerneldir.0] (/dev/mmcblk0p2, vfat, inactive)

またユーザデータ、具体的には/etcと/homeと/varディレクトリの下にあるデータが消えていないことも確認できる。

終わりに

セキュリティ機能を有効にした組み込みLinuxのアップデートを試した。
dm-verityのhash treeをどこに持たせるかなど改善できそうなところはまだまだあると思うが、今回はひとまず動くところまで。
今回作成した設定は次回の記事で説明する予定。