初めに
前回の記事で作成した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用ファイル置場。キーファイルや初回起動フラグなど。 |
ブート~アップデート~再起動の流れ
- u-bootを起動
- u-bootが環境変数とboot.scrを使ってA面を起動面に選択。
- A面用のパーティションに格納されたLinux+initramfsで起動
- initramfs内で以下の流れでパーティションをマウント
- dm-veriryでrootfsを検証、マウント
- dm-cryptで暗号化領域をマッピング、マウント
- overlayfsで暗号化領域をrootfsの上(?)にマウント
- switch_rootで検証したrootfsで起動
- raucでアップデートを以下の手順で実行
- アップデート用のファイルを書き込み可能領域に格納
- B面にデータを書き込むコマンドを実行
- 再起動
- u-bootを起動
- u-bootが環境変数とboot.scrを使ってB面を起動面に選択
- B面用のパーティションに格納されたLinux+initramfsで起動
- この後は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.bz2
とupdate-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をどこに持たせるかなど改善できそうなところはまだまだあると思うが、今回はひとまず動くところまで。
今回作成した設定は次回の記事で説明する予定。