Blog:
嵌入式 ARM 平台使用dm-crypt加密磁盘分区

sexta-feira, 9 de agosto de 2019

简介

出于安全考虑,嵌入式设备有时需要将某个数据分区甚至整个文件系统进行加密处理,本文就介绍在嵌入式 Linux系统下基于dm-crypt进行加密示例,dm-crypt加密具有速度快,易用性强,适用性广等特点,dm-crypt集成于linux kernel中,通过前端应用cryptsetup来进行调用。

本文演示所使用的ARM平台来自Toradex基于NXP iMX6Q SoC平台的Apalis iMX6 ARM核心板。

准备

Apalis iMX6Q ARM核心版配合Apalis Evaluation Board载板,连接调试串口UART1(载板X29)到开发主机方便调试。更多关于Apalis iMX6配合Apalis Evaluation Board载板的说明请参考Datasheet开发上手指南

开发Linux PC主机, 用于搭建Ycoto Project/OpenEmbedded 编译环境,方便编译针对Apalis iMX6Q的集成所需功能的测试Linux image。

编译集成dm-crypt的Linux image

首先根据下面文章的描述在Linux开发主机上面配置Ycoto Project/OpenEmbedded编译环境
https://developer.toradex.cn/knowledge-base/board-support-package/openembedded-(core)

本文配置使用 LinuxImageV2.8 分支。

配置Linux kernel,增加dm-crypt的支持

$ cd oe-core/build
$ make -c menuconfig virtual/kernel
Device Drivers  --->
[*] Multiple devices driver support (RAID and LVM)  --->
<*>   Device mapper support
[*]     Device mapper debugging support
<*>     Crypt target support

-*- Cryptographic API  --->
<*>   User-space interface for hash algorithms
<*>   User-space interface for symmetric key cipher algorithms
<*>   User-space interface for random number generator algorithms
<*>   User-space interface for AEAD cipher algorithms

$ bitbake virtual/kernel -f -c deploy
$ bitbake virtual/kernel -f -c compile

修改配置文件以增加所需要的安装包

conf/local.conf 文件
https://github.com/simonqin09/crypt_test/blob/master/local.conf.patch

layers/meta-openembedded/meta-oe/recipes-support/lvm2/libdevmapper_2.02.171.bb 文件
https://github.com/simonqin09/crypt_test/blob/master/libdevmapper.patch

编译部署image

执行下面命令编译 Linux image
$ bitbake -k angstrom-lxde-image

编译成功的image位于 ../deploy/images/apalis-imx6/ 目录,参考这里的说明将linux image更新到Apalis iMX6模块上

测试加密数据分区(非rootfs启动分区)

进入上一步骤更新好的Apalis iMX6 Linux系统串口终端,首先准备用于加密的分区,可以是实际的物理分区(如外部USB盘,SD卡或者sata硬盘等),也可以通过 dd 命令在现有eMMC Linux 分区上面创建一个分区块,其后续加密操作都是类似的,本文就以后者作为演示。

具体操作流程如下:

在 $HOME 目录下创建用于测试的分区块,100MB容量

root@apalis-imx6:~# cd /home/root/
root@apalis-imx6:~# dd if=/dev/urandom of=test.luks bs=1M count=100

创建加密分区,这里会要求设置加密密码,如果需要调试,可以增加 --debug 调试参数
root@apalis-imx6:~# cryptsetup luksFormat test.luks
WARNING!
========
This will overwrite data on test.luks irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase:
Verify passphrase:

可选操作:如果想要实现自动通过脚本挂载,则需要配置 key 文件,并添加到加密分区
root@apalis-imx6:~# dd if=/dev/urandom of=key bs=1024 count=4
root@apalis-imx6:~# cryptsetup luksAddKey test.luks key
Enter any existing passphrase:

打开加密分区

  • 手动方式,需要手动输入并验证密码
    root@apalis-imx6:~# cryptsetup luksOpen test.luks testvolume
    Enter passphrase for test.luks:
  • 通过 key 文件自动方式
    root@apalis-imx6:~# cryptsetup luksOpen --key-file key test.luks testvolume

格式化加密分区
root@apalis-imx6:~# mkfs.ext4 -j /dev/mapper/testvolume
mke2fs 1.43.5 (04-Aug-2017)
Creating filesystem with 100352 1k blocks and 25168 inodes
Filesystem UUID: db13767e-9057-48f2-9908-31a14ec6d02c
Superblock backups stored on blocks:
       8193, 24577, 40961, 57345, 73729
Allocating group tables: done                           
Writing inode tables: done                           
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

挂载加密分区
root@apalis-imx6:~# mkdir /mnt/test
root@apalis-imx6:~# mount /dev/mapper/testvolume /mnt/test/

然后就可以进入 /mnt/test/ 对加密分区进行操作,操作完成后,解除挂载并关闭加密分区
root@apalis-imx6:/# umount /mnt/test
root@apalis-imx6:/# cryptsetup luksClose testvolume

测试加密 Rootfs 启动分区

由于Uboot下无法完成对加密启动分区的解密,因此我们需要准备一个最小启动initramfs镜像,uboot加载initramfs启动后再解密并挂载rootfs启动分区,最后切换到这个启动分区来完成整个启动过程

首先还是通过之前配置好的Ycoto环境编译所需的initramfs镜像

配置busybox,增加 --install 和 mdev 支持

添加 fragment.cfg 文件到 layers/openembedded-core/meta/recipes-core/busybox/busybox/ 目录,内容如下:
https://github.com/simonqin09/crypt_test/blob/master/fragment.cfg

如下修改 layers/openembedded-core/meta/recipes-core/busybox/busybox_1.24.1.bb 文件
https://github.com/simonqin09/crypt_test/blob/master/busybox.patch

如下修改 layers/meta-openembedded/meta-initramfs/recipes-bsp/images/initramfs-debug-image.bb 文件
https://github.com/simonqin09/crypt_test/blob/master/initramfs-debug-image.patch

编译 initramfs image
$ cd build
$ bitbake -k initramfs-debug-image

添加 key 文件到initramfs image中

从生成目录将 initramfs image文件复制到PC的工作目录
$ mkdir work-dir
$ cd work-dir
$ cp deploy/images/apalis-imx6/Angstrom-initramfs-debug-image-glibc-ipk-v2017.12-apalis-imx6.rootfs.cpio.gz .

解压cpio.gz文件,并生成随机内容的key文件,为后面加密rootfs启动分区使用
$ mkdir temp
$ cd temp
$ gunzip -c ../Angstrom-initramfs-debug-image-glibc-ipk-v2017.12-apalis-imx6.rootfs.cpio.gz | cpio -i
$ dd if=/dev/urandom of=key bs=1024 count=4

重新压缩为 cpio.gz 文件
$ find . | cpio -H newc -o | gzip -9 > ../initramfs.cpio.gz

将生成的 cpio.gz 文件转换为 uboot 可用的initramfs image
$ cd ..
$ mkimage -A arm -O linux -T ramdisk -n "Initial Ram Disk" -d initramfs.cpio.gz initramfs.img

将生成的 initramfs.img 文件复制到FAT32分区的SD卡上面,连接到Apalis iMX6底板X18 8bit SD插槽。启动Apalis iMX6模块,进入 uboot 命令行,将 initramfs image文件复制到 iMX6 boot 分区并修改启动参数

复制 initramfs image到boot分区
Apalis iMX6 # mmc rescan
Apalis iMX6 # load mmc 1:1 ${ramdisk_addr_r} initramfs.img
Apalis iMX6 # fatwrite mmc 0:1 ${ramdisk_addr_r} initramfs.img ${filesize}

修改启动环境变量
Apalis iMX6 # setenv boot_initfs ‘run load_initramfs_files && bootm ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}’
Apalis iMX6 # setenv load_initramfs_files ‘load mmc 0:1 ${fdt_addr_r} ${fdt_file}; load mmc 0:1 ${ramdisk_addr_r} initramfs.img; load mmc 0:1 ${kernel_addr_r} ${boot_file}’
Apalis iMX6 # setenv bootcmd ‘run boot_initfs; echo; echo initramfs_failed; run emmcboot ; echo ; echo emmcboot failed ; run nfsboot ; echo ; echo nfsboot failed ; usb start ;setenv stdout serial,vga ; setenv stdin serial,usbkbd’
Apalis iMX6 # saveenv

重新启动后,系统会加载到initramfs环境下,然后进行对rootfs 分区进行加密操作

加密并挂载 rootfs 分区 /dev/mmcblk0p2
/ # cryptsetup luksFormat /dev/mmcblk0p2
/ # cryptsetup --debug luksAddKey /dev/mmcblk0p2 key
/ # cryptsetup luksOpen --key-file key /dev/mmcblk0p2 rootfs
/ # mke2fs -t ext4 /dev/mapper/rootfs
/ # mkdir -p /mnt/rootfs
/ # mount /dev/mapper/rootfs /mnt/rootfs

将rootfs 文件复制到加密启动分区

首先将前面章节3生成的Linux image文件 Apalis-iMX6_LXDE-Image-Tezi_2.8b6-2019xxxx.tar 解压,并提取 rootfs压缩包到FAT32 SD卡
$ tar xvf Apalis-iMX6_LXDE-Image-Tezi_2.8b6-2019xxxx.tar
$ cd Apalis-iMX6_LXDE-Image-Tezi_2.8b6
$ cp Apalis-iMX6_LXDE-Image.rootfs.tar.xz /media/sd (SD卡挂载目录)

然后将SD卡连接到 Apalis iMX6进行挂载
mkdir /mnt/sd
/ # mount -t vfat /dev/mmcblk1p1 /mnt/sd

最后将SD卡里面的rootfs文件系统压缩包解压到刚才创建并挂载的加密启动分区中
/ # cd /mnt/rootfs
/ # tar Jxf /mnt/sd/Apalis-iMX6_LXDE-Image.rootfs.tar.xz

卸载并关闭加密启动分区
/ # umount /mnt/rootfs
/ # cryptsetup luksClose rootfs

为了实现在 initramfs 环境下实现自动解密rootfs启动分区并切换到启动分区加载启动,需要修改 initramfs image的 init脚本文件

重新解压上面操作 c 添加 key文件后生成的 initramfs cpio.gz文件
$ mkdir temp
$ cd temp
$ gunzip -c ../ initramfs.cpio.gz | cpio -i

替换 init 文件为如下内容
https://github.com/simonqin09/crypt_test/blob/master/init.mod

重新压缩并转换为 uboot 可用的文件
$ find . | cpio -H newc -o | gzip -9 > ../initramfs-new.cpio.gz
$ cd ..
$ mkimage -A arm -O linux -T ramdisk -n "Initial Ram Disk" -d initramfs-new.cpio.gz initramfs.img

和上述操作 d 同样方法将新的 initramfs.img 文件复制到Apalis iMX6 boot分区后重新启动,系统会正常启动到最初的文件系统了,然后就可以继续进行其他操作了。
U-Boot 2016.11-2.8.6+g83a53c1 (Jul 29 2019 - 04:51:51 +0000)
CPU:   Freescale i.MX6Q rev1.5 at 792 MHz
Reset cause: POR
I2C:   ready
DRAM:  2 GiB

Hit any key to stop autoboot:  0
reading imx6q-apalis-eval.dtb
54620 bytes read in 18 ms (2.9 MiB/s)
reading initramfs-key.img
4455676 bytes read in 147 ms (28.9 MiB/s)
reading zImage
5483824 bytes read in 163 ms (32.1 MiB/s)
## Loading init Ramdisk from Legacy Image at 12200000 ...
  Image Name:   Initial Ram Disk
  Image Type:   ARM Linux RAMDisk Image (gzip compressed)
  Data Size:    4455612 Bytes = 4.2 MiB

Starting kernel ...

[    3.918790] Freeing unused kernel memory: 1024K
[    4.023894] hub 1-1.1:1.0: USB hub found
[    4.028022] hub 1-1.1:1.0: 4 ports detected
[    8.736558] EXT4-fs (dm-0): couldn't mount as ext3 due to feature incompatibilities
[    8.744787] EXT4-fs (dm-0): couldn't mount as ext2 due to feature incompatibilities
[    8.895730] EXT4-fs (dm-0): recovery complete
[    8.900834] EXT4-fs (dm-0): mounted filesystem with ordered data mode. Opts: (null)
[    9.345988] systemd[1]: System time before build time, advancing clock.
[    9.415901] systemd[1]: systemd 234 running in system mode. (+PAM -AUDIT -SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP -LIBCRYPTSETUP -GCRYPT -GN)
[    9.437154] systemd[1]: Detected architecture arm.

The Angstrom Distribution apalis-imx6 ttymxc0
Angstrom v2017.12 - Kernel
Apalis-iMX6_LXDE-Image 2.8b6 20190801
apalis-imx6 login:

总结

本文示例了基于ARM嵌入式平台在嵌入式Linux系统下通过 dm-crypt 来加密磁盘,以便对相关数据进行保护。

Autor: 秦海,技术销售工程师,韬睿(上海)

Deixe um comentário

Please login to leave a comment!
Have a Question?