Jetson Nano – Rootfs Encryption

Jetson Nano

この記事ではJetson Nanoでrootfsをencryptionする方法について解説します。rootfsにコードやデータを秘匿に保持したい場合はrootfsの暗号化を検討してください。Secure Boot Sequenceの記事で解説しましたが、u-bootまではSecure Bootできているため、u-bootを修正してinitrdとkernelを署名検証し、kernelがrootfsを復号しマウントできればrootfsに保存されたデータは覗き見できなくなります。rootfsの暗号化鍵を安全に隠す方法も提案します。

方針

Linuxファイルシステムを暗号化するための標準的なLUKS(cryptsetup)を導入します。rootfsがマウントされる前に使用されるメモリ上のファイルシステムであるinitrdにLUKSを導入し、initrdがrootfsを復号マウントできるようにします。

loading initrd and kernel by u-boot

またu-bootのデフォルトの動作はrootfsが保存されるパーティションからinitrdとkernelをロードするため、rootfsが暗号化されてしまうと、initrdとkernelがロードできません。その為initrdとkernelを別のパーティションに配置し、initrdとkernelをその別パーティションからロードできるようにu-bootを修正します。

前提

現時点でJetson NanoがサポートされるJetson Linuxの最新は32.7.2であるため、こちらを使用して以降の作業を進めます。またホストPCはUbuntu 20.04がインストールされていて、こちらの記事で説明したセットアップがされているものとして進めます。

initrdへのLUKS導入

Jetson Nano上で以下を実行し、initrdにLUKS(cryptsetup)を導入します。

sudo sed -i 's/MODULES=most/MODULES=dep/g' /etc/initramfs-tools/initramfs.conf
cat << EOS > cryptsetup
export KEYMAP=y
export BUSYBOX=y
export FRAMEBUFFER=y
export CRYPTSETUP=y
EOS
sudo cp cryptsetup /etc/initramfs-tools/conf.d/
sudo apt update
sudo apt install -y cryptsetupCode language: Bash (bash)

1行目で/etc/initramfs-tools/initramfs.confを修正しています。この修正によりinitrdの肥大化によるkernel起動の失敗を防ぐことができます。詳しくはこちらのページを参照してください。

上記コマンドを実行した後、/boot/extlinux/extlinux.confのINITRDに/boot/initrdではなく、/boot/initrd.imgが指定されていることを確認します。下記のように表示されれば問題ありません。(先頭の#はコメントアウトですのでその行は無視してください)

$ grep INITRD /boot/extlinux/extlinux.conf
      INITRD /boot/initrd.img
#    INITRD /boot/initrdCode language: PHP (php)

rootfsの暗号化

rootfsイメージの暗号化処理はホストPCで行います。以下の作業はこちらの記事で解説したLinux_for_Tegraディレクトリで実施します。

initrdにLUKSを導入したJetson NanoのSD cardをホストPCに接続し、以下のようにマウントします。

sudo mkdir /mnt/jetson-nano-app
sudo mount /dev/sdx1 /mnt/jetson-nano-appCode language: Bash (bash)

/dev/sdx1はホストPCでのSD Cardのデバイス名です。各自の環境に応じて読み替えてください。

LUKSフォーマットで暗号化rootfsイメージを作成します。

$ dd if=/dev/zero of=bootloader/system.img.raw.crypt bs=1G count=14
$ sudo losetup -f bootloader/system.img.raw.crypt
$ losetup | grep system.img.raw.crypt
/dev/loop??...
$ sudo cryptsetup luksFormat --type luks1 /dev/loop?? 
Are you sure? (Type uppercase yes): YES
Enter passphrase for /path/to/Linux_for_Tegra/bootloader/system.img.raw.crypt: supersecret
Verify passphrase: supersecret
$ sudo cryptsetup luksOpen /dev/loop?? luks
Enter passphrase for /path/to/Linux_for_Tegra/bootloader/system.img.raw.crypt: supersecret
$ ls /dev/mapper/
control  luks
$ sudo mkfs.ext4 /dev/mapper/luks
$ mkdir luks
$ sudo mount /dev/mapper/luks luks
# NOTE: `/mnt/jetson-nano-app/` should end with `/`
$ sudo rsync -axHAWX --numeric-ids --info=progress2 /mnt/jetson-nano-app/ luks
$ sudo umount luks
$ sudo cryptsetup luksClose /dev/mapper/luks
$ sudo losetup -d /dev/loop??
$ sudo bootloader/mksparse -v -fillpattern=0 bootloader/system.img.raw.crypt bootloader/system.imgCode language: Bash (bash)

/dev/loop??はホストPCにより値が異なります。各自の環境に応じて読み替えてください。

cryptsetup luksFormat –type luks1 を指定している部分がありますが、Jetson Nanoはluks1を使用するため、ホストPCで明示的に指定する必要があります。

上記作業により、bootloader/system.img.raw.cryptはパスフレーズsupersecretで暗号化されます。

initrdとkernelのパーティションとパーティションイメージの作成

bootloader/t210ref/cfg/flash_l4t_t210_max-spi_sd_p3448.xmlを以下のように修正しinitrdとkernelを配置するパーティションを作成します。

@@ -255,6 +255,20 @@
               can be accessed as the fixed known special device `/dev/mmcblk0p1`. </description>
         </partition>
 
+        <partition name="BOOTPART" id="2" type="data">
+            <allocation_policy> sequential </allocation_policy>
+            <filesystem_type> basic </filesystem_type>
+            <size> 134217728 </size>
+            <file_system_attribute> 0 </file_system_attribute>
+            <allocation_attribute> 0x8 </allocation_attribute>
+            <percent_reserved> 0 </percent_reserved>
+            <align_boundary> 4096 </align_boundary>
+            <filename> bootpart.img </filename>
+            <description> **Optional.** Contains the copy of /boot in rootfs. This partition must be defined after
+              `primary_GPT` so that it can be accessed as the fixed known special device
+              `/dev/mmcblk0p2`. </description>
+        </partition>
+
         <partition name="GPT" type="GPT">
             <allocation_policy> sequential </allocation_policy>
             <filesystem_type> basic </filesystem_type>Code language: Diff (diff)

次にパーティションのイメージを作成します。上記を見るとわかりますが、bootpart.imgという名前のイメージを作成します。

まずSD Cardから/boot以下をコピーします。

dd if=/dev/zero of=bootloader/bootpart.img bs=1M count=128
mkfs.ext4 bootloader/bootpart.img
mkdir bootpart
sudo mount bootloader/bootpart.img bootpart
# NOTE: `/mnt/jetson-nano-app/boot` should not end with `/`
sudo rsync -axHAWX --numeric-ids /mnt/jetson-nano-app/boot bootpartCode language: Bash (bash)

initrdにrootfsのパスフレーズを出力するスクリプトkey.shを追加します。

sudo unmkinitramfs bootpart/boot/initrd.img initrd
sudo tee initrd/scripts/key.sh << EOS > /dev/null
#!/bin/sh

echo -n "supersecret"
EOS
sudo chmod +x initrd/scripts/key.sh
(
cd initrd
sudo bash -c "find . | cpio --quiet -H newc -o | gzip -9 -n > ../bootpart/boot/initrd.img"
)Code language: JavaScript (javascript)

bootpart/boot/extlinux/extlinux.confを修正します。特にroot=/dev/mmcblk0p1の記述は非暗号化rootfsの指定の為削除します。

@@ -7,7 +7,7 @@
       MENU LABEL primary kernel
       LINUX /boot/Image
       INITRD /boot/initrd.img
-      APPEND ${cbootargs} quiet root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 
+      APPEND ${cbootargs}
 
 # When testing a custom kernel, it is recommended that you create a backup of
 # the original kernel and add a new entry to this file so that the device canCode language: PHP (php)

u-bootの修正

u-bootのカスタマイズの方法はこちらの記事を参照してください。

参考記事