forked from brl/citadel
362 lines
10 KiB
Plaintext
362 lines
10 KiB
Plaintext
|
<?xml version="1.0" encoding="utf-8"?>
|
||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="resource-image">
|
||
|
<info>
|
||
|
<link type="guide" xref="index#internals"/>
|
||
|
</info>
|
||
|
<title>Citadel Resource Images</title>
|
||
|
<p>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.</p>
|
||
|
<p>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.</p>
|
||
|
<p>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.</p>
|
||
|
<p>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.</p>
|
||
|
<section>
|
||
|
<title>Resource Image Types</title>
|
||
|
<p>Currently the following image types are defined for use in Citadel:</p>
|
||
|
<section>
|
||
|
<title>1) Base Root Filesystem ("rootfs")</title>
|
||
|
<p>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.</p>
|
||
|
<p>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.</p>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>2) Kernel Image ("kernel")</title>
|
||
|
<p>The kernel modules for the running kernel are stored in a resource image
|
||
|
so that the root filesystem is independent from the running kernel.</p>
|
||
|
<p>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.</p>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>3) Extra Resource Image ("extra")</title>
|
||
|
<p>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.</p>
|
||
|
<p>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.</p>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>4) Realm Filesystem Image ("realmfs")</title>
|
||
|
<p>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.</p>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>Image Layout</title>
|
||
|
<p>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.</p>
|
||
|
<p>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.</p>
|
||
|
<p>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.</p>
|
||
|
<p>Image file:</p>
|
||
|
<code> [ Header ][ Ext4 Disk Image ][ dm-verity hash data ]</code>
|
||
|
<p>Partition:</p>
|
||
|
<code> [ Ext4 Disk Image ][ dm-verity hash data ][ unused space ][ Header ]</code>
|
||
|
</section>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>Image Header</title>
|
||
|
<p>The image header contains the following fields.</p>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<th>
|
||
|
<p>Field</p>
|
||
|
</th>
|
||
|
<th>
|
||
|
<p>Size</p>
|
||
|
</th>
|
||
|
<th>
|
||
|
<p>Content</p>
|
||
|
</th>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>MAGIC</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>4 bytes</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>('S', 'G', 'O', 'S')</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>status</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>1 byte</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Used by images installed to partition</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>flags</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>1 byte</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Various flag values</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>metainfo-len</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>2 bytes</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>16 bit big-endian length</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>metainfo</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>(metainfo-len) bytes</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>TOML document containing key-value pairs</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>signature</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>64 bytes</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>ed25519 signature over metainfo field</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
<section>
|
||
|
<title>Header Fields</title>
|
||
|
<section>
|
||
|
<title>MAGIC</title>
|
||
|
<p>The initial 4 bytes are always set to the ascii bytes 'SGOS' so that a
|
||
|
valid image file can easily be identified.</p>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>status</title>
|
||
|
<p>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.</p>
|
||
|
<p>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.</p>
|
||
|
<p>The defined status values are:</p>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<th>
|
||
|
<p>status</p>
|
||
|
</th>
|
||
|
<th>
|
||
|
<p>value</p>
|
||
|
</th>
|
||
|
<th>
|
||
|
<p>description</p>
|
||
|
</th>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>STATUS_INVALID</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>0</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Partition does not contain a valid image</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>STATUS_NEW</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>1</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Newly written partition which has not yet been booted</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>STATUS_TRY_BOOT</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>2</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Set when booting a partition for the first time</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>STATUS_GOOD</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>3</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Partition has been successfully booted at least once</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>STATUS_FAILED</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>4</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Partition has failed to boot</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>STATUS_BAD_SIG</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>5</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Signature verification on metainfo failed</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>STATUS_BAD_META</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>6</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Parsing metainfo field failed</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>flags</title>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<th>
|
||
|
<p>flag</p>
|
||
|
</th>
|
||
|
<th>
|
||
|
<p>value</p>
|
||
|
</th>
|
||
|
<th>
|
||
|
<p>description</p>
|
||
|
</th>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>FLAG_PREFERRED_BOOT</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>0x01</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Override boot choice to boot from this partition</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>FLAG_HASH_TREE</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>0x02</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Image contains an appended dm-verity hash tree</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>
|
||
|
<p>FLAG_DATA_COMPRESSED</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>0x04</p>
|
||
|
</td>
|
||
|
<td>
|
||
|
<p>Image is compressed with xz</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>metainfo-len</title>
|
||
|
<p>Length in bytes of the `metainfo` field.</p>
|
||
|
<p>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</p>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>metainfo</title>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>signature</title>
|
||
|
<p>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`</p>
|
||
|
</section>
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>Booting</title>
|
||
|
<p>During boot of Citadel, the initramfs sets up the Citadel root filesystem. The
|
||
|
filesystem is built by locating and mounting three components:</p>
|
||
|
<list>
|
||
|
<item>
|
||
|
<p>Base root filesystem</p>
|
||
|
</item>
|
||
|
<item>
|
||
|
<p>Kernel modules</p>
|
||
|
</item>
|
||
|
<item>
|
||
|
<p>Extra resources</p>
|
||
|
</item>
|
||
|
</list>
|
||
|
<p>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.</p>
|
||
|
<p>Kernel modules and extra resources are stored in file images which are
|
||
|
loop mounted during boot.</p>
|
||
|
<p>An additional type of resource image called a sealed application image exists
|
||
|
for the creation of immutable application image filesystems.</p>
|
||
|
<p>Resource images can optionally have dm-verity enabled when mounted.</p>
|
||
|
</section>
|
||
|
</section>
|
||
|
</page>
|