Skip to main content

Set Up Root-Verity

Goals

Configuring root-verity offers good protection against modification of the root (/) partition.

info

An alternative (both cannot be configured) is to instead configure usr-verity to protect against modification of the usr (/usr) partition.

The goal of this document is to create a Trident Host Configuration file and a COSI file that can be used to install and service an image with a root-verity partition.

Prerequisites

  1. Ensure that oras is installed.
  2. Ensure Image Customizer container is accessible.

Instructions

Step 1: Download the minimal base image

Pull minimal-os as a base image from MCR by running:

mkdir -p $HOME/staging
pushd $HOME/staging
oras pull mcr.microsoft.com/azurelinux/3.0/image/minimal-os:latest --platform linux/amd64
popd

Step 2: Build Trident RPMs

Build the Trident RPMs by running:

make bin/trident-rpms.tar.gz

After running this make command, the RPMs will be built and packaged into bin/trident-rpms.tar.gz and unpacked into bin/RPMS/x86_64:

$ ls bin/RPMS/x86_64/
trident-0.3.DATESTRING-dev.COMMITHASH.azl3.x86_64.rpm
trident-install-service-0.3.DATESTRING-dev.COMMITHASH.azl3.x86_64.rpm
trident-provisioning-0.3.DATESTRING-dev.COMMITHASH.azl3.x86_64.rpm
trident-service-0.3.DATESTRING-dev.COMMITHASH.azl3.x86_64.rpm
trident-static-pcrlock-files-0.3.DATESTRING-dev.COMMITHASH.azl3.x86_64.rpm
trident-update-poll-0.3.DATESTRING-dev.COMMITHASH.azl3.x86_64.rpm

Copy RPMs to staging folder:

cp -r bin/RPMS $HOME/staging

Step 3: Define COSI Configuration

To create a root-verity volume, there are a few Image Customizer configuration sections that are important. These sections are detailed in the docs on root-verity. Putting them together and following the Image Customizer documentation, the full configuration $HOME/staging/ic-config.yaml can look like this:

storage:
disks:
- partitionTableType: gpt
maxSize: 5G
partitions:
- id: esp
type: esp
size: 8M

- id: boot
size: 1G

- label: root-data
id: root-data
size: 2G

- label: root-hash
id: root-hash
size: 128M

- id: var
size: grow

bootType: efi

verity:
- id: root
name: root
dataDeviceId: root-data
hashDeviceId: root-hash
dataDeviceMountIdType: part-label
hashDeviceMountIdType: part-label

filesystems:
- deviceId: esp
type: fat32
mountPoint:
path: /boot/efi
options: umask=0077

- deviceId: boot
type: ext4
mountPoint:
path: /boot

- deviceId: root
type: ext4
mountPoint:
path: /
options: defaults,ro

- deviceId: var
type: ext4
mountPoint:
path: /var

os:
bootloader:
resetType: hard-reset
hostname: root-verity-image

selinux:
mode: enforcing

kernelCommandLine:
extraCommandLine:
- log_buf_len=1M

packages:
remove:
- grub2-efi-binary

install:
# replace grub2-efi-binary with grub2-efi-binary-noprefix
- grub2-efi-binary-noprefix
- curl
- device-mapper
- dracut-overlayfs
- efibootmgr
- iproute
- iptables
- lsof
- lvm2
- mdadm
- netplan
- openssh-server
- systemd-udev
- tpm2-tools
- trident-service
- veritysetup
- vim

services:
enable:
- trident

Step 4: Invoke Image Customizer

Assuming RPMs, a base image image.vhdx and Image Customizer configuration ic-config.yaml found in $HOME/staging.

Invoke Image Customizer, paying special attention to specify --output-image-format=cosi:

pushd $HOME/staging
docker run \
--rm \
--privileged=true \
-v /dev:/dev \
-v ".:/staging:z" \
mcr.microsoft.com/azurelinux/imagecustomizer:0.18.0 \
--image-file "/staging/image.vhdx" \
--config-file "/staging/ic-config.yaml" \
--rpm-source "/staging/RPMS/x86_64" \
--build-dir "/build" \
--output-image-format "cosi" \
--output-image-file "/staging/out/image.cosi"
popd

Step 5: Trident Host Configuration

Create a Trident Host Configuration file that aligns to the Image Customizer COSI that was created in step 4. The esp, root, root-hash, and var partitions/filesystems should reflect what was specified in the Image Customizer configuration.

Some things to note that are defined in the Host Configuration below:

The remainder of the Trident Host Configuration file describes things like where to find the COSI file (can be a local path, an HTTP url, or an OCI url) and what the disk device path is (in this case, /dev/sda):

image:
url: image.cosi
sha384: ignored
storage:
disks:
- id: os
device: /dev/sda
partitionTableType: gpt
partitions:
- id: esp
type: esp
size: 1G
- id: boot
type: xbootldr
size: 200M
- id: root-data-a
type: root
size: 4G
- id: root-data-b
type: root
size: 4G
- id: root-hash-a
type: root-verity
size: 1G
- id: root-hash-b
type: root-verity
size: 1G
- id: var
type: linux-generic
size: 1G

abUpdate:
volumePairs:
- id: root-data
volumeAId: root-data-a
volumeBId: root-data-b
- id: root-hash
volumeAId: root-hash-a
volumeBId: root-hash-b

verity:
- id: root
name: root
dataDeviceId: root-data
hashDeviceId: root-hash

filesystems:
- deviceId: esp
mountPoint:
path: /boot/efi
options: umask=0077
- deviceId: boot
mountPoint: /boot
- deviceId: var
mountPoint: /var
- deviceId: root
mountPoint:
path: /
options: defaults,ro

os:
selinux:
mode: enforcing
netplan:
version: 2
ethernets:
vmeths:
match:
name: enp*
dhcp4: true

Troubleshooting

With root-verity, configurations can be difficult as the configuration files are often on the root partition. In the future, this section will be expanded to include learnings and hints for how to navigate these challenges.