Resource images are disk image files that are mounted read-only to create the citadel filesystem. The first block (4096 bytes) of the image file contains a header and immediately following the header is the raw disk image contents.
The header contains information about the image including parameters for configuring dm-verity to enforce the immutability of the image. The header also contains a public key signature over the image information so that the authenticity of the header information can be verified.
The root filesystem of citadel is also distributed as a resource image, and this image will be installed to a disk partition for normal operation.
Resource images other than the root filesystem are mounted by creating loop devices on the image file. Prior to mounting the image dm-verity is configured on the loop device or the rootfs partition.
Currently the following image types are defined for use in Citadel:
The base rootfs image is the only image type which is installed to a partition. It is mounted as the root of the Citadel filesystem. When an image is installed on a partition the 4906 byte header block is stored in the last block (8 sectors) of the partition.
Citadel uses two partitions (A & B) for the root filesystem. This allows updating one partition while the other one is being used. Then the system can be rebooted into the updated rootfs partition. If the system fails to boot after updating it will be reverted to use the working partition.
The kernel modules for the running kernel are stored in a resource image so that the root filesystem is independent from the running kernel.
During boot, this image is mounted and the kernel modules and a bind mount is created over /usr/lib/modules on the Citadel root filesystem.
This image type contains additional directories of files which are mounted during boot on the Citadel root filesystem. It contains files such as firmware files and desktop icons which occupy substantial space but do not need to be updated frequently.
By placing these files into a separate image the size of the root filesystem image is reduced. This makes the more frequently updated rootfs image smaller.
This type of image contains the entire root filesystem for running a realm container or VM. Unlike the other types of resource images, these images can be modified and then signed with keys controlled by the user. This allows updates and installation of software while still preserving the read-only property.
Each resource image file reserves an initial 4096 byte block where a header is stored. This is the maximum length of the header, which is generally much smaller than this size.
Following the header is a raw disk filesystem image which may optionally be compressed with xz compression algorithm. The disk image filesystem is ext4, but this is an implementation detail which may change in the future. A header flag (FLAG_DATA_COMPRESSED) indicates if an image is compressed and if so the image must be decompressed before being used. Image updates are distributed in compressed form and are decompressed once during installation.
When dm-verity is used a hash tree must be generated for the image. When an image is installed it is first decompressed and then the dm-verity hash data is generated. This hash data is stored in the image file immediately following the image data and the flag FLAG_HASH_TREE is set to indicate this data is present.
Image file:
[ Header ][ Ext4 Disk Image ][ dm-verity hash data ]
Partition:
[ Ext4 Disk Image ][ dm-verity hash data ][ unused space ][ Header ]
The image header contains the following fields.
Field |
Size |
Content |
---|---|---|
MAGIC |
4 bytes |
('S', 'G', 'O', 'S') |
status |
1 byte |
Used by images installed to partition |
flags |
1 byte |
Various flag values |
metainfo-len |
2 bytes |
16 bit big-endian length |
metainfo |
(metainfo-len) bytes |
TOML document containing key-value pairs |
signature |
64 bytes |
ed25519 signature over metainfo field |
The initial 4 bytes are always set to the ascii bytes 'SGOS' so that a valid image file can easily be identified.
The `status` field is used only on base rootfs image installed on a partition. It must be set to 0 for all other images. The field is used to make decisions about which parition to attempt to boot.
The status value is stored in the low nibble (4 bits) of this field and the high nibble is reserved for counting boot attempts in `STATUS_TRY_BOOT` state.
The defined status values are:
status |
value |
description |
---|---|---|
STATUS_INVALID |
0 |
Partition does not contain a valid image |
STATUS_NEW |
1 |
Newly written partition which has not yet been booted |
STATUS_TRY_BOOT |
2 |
Set when booting a partition for the first time |
STATUS_GOOD |
3 |
Partition has been successfully booted at least once |
STATUS_FAILED |
4 |
Partition has failed to boot |
STATUS_BAD_SIG |
5 |
Signature verification on metainfo failed |
STATUS_BAD_META |
6 |
Parsing metainfo field failed |
flag |
value |
description |
---|---|---|
FLAG_PREFERRED_BOOT |
0x01 |
Override boot choice to boot from this partition |
FLAG_HASH_TREE |
0x02 |
Image contains an appended dm-verity hash tree |
FLAG_DATA_COMPRESSED |
0x04 |
Image is compressed with xz |
Length in bytes of the `metainfo` field.
Since header page has a fixed size of one block (4096 bytes), and all other header fields have fixed sizes the maximum length of the `metainfo` field is 4096 - (4 + 2 + 2 + 64) = 4024 bytes
When the rootfs partition is chosen to mount, an attempt will be made to verify the signature before configuring dm-verity. If this signature verification fails, the partition status will be changed to `STATUS_BAD_SIG`
During boot of Citadel, the initramfs sets up the Citadel root filesystem. The filesystem is built by locating and mounting three components:
Base root filesystem
Kernel modules
Extra resources
The base root filesystem is stored on a partition unless running in certain special modes such as installer and live disk. During installation the same base root filesystem image is mounted from a loop mounted image file. This same file will eventually be written to a partition during installation.
Kernel modules and extra resources are stored in file images which are loop mounted during boot.
An additional type of resource image called a sealed application image exists for the creation of immutable application image filesystems.
Resource images can optionally have dm-verity enabled when mounted.