From 7faf0ce39e2793237e91c7ffd0fef2056f6ad349 Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Mon, 7 Dec 2020 17:24:22 -0500 Subject: [PATCH] Citadel documentation system --- docs/.gitignore | 1 + docs/Makefile | 25 ++ docs/README.md | 26 ++ docs/duck/boot.duck | 84 ++++ docs/duck/citadel.duck | 61 +++ docs/duck/developer.duck | 26 ++ docs/duck/disk-layout.duck | 198 ++++++++++ docs/duck/index.duck | 11 + docs/duck/kernel-cmdline.duck | 13 + docs/duck/realm-config.duck | 88 +++++ docs/duck/realmfs.duck | 139 +++++++ docs/duck/realms.duck | 56 +++ docs/duck/resource-image.duck | 242 ++++++++++++ docs/realms.md | 247 ------------ .../citadel-documentation.bb | 20 + .../files/citadel-documentation.desktop | 9 + .../files/pages/boot.page | 66 ++++ .../files/pages/citadel.page | 57 +++ .../files/pages/developer.page | 24 ++ .../files/pages/disk-layout.page | 184 +++++++++ .../files/pages/index.page | 10 + .../files/pages/kernel-cmdline.page | 33 ++ .../files/pages/realm-config.page | 123 ++++++ .../files/pages/realmfs.page | 140 +++++++ .../files/pages/realms.page | 51 +++ .../files/pages/resource-image.page | 361 ++++++++++++++++++ .../images/citadel-extra-image.bb | 4 + .../packagegroups/packagegroup-citadel.bb | 1 + realmfs-builder/basic-image.conf | 2 +- 29 files changed, 2054 insertions(+), 248 deletions(-) create mode 100644 docs/.gitignore create mode 100644 docs/Makefile create mode 100644 docs/README.md create mode 100644 docs/duck/boot.duck create mode 100644 docs/duck/citadel.duck create mode 100644 docs/duck/developer.duck create mode 100644 docs/duck/disk-layout.duck create mode 100644 docs/duck/index.duck create mode 100644 docs/duck/kernel-cmdline.duck create mode 100644 docs/duck/realm-config.duck create mode 100644 docs/duck/realmfs.duck create mode 100644 docs/duck/realms.duck create mode 100644 docs/duck/resource-image.duck delete mode 100644 docs/realms.md create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/citadel-documentation.bb create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/citadel-documentation.desktop create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/boot.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/citadel.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/developer.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/disk-layout.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/index.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/kernel-cmdline.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/realm-config.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/realmfs.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/realms.page create mode 100644 meta-citadel/recipes-citadel/citadel-documentation/files/pages/resource-image.page diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..0013ab2 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +pages/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..a29a268 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,25 @@ + +DUCKFILES= $(notdir $(wildcard duck/*.duck)) +PAGES= $(addprefix pages/, $(DUCKFILES:.duck=.page)) +OUTDIR= pages +RECIPE_PAGES_DIR= ../meta-citadel/recipes-citadel/citadel-documentation/files/pages + +.PHONY: directories clean + +all: directories $(PAGES) + +install: directories $(PAGES) + rm -f ${RECIPE_PAGES_DIR}/*.page + cp $(PAGES) ${RECIPE_PAGES_DIR} + + +clean: + rm $(PAGES) + +directories: ${OUTDIR} + +${OUTDIR}: + mkdir -p ${OUTDIR} + +pages/%.page: duck/%.duck + ducktype -o $@ $< diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..d82929d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,26 @@ + +## Writing documentation + +http://projectmallard.org/ducktype/1.0/index.html + +## Building documentation + +Building the documentation requires the 'ducktype' utility. The makefile will use this +command to generate a directory of .page files from the .duck files in the /duck directory. + + $ sudo apt install ducktype + $ make + +## Reading documentation + +After generating the documentation it can be previewed by running yelp on the /pages directory: + + $ yelp pages + +## Installing documentation + +After making changes to the documentation, run 'make install' to update the set +of .page files in the citadel-documentation recipe. + + $ make install + diff --git a/docs/duck/boot.duck b/docs/duck/boot.duck new file mode 100644 index 0000000..72f2bd8 --- /dev/null +++ b/docs/duck/boot.duck @@ -0,0 +1,84 @@ + += Booting Citadel + [topic] +@link[guide >index#internals] + +== Disk Layout + +When Citadel is installed two disk partitions are created on the target disk. + + [screen] + sda 8:0 0 477G 0 disk + ├─sda1 8:1 0 511M 0 part + └─sda2 8:2 0 476.5G 0 part + +The first partition is an EFI boot partition and the second partition is LUKS encrypted +and contains multiple LVM volumes when decrypted. + + [screen] + [[[ + + /dev/sda1 /dev/sda2 + + [EFI ESP Boot partition] [ LUKS encrypted partition filling remainder of disk ] + . . + . . + . | . + . | . + . V . + . . + [ rootfsA ] [ rootfsB ] [ citadel-storage ] + ]]] + + +There are three logical volumes. Two root filesystem partitions so that one partition +can be updated while the other one is in use, and the remaining space is contained +in a volume called 'storage'. + + [screen sh] + # lvs + LV VG Attr LSize + rootfsA citadel -wi-a----- 2.00g + rootfsB citadel -wi-ao---- 2.00g + storage citadel -wi-ao---- 472.43g + +=== Bootloader + +==== LUKS + +The kernel initramfs has an /etc/crypttab file which guides the discovery of the LUKS partition. +The UUID of the LUKS partition is hardcoded to the value listed below. If citadel is installed +on more than one device on the system, the intended LUKS partition may not be chosen correctly. +This problem can be addressed by changing the UUID of other citadel LUKS partitions and passing +the UUID on kernel commandline to override /etc/crypttab. See systemd-cryptsetup-generator(8). + + [screen sh] + # cat /etc/crypttab + luks UUID=683a17fc-4457-42cc-a946-cde67195a101 - discard + +==== Mounting rootfs + +The initramfs boot stage is orchestrated by various systemd unit files which can be found +in the citadel source tree at: + + [screen] + citadel/meta-citadel/recipes-initrd/citadel-initramfs + +The same kernel and initramfs is used for the installer image. One task of these unit files +is to set up a live mode boot when a certain kernel command line option is set. For a regular +boot, a pair of unit files will attempt to mount the root filesystem partition when it becomes +available: + + [screen] + citadel-rootfs-mount.path + citadel-rootfs-mount.service + +The .path unit triggers every time /dev/mapper changes and the corresponding .service unit is +activated only when all of the LVM volumes inside + + [screen] + ConditionPathExists=/dev/mapper/citadel-rootfsA + ConditionPathExists=/dev/mapper/citadel-rootfsB + ConditionPathExists=/dev/mapper/citadel-storage + + diff --git a/docs/duck/citadel.duck b/docs/duck/citadel.duck new file mode 100644 index 0000000..bca8519 --- /dev/null +++ b/docs/duck/citadel.duck @@ -0,0 +1,61 @@ + += Subgraph Citadel + [topic] + +@link[guide >index] +@desc Introduction to Subgraph Citadel + +== What is Citadel? + +Citadel is the core operating system of the new version of Subgraph OS. + +Citadel includes the GNOME desktop session and a few basic system services and +nothing else. It is built and distributed as a single static disk image +rather than as a collection of software packages like a traditional Linux +distribution such as Ubuntu or Fedora. Citadel disk images are built entirely +from the source code of the individual software components. This gives us +complete control over what is included and how each component is configured. + +[note .advanced] + Citadel is a modern desktop operating system based on the GNOME desktop, but if you + prefer we also include an tiling window manager called Sway as an alternative. + +Since the Citadel root filesystem is immutable it is not possible to install +applications such as a web browser or text editor directly into Citadel. +Instead applications are run in a separate isolated environment called a Realm. + +When Citadel is first installed a single primary Realm is created and while running +a single realm the system resembles and behaves similar to any other desktop Linux +system. The separation between Citadel and the realm in which user applications are +launched is mostly transparent to the user. However, a user may create as many new +realms as they like and each new realm behaves like a freshly installed Debian Linux +environment where the user may install packages and store files. + +Realms are implemented in Subgraph OS as either containers or as virtual machines +running in a custom KVM hypervisor. Both approaches have advantages so the user is +free to choose either option for each realm they create. + +[note .advanced] + Hypervisor isolation is stronger and more secure, but container isolation uses + less system resources and makes it possible to access hardware devices and other + system features directly. A Citadel user can decide which configuration makes + more sense for each Realm they create. + +=== Stateless Foundation + +In the architecture of Citadel the building blocks of the system are +immutable filesystem images rather than packages. These images are mounted +read-only and this property is enforced with a Linux kernel feature (dm-verity) +which efficiently guarantees each block loaded from disk has a valid +cryptographic checksum. This means that Citadel always loads exactly the +operating system software prepared by Subgraph and rebooting the system will +always brings the computer into a known consistent state. + +When Citadel is updated an entirely new image is loaded rather than applying +a set of changes on top of an existing filesystem. By atomically updating the +entire system from one version to the next there is only ever a single software +configuration to consider and the system can never end up in an inconsistent state. +System upgrades cannot break your computer in mysterious ways and even if an +upgrade fails to boot for some reason, the system simply reverts to the +previously working version. + diff --git a/docs/duck/developer.duck b/docs/duck/developer.duck new file mode 100644 index 0000000..8dfe719 --- /dev/null +++ b/docs/duck/developer.duck @@ -0,0 +1,26 @@ += Developer Guide + [topic] +@link[guide >index#internals] + +== Make Root Filesystem Writable + +Sometimes it can be useful to make changes directly to the citadel root filesystem to +experiment with changes or to debug a problem. + +First $code(citadel.noverity) must be added to the kernel commandline. After booting with +this command line option verify that dm-verity has been disabled with the $code(dmsetup) +command. + + [screen] + # dmsetup status rootfs + 0 4194304 linear + +If the output displays $code(verity) instead of $code(linear) then dm-verity is enabled +and the disk cannot be safely written to. + +Next remount the root filesystem with read-write flag. + + [screen] + # mount -oremount,rw,noatime / + +== Debugging GNOME startup diff --git a/docs/duck/disk-layout.duck b/docs/duck/disk-layout.duck new file mode 100644 index 0000000..06ae7c9 --- /dev/null +++ b/docs/duck/disk-layout.duck @@ -0,0 +1,198 @@ += Disk Layout + [topic] + @link[guide >index#internals] + @desc A Hands-on guide the Citadel Disk and Filesystem Layout + +== Partitions + +During installation, two partitions are created on the disk chosen as +the target of the install. + +For example, if the installation disk is $code(/dev/sda): + +[terms] +- $code(/dev/sda1) +* 512MB EFI System Partition +- $code(/dev/sda2) +* Remainder of the disk + +The partition layout of a running system can be viewed by running the $code(lsblk) command. + +[screen] + citadel:~ # lsblk /dev/sda + NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT + sda 8:0 0 477G 0 disk + ├─sda1 8:1 0 511M 0 part + └─sda2 8:2 0 476.5G 0 part + └─luks 252:0 0 476.4G 0 crypt + ├─citadel-rootfsA 252:1 0 2G 0 lvm + │ └─rootfs 252:4 0 354M 1 crypt / + ├─citadel-rootfsB 252:2 0 2G 0 lvm + └─citadel-storage 252:3 0 472.4G 0 lvm /storage + +Several further block devices are created during boot when the main disk partition +is decrypted. + +[screen] + sda + ├─sda1 (a) /boot partition + └─sda2 (b) LUKS encrypted partition + └─citadel (c) LVM volume group + ├─citadel-rootfsA (d1) rootfs partition A (Read Only) + │ └─rootfs (e) The dm-verity device created for rootfsA + ├─citadel-rootfsB (d2) rootfs partition B (Read Only) + └─citadel-storage (f) mounted as /storage (Read/Write) + + +[terms] +- $code((a) /boot partition) +* EFI boot partition +- $code((b) LUKS encrypted partition) +* Remainder of disk is an encrypted volume +- $code((c) LVM volume group) +* Main partition contains several LVM volumes +- $code((d) citadel-rootfs(A/B)) +* Two root partitions so one can be updated while other is in use. +- $code((e) /dev/mapper/rootfs) +* verity mapper device for mounted root partion +- $code((f) /dev/mapper/citadel-storage) +* The writable filesystem + +== Citadel Filesystem Layout + +[code] + / + ├─ /run/citadel/images/ + │ │ + │ ├─ modules.mountpoint/ (modules image mounted here) + │ └─ extra.mountpoint/ (extra image mounted here) + │ + └─ /storage + │ + ├─ resources/dev (resource images for channel 'dev') + │ │ + │ ├─base-realmfs.img + │ └─main-realmfs.img + │ + ├─ /realms (/realms is a bind mount of /storage/realms) + │ ├─skel/ + │ └─config + │ + ├─ /realms/realmfs-images + │ │ + │ ├─citadel-kernel-5.7-dev-001.img + │ └─citadel-extra-dev-001.img + │ + └─ /realms/realm-main + ├─ home + └─ config + +== Resource Image Mounts + + +Resource images are mounted into the system by creating loop devices. These devices can be +viewed by running the 'losetup' command inside Citadel. + +[screen] + citadel:~ # losetup -ONAME,OFFSET,RO,BACK-FILE + NAME OFFSET RO BACK-FILE + /dev/loop1 4096 1 /storage/resources/dev/citadel-extra-dev-001.img + /dev/loop2 4096 1 /storage/realms/realmfs-images/main-realmfs.img + /dev/loop0 4096 1 /storage/resources/dev/citadel-kernel-5.0.6-dev-000.img + +Resource image files are protected against accidental changes or malicious tampering by +using dm-verity so that the kernel verifies a cryptographic checksum of each block loaded +from the image. + +You can view the verity device mapper node associated with each loop device with +the $code(lsblk) command. + +[screen] + citadel:~ # lsblk /dev/loop0 /dev/loop1 /dev/loop4 + NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT + loop0 7:0 0 116.9M 1 loop + └─verity-kernel 252:5 0 116M 1 crypt /run/citadel/images/kernel.mountpoint + loop1 7:1 0 938.9M 1 loop + └─verity-extra 252:6 0 931.5M 1 crypt /run/citadel/images/extra.mountpoint + loop2 7:2 0 4G 1 loop + └─verity-realmfs-main-11922f31 252:9 0 4G 1 crypt /run/citadel/realmfs/realmfs-main-11922f31.mountpoint + +Parameters of each dm-verity instance can be viewed with the veritysetup command. + +[screen] + citadel:~ # veritysetup status verity-kernel + /dev/mapper/verity-kernel is active and is in use. + type: VERITY + status: verified + hash type: 1 + data block: 4096 + hash block: 4096 + hash name: sha256 + salt: fa430cb7887de60dca6fd1974868036ea39cf5017eb55f02e3a76f82a12a0431 + data device: /dev/loop0 + data loop: /storage/resources/dev/citadel-kernel-5.0.6-dev-000.img + size: 237536 sectors + mode: readonly + hash device: /dev/loop0 + hash loop: /storage/resources/dev/citadel-kernel-5.0.6-dev-000.img + hash offset: 237544 sectors + +When a resource image file is mounted, a file in the root directory called 'manifest' lists +bind mounts to perform to integrate the image into the Citadel root filesystem. + +Each line of this file is a directory to bind mount from the mounted image to the root +filesystem. If a directory should be mounted to a location which is different than +the source directory the source and target directories are both listed on a single +line and separated by the ':' character. In the 'extra' image below, the directory +/usr/share from the resource image is mounted to /opt/share on the Citadel filesystem. + + +[screen] + citadel:~ # cat /run/citadel/images/kernel.mountpoint/manifest + /usr/lib/modules + + citadel:~ # cat /run/citadel/images/extra.mountpoint/manifest + /usr/lib/firmware + /usr/share:/opt/share + + +The citadel-image utility can be used to view the metainfo variables stored in the header +section of a resource image file. + +[screen] + citadel:~ # citadel-image metainfo /storage/resources/dev/citadel-extra-dev-001.img + image-type = "extra" + channel = "dev" + version = 1 + timestamp = "20190331172025" + nblocks = 195924 + shasum = "04e6f58afa6f608aff2d6cbb47cbe704f8ab0995f4dfe8e1c03655dc9bb6635a" + verity-salt = "7bf3eec3c51ffd2e82329a9fc6fe42915743874d7c5af43589e589c037ae81e5" + verity-root = "b94eb3431c4fb95e5b9bd62b4505d089414ae660d75eee0fce54b8483d3f9571" + + citadel:~ # citadel-image metainfo /storage/resources/dev/citadel-kernel-5.0.6-dev-000.img + image-type = "kernel" + kernel-version = "5.0.6" + kernel-id = "36b7a960dcd51d1649f83a7361f9eb5c2af5741ce6cc53689b411347aa1298b6" + channel = "dev" + version = 1 + timestamp = "20190407002748" + nblocks = 29692 + shasum = "c988bd7d468c409eb6cd3af8fa9e17b0a75a72d6ad765ad1749d15628a9096be" + verity-salt = "fa430cb7887de60dca6fd1974868036ea39cf5017eb55f02e3a76f82a12a0431" + verity-root = "f4c4fbaebb59d348bd44cfb1cdef54a813728aabc5acc439c2e739b63c1b8370" + + +RealmFS images also have a resource image header with a slightly different set of +metainfo variables. + +[screen] + citadel:~ # citadel-image metainfo /storage/realms/realmfs-images/main-realmfs.img + image-type = "realmfs" + realmfs-name = "main" + nblocks = 1048575 + channel = "realmfs-user" + verity-salt = "ad254e6dd385c0392ed8a6a41b849cfd4ef98ec3643e186feb011d5aa4f1d194" + verity-root = "11922f311b5a9141d65b7ef82e1c9159d75e413d1b420a7e3302ec8ec0ad8593" + + diff --git a/docs/duck/index.duck b/docs/duck/index.duck new file mode 100644 index 0000000..273c569 --- /dev/null +++ b/docs/duck/index.duck @@ -0,0 +1,11 @@ + + += Citadel Help + [guide] + +== User Guide + [#user .2column] + +== Citadel Internals + [#internals .2column] + diff --git a/docs/duck/kernel-cmdline.duck b/docs/duck/kernel-cmdline.duck new file mode 100644 index 0000000..347fbca --- /dev/null +++ b/docs/duck/kernel-cmdline.duck @@ -0,0 +1,13 @@ += Kernel Command Line Options + [topic] + @link[guide >index#internals] + +* citadel.noverity +* citadel.nosignatures +* citadel.install +* citadel.overlay +* citadel.channel +* citadel.verbose +* citadel.debug +* citadel.sway + diff --git a/docs/duck/realm-config.duck b/docs/duck/realm-config.duck new file mode 100644 index 0000000..7783edd --- /dev/null +++ b/docs/duck/realm-config.duck @@ -0,0 +1,88 @@ += Configuring Realms + [topic] + @link[guide >index#user] + @desc Realm configuration file reference + + +Realms are usually configured with the tools for managing realms, but the configuration +is stored in a TOML file in the realm directory and can also be edited by hand. + + +== Options + +[terms] +- $code(use-wayland) +* If 'true' access to Wayland display will be permitted in realm by + adding wayland socket /run/user/1000/wayland-0 + +- $code(use-x11) +* If 'true' access to X11 server will be added to realm by bind mounting directory + /tmp/.X11-unix + +- $code(use-sound) +* If 'true' allows the use of sound inside realm. The following items will + be added to realm: + * /dev/snd + * /dev/shm + * /run/user/1000/pulse + +- $code(use-kvm) +* If enabled, /dev/kvm will be added to the realm. + This option is only available for nspawn realms. + +- $code(use-shared-dir) +* If enabled the directory /realms/Shared will be bind mounted into the home directory of the realm. + This directory is shared between all running realms that have this option enabled as a + convenient way to move files between realms. + +- $code(use-ephemeral-home) +* If 'true' the home directory of this realm will be set up in ephemeral mode. + The ephemeral home directory is set up with the following steps + [steps] + * Home directory is mounted as tmpfs + * Any files in /realms/skel are copied into home directory + * Any files in /realms/realm-${name}/skel are copied into home directory + * Any directories listed in $code(ephemeral-persistent-dirs) are bind mounted + from /realms/realm-${name}/home into ephemeral home directory. + +- $code(ephemeral-persistent-dirs) default: ["Documents"] +* A list of subdirectories of /realms/realm-${name}/home to bind mount into realm + home directory when $code(ephemeral-home) is enabled. + +- $code(use-network) +* network + +- $code(network-zone) default: "clear" +* network zone + +- $code(use-gpu) +* Enables hardware graphics acceleration in relam. + if 'true' render node device /dev/dri/renderD128 will be added to realm. + +- $code(use-gpu-card0) +* If 'true' and $code(use-gpu) is also enabled, privileged device /dev/dri/card0 + will be added to realm. + +- $code(realmfs) default: "base" +* name of realmfs image + +- $code(overlay) default: "storage" +* type of overlay to use + +- $code(terminal-scheme) +* terminal color scheme + +- $code(extra-bindmounts) +* bind mounts + +- $code(extra-bindmounts-ro) +* read-only bind mounts + + +- $code(system-realm) default: false +* system realm + +- $code(autostart) default: false +* autostart realm + + diff --git a/docs/duck/realmfs.duck b/docs/duck/realmfs.duck new file mode 100644 index 0000000..b87edc8 --- /dev/null +++ b/docs/duck/realmfs.duck @@ -0,0 +1,139 @@ + += RealmFS Images + [topic] +@link[guide >index#user] +@desc Realm root filesystem images + +A RealmFS image contains a root filesystem for one or more realm instances. +Similar to resource images, RealmFS images are signed and mounted with dm-verity +to prevent tampering with the data on the root filesystem such as the +installation of malware or backdoors. The keys used to sign RealmFS images are +controlled by the user which makes it possible to upgrade software and install +new packages on the image. + +RealmFS images are always mounted as read-only and this property is enforced +with dm-verity. Since RealmFS images are immutable a single image can be shared +between multiple running realm instances. By default, when a realm is launched a +temporary overlay is added to the root filesystem so that changes can be +performed that will last only until the realm is stopped or restarted. This +allows experimenting with the system configuration or installing new software +temporarily. The root filesystem can then be reverted to the original state by +simply restarting the realm. + +== Updates + +Since the root filesystem of realms are stored on read-only disk images, +packages cannot be permanently installed or upgraded in the usual way. Changes +to the root filesystem will succeed inside a realm, but these changes will be +lost as soon as the realm is stopped or restarted. + +To make persistent changes to a RealmFS image, the image is first copied, then +changes are applied to the copy. After applying changes a new dm-verity hash +tree is generated for the image and the RealmFS image header is updated and +signed. + +[note .advanced] + The process of generating a signature and a dm-verity hash tree for a RealmFS image + after applying some changes such as updating packages is called $em[.strong](Sealing) + the image. + +=== Apt-Cacher NG Realm + +Upon booting a system utility realm is started which runs an Apt-Cacher NG +instance. Each realm is configured to use this realm as a proxy for package +installation. + +[code] + /etc/apt/apt.conf.d/000apt-cacher-ng-proxy + + Acquire::http::Proxy "http://172.17.0.213:3142/"; + +The apt source lines use the special Apt-Cacher NG syntax. + +[code] + /etc/apt/sources.list + + deb http://HTTPS///deb.debian.org/debian buster main contrib non-free + +Using a package cache avoids downloading and storing packages multiple times +when updating multiple RealmFS images. It also makes it possible to download and +cache packages while connected to a network before booting the system into a +safe mode without enabling the network to perform upgrades of realm packages. + +=== Updates (Container method) + +First the RealmFS image is copied to a temporary file. On a filesystem such as +btrfs, the image file will be cloned as a reflink rather than copying the file. +The copy of the RealmFS will then be mounted as writable so that changes can be +made. A systemd-nspawn container is launched and a root shell opened so that the +user can update packages, install new software, or perform any other +modifications to the root filesystem. + +Once the shell is exited a prompt asks the user if they would like to save the +current changes or discard them. If the user chooses to save the changes, the +copied image is then sealed by generating a dm-verity hash tree and the header +of the image is signed with the user RealmFS sealing key. + +=== Updates with pH Hypervisor + +When a realm is launched with pH, the overlay is managed by the emulated disk +device of the hypervisor which tracks changes to blocks of the disk and stores +the changed blocks in memory. Since the hypervisor is tracking all of the +changes to the disk, it can also transparently apply the changes and generate a +new sealed RealmFS image and then discard the changed blocks and start directly +using the new image. + +This process is initiated by the user when they decide they would like to commit +any changes they have made to the root filesystem in the running realm +permanently to the underlying RealmFS image. + +[steps] +* The user makes changes to the root filesystem of the realm and pH tracks the blocks that have changed. +* A user request is made to pH to apply the changes to the RealmFS image. +* pH opens a prompt on the desktop to ask the user to confirm that they really did make this request. +* A copy (or reflink) of the current RealmFS is made, and pH applies the changed blocks to this copy. +* The copy is then sealed with the RealmFS key of the user. +* Now pH can quietly swap in the new version of the RealmFS image and discard all of the tracked block changes. + +== Signing RealmFS Images + +A secret key for signing RealmFS images is generated during installation and +stored on disk in an encrypted file called the User Keyring. During boot +when the user enters a passphrase to decrypt the disk, this passphrase is also +used to decrypt the keyring file and the public and secret key pair is +loaded into the kernel key storage. + +The risk exists that an attacker who is able to compromise the kernel may +recover this secret key. This would allow the attacker to modify sealed RealmFS +images and install backdoors or other malware into realm root filesystems. Even +without obtaining the signing key an attacker who has compromised Citadel could +wait for the user to perform an update and make malicious changes at the same +time which the user will then sign. + +For these reasons, it is also possible to configure the system so that only +the public key is retained in the kernel upon boot and the user must boot +into a special mode so that the private key is available to perform updates. + +=== Safe Mode + +If upgrades are performed in normal operating mode, an attacker who has +compromised citadel can persistently backdoor the upgraded realmfs images. +Safe mode is a way to boot citadel without starting any realms or enabling the +network device. Since the integrity of the Citadel root filesystem is enforced +by dm-verity and no realms are running, even if the system had become compromised +at some point in the past it is assumed to now be in a safe state for performing +updates and signing them with the user sealing keys. + +Since the network is not available in safe mode, the packages to be installed or +upgraded must be stored somewhere. By either performing the packge updates with +the $code(--download-only) flag or installing them to the temporary overlay of a realm +the user will cause them to be stored on the Apt-Cache NG service realm so that +they are available for install in safe mode. + +== Base RealmFS image + +Citadel ships with a RealmFS image called $code(base-realmfs.img). There is nothing +special about this image other than that it is initially signed by Subgraph until +the user modifies or updates it. During installation, a copy of this RealmFS is +created with the name $code(main-realmfs.img) and sealed with the newly generated +user keys. diff --git a/docs/duck/realms.duck b/docs/duck/realms.duck new file mode 100644 index 0000000..b2ca0b4 --- /dev/null +++ b/docs/duck/realms.duck @@ -0,0 +1,56 @@ + += Citadel Realms + [topic] +@link[guide >index#user] + +Citadel contains only the base operating system and the GNOME desktop, it does not +include any applications. To be able to install and run applications Citadel can +create spaces which are called Realms. + +A Realm is a container similar to a Docker or LXC container in which any Linux +distribution could be installed. We use a Debian based image but it would not be +difficult to create an image for another Linux distribution. + +The realm containers are launched with systemd-nspawn but this is a detail of +how they are implemented and not something it is necessary to learn about in +order to use them. + +== The $em(current) realm + +Multiple realms may be launched at once but the GNOME Desktop is only associated with +one of the running realms. This realm is called the `current` realm. + +When displaying applications available to launch from the desktop, GNOME will only +be aware of applications that are installed in the realm which is set as `current` +and any application launched from the desktop will run inside this current realm. + +Setting another realm as current does not affect any applications that are already running. +Changing the current realm only means that any further applications which are launched +will now run in the newly chosen realm. + +== Realm directory layout + +The realms base directory is stored on the storage partition at `/storage/realms` and is bind mounted to `/realms` on the root filesystem for convenience. + +[screen] + /realms + config + /Shared + /skel + /default.realm -> realm-main + /realm-main + /realm-project + /realm-testing + +=== /realms/config + +This is the global realm configuration file. Options set in this file apply to every realm +unless the same option has been overridden with a different value in the config file for +a realm. + +=== /realms/Shared + +This directory is bind mounted to `/home/user/Shared` of each running realm that has +the option `use-shared-dir` enabled. It's a convenient way to move files between +different realms and between citadel and realms. + diff --git a/docs/duck/resource-image.duck b/docs/duck/resource-image.duck new file mode 100644 index 0000000..86f634a --- /dev/null +++ b/docs/duck/resource-image.duck @@ -0,0 +1,242 @@ + += Citadel Resource Images + [topic] +@link[guide >index#internals] + +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. + +== Resource Image Types + +Currently the following image types are defined for use in Citadel: + +=== 1) Base Root Filesystem ("rootfs") + +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. + +=== 2) Kernel Image ("kernel") + +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. + +=== 3) Extra Resource Image ("extra") + +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. + +=== 4) Realm Filesystem Image ("realmfs") + +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. + +=== Image Layout + +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: +[code] +[[[ + [ Header ][ Ext4 Disk Image ][ dm-verity hash data ] +]]] + +Partition: + +[code] +[[[ + [ Ext4 Disk Image ][ dm-verity hash data ][ unused space ][ Header ] +]]] + +== Image Header + +The image header contains the following fields. + +[table] +[tr] +- Field +- Size +- Content +[tr] +* MAGIC +* 4 bytes +* ('S', 'G', 'O', 'S') +[tr] +* status +* 1 byte +* Used by images installed to partition +[tr] +* flags +* 1 byte +* Various flag values +[tr] +* metainfo-len +* 2 bytes +* 16 bit big-endian length +[tr] +* metainfo +* (metainfo-len) bytes +* TOML document containing key-value pairs +[tr] +* signature +* 64 bytes +* ed25519 signature over metainfo field + + +=== Header Fields + +==== MAGIC + +The initial 4 bytes are always set to the ascii bytes 'SGOS' so that a +valid image file can easily be identified. + +==== status + +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: + +[table] +[tr] +- status +- value +- description +[tr] +* STATUS_INVALID +* 0 +* Partition does not contain a valid image +[tr] +* STATUS_NEW +* 1 +* Newly written partition which has not yet been booted +[tr] +* STATUS_TRY_BOOT +* 2 +* Set when booting a partition for the first time +[tr] +* STATUS_GOOD +* 3 +* Partition has been successfully booted at least once +[tr] +* STATUS_FAILED +* 4 +* Partition has failed to boot +[tr] +* STATUS_BAD_SIG +* 5 +* Signature verification on metainfo failed +[tr] +* STATUS_BAD_META +* 6 +* Parsing metainfo field failed + +==== flags + +[table] +[tr] +- flag +- value +- description +[tr] +* FLAG_PREFERRED_BOOT +* 0x01 +* Override boot choice to boot from this partition +[tr] +* FLAG_HASH_TREE +* 0x02 +* Image contains an appended dm-verity hash tree +[tr] +* FLAG_DATA_COMPRESSED +* 0x04 +* Image is compressed with xz + +==== metainfo-len + +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 + +==== metainfo + +==== signature + +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` + + +=== Booting + +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. diff --git a/docs/realms.md b/docs/realms.md deleted file mode 100644 index f1757bd..0000000 --- a/docs/realms.md +++ /dev/null @@ -1,247 +0,0 @@ -Citadel Realms --------------- - -Citadel contains only the base operating system and the Gnome desktop, it does not -include any applications. To be able to install and run applications Citadel can -create spaces which are called Realms. - -A Realm is a container similar to a Docker or LXC container in which any Linux -distribution could be installed. We use a Debian based image but it would not be -difficult to create an image for another Linux distribution. - -The realm containers are launched with systemd-nspawn but this is a detail of -how they are implemented and not something it is necessary to learn about in order to use them. - -Citadel provides a command-line tool `realms` for creating, managing, and launching Realm instances. - -### The `default` realm - -One realm is always selected to be the `default` realm. This realm -starts automatically when the system boots. The `realms` utility can be used -to change which realm is the default realm. Switching the default realm changes -the symlink `/realm/default.realm` to point to a different realm instance directory. - - citadel:~# realms default - Default Realm: main - - citadel:~# realms default project - [+] default realm changed from 'main' to 'project' - - citadel:~# realms default - Default Realm: project - -### The `current` realm - -Multiple realms may be launched at once but the Gnome Desktop is only associated with -one of the running realms. This realm is called the `current` realm. - -When displaying applications available to launch from the desktop, Gnome will only -be aware of applications that are installed in the realm which is set as `current` -and any application launched from the desktop will run inside this current realm. - -Setting another realm as current does not affect any applications that are already running. -Changing the current realm only means that any further applications which are launched -will now run in the newly chosen realm. - -Changing or querying the current realm is done with the `realms current` command, and -if you choose a realm which is not currently running it will be automatically started. - - citadel:~# realms current - Current Realm: main - - citadel:~ # realms current project - [+]: Started realm 'project' - [+]: Realm 'project' set as current realm - - citadel:~ # realms current - Current Realm: project - -Underneath the hood, this command just changes the symlink `/run/realms/current.realm` to -point to a new realm. This directory is monitored for changes with `inotify` and when -the symlink changes a new set of `.desktop` files is swapped into a temporary directory -where Gnome will look for metadata about which applications are installed. - -### Creating a new realm - -New realms are created with the command `realms new ` - -When a new realm is created a btrfs snapshot of some application image is created at -`/realms/realm-$name/rootfs`. By default it is the base image (`base.appimg`) which -is cloned as a snapshot. Application images are described in detail in a later section. - - citadel:~ # realms new project - [+]: Populating realm home directory with files from /realms/skel - Create a snapshot of '/storage/appimg/base.appimg' in '/realms/realm-project/rootfs' - -A new empty home directory is also created for the realm instance. Any file which are placed -into the `/realm/skel` directory will be copied into any newly created realm home directory. - - -### Realms configuration file - -All of the curretly supported configuration options are listed below with their default values assigned. - - use-shared-dir = true - use-sound = true - use-x11 = true - use-wayland = true - use-gpu = false - use-kvm = false - use-network = true - network-zone = "clear" - -If you wish to change any of these options to something other than what is listed above add the -corresponding line to the file `/realms/realm-$name/config` - - citadel:~ # echo "use-gpu = true" > /realms/realm-main/config - -#### Option `use-shared-dir` - -Set to `false` to disable mounting the shared directory `/realms/Shared` into this realm at -`/home/user/Shared`. - -#### Option `use-sound` - -Set to `false` to prevent mounting pulse audio socket and sound device into this realm. - -#### Option `use-x11` - -Set to `false` to prevent mounting `/tmp/.X11-unix` into the realm. This is the socket for communicating -with the `XWayland` X11 compatibility daemon. - -#### Option `use-wayland` - -Set to `false` to prevent mounting the wayland display server socket `/run/user/1000/wayland-0` - into the realm. - -#### Option `use-gpu` - -Set to `true` to mount the device `/dev/dri/renderD128` into the realm. Adding this -device will make hardware graphics acceleration available to applications running -in the realm. - -#### Option `use-kvm` - -Set to `true` to mount the device `/dev/kvm` into the realm. This will make it -possible to run Qemu and other KVM based tools with hardware virtualization -inside the realm. - -#### Option `use-network` - -Set to `false` to disable configuring the realm with access to the internet. The -realm instance will only have a localhost network interface. - -#### Option `network-zone` - -Setting a name here will create bridge device in citadel with the name vz-$name if -it doesn't already exist and attach this realm instance to that bridge. - -### Realms base directory layout - -The realms base directory is stored on the storage partition at `/storage/realms` and is bind mounted to `/realms` on the root filesystem for convenience. - - /realms - config - /Shared - /skel - /default.realm -> realm-main - /realm-main - /realm-project - /realm-testing - -#### File `/realms/config` - -This file is a template of the configuration file for individual realms. When a new -realm is created this file in copied into the new realm instance directory. By -modifying this file, the default configuration for new realm instances can be changed. - -#### Directory `/realms/Shared` - -This directory is bind mounted to `/home/user/Shared` of each running realm that has -the option `use-shared-dir` enabled. It's a convenient way to move files between -different realms and between citadel and realms. - -#### Directory `/realms/skel` - -Files which are added to this directory will be copied into the home directory of -any newly created realm. The directory is copied as a tree of files and may contain -subdirectories. - -#### Symlink `/realms/default.realm` - -A symlink which points to a realm instance directory of the default realm. The -default realm is the realm which starts when the system is booted. - -#### Directory `/realms/realm-$name` - -This is a realm instance directory, for a realm with $name as the realm name. - - /realm-main - config - /home - /rootfs - - ##### `config` - -Configuration file for the realm instance copied from `/realms/config` or -created by the user. - -##### `/home` - -Home directory for this realm. It will be mounted to `/home/user` in -the realm instance. - -##### `/rootfs` - -The root filesystem of this realm. It is cloned from (a btrfs subvolume snapshot of) -some application image. - -### Application Images - -(Not to be confused with the [AppImage](https://appimage.org) packaging system) - -The root filesystem for realms are called Application Images but we often use -the shorter name *appimg*. - -We have created [a framework](https://github.com/subgraph/citadel/tree/master/appimg-builder) -for building a Debian based images and we use this to build the default appimg that we ship. - -We also encourage users to experiment with building their own custom images. - - -**Tree Application Images** are the only type of application image which are currently implemented for realms. - -The rootfs is a tree of files on the filesystem, and it is also a btrfs subvolume -which is cloned at zero cost (internally with `btrfs subvolume snapshot`) to use -as the root filesystem of newly created realms. - - -#### Block Application Images (and also Sealed Application Images) - -In the future we will add another type of application image called a **Block -Application Image**. This type of image will be stored as a disk volume image file -and will be mounted with a loop device rather than existing as a tree of files on the -filesystem. - -This will make it possible to enforce [dm-verity](https://www.kernel.org/doc/Documentation/device-mapper/verity.txt) -verification over the image and ensure that no malicous or unintended modifications -can be made to any of the the files on the root filesystem. Signature verification -over the dm-verity root hash is done from the citadel rootfs image which is also -secured with dm-verity. When enforcement of boot integrity is also implemented this -will create a chain of cryptographic assurances that no component of the system has -been tampered with. - -Block images with signatures and dm-verify verification enabled are called **Sealed Application Images** - -### Updating an Application Image - -To modify or update an application image run the `realms update-appimg` command. -A container will be created for updating the image and a root shell session will -open. From this session regular package management commands can be run. Any changes -made will only affect future realms created from this appimg. - - citadel:~ # realms update-appimg - [+]: Entering root shell on base appimg - root@base-appimg-update:/# apt update - - [...] diff --git a/meta-citadel/recipes-citadel/citadel-documentation/citadel-documentation.bb b/meta-citadel/recipes-citadel/citadel-documentation/citadel-documentation.bb new file mode 100644 index 0000000..426b614 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/citadel-documentation.bb @@ -0,0 +1,20 @@ +DESCRIPTION = "Citadel Yelp Documentation" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" + +inherit allarch + +SRC_URI = "\ + file://pages \ + file://citadel-documentation.desktop \ +" + +do_install() { + install -m 0755 -d ${D}${datadir}/citadel-documentation + install -m 0755 -d ${D}${datadir}/applications + + install -m 0644 ${WORKDIR}/pages/*.page ${D}${datadir}/citadel-documentation + install -m 0644 ${WORKDIR}/citadel-documentation.desktop ${D}${datadir}/applications +} + +FILES_${PN} = "/" diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/citadel-documentation.desktop b/meta-citadel/recipes-citadel/citadel-documentation/files/citadel-documentation.desktop new file mode 100644 index 0000000..b4b94a4 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/citadel-documentation.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=Citadel Documentation +Keywords=documentation;information;manual;help; +Categories=Core;Documentation; +Icon=help-browser +Exec=/usr/libexec/citadel-run yelp /opt/share/citadel-documentation +Type=Application +Terminal=false +StartupNotify=true diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/boot.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/boot.page new file mode 100644 index 0000000..1704856 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/boot.page @@ -0,0 +1,66 @@ + + + + + + Booting Citadel +
+ Disk Layout +

When Citadel is installed two disk partitions are created on the target disk.

+ sda 8:0 0 477G 0 disk +├─sda1 8:1 0 511M 0 part +└─sda2 8:2 0 476.5G 0 part +

The first partition is an EFI boot partition and the second partition is LUKS encrypted + and contains multiple LVM volumes when decrypted.

+ + /dev/sda1 /dev/sda2 + + [EFI ESP Boot partition] [ LUKS encrypted partition filling remainder of disk ] + . . + . . + . | . + . | . + . V . + . . + [ rootfsA ] [ rootfsB ] [ citadel-storage ] + +

There are three logical volumes. Two root filesystem partitions so that one partition + can be updated while the other one is in use, and the remaining space is contained + in a volume called 'storage'.

+ # lvs +LV VG Attr LSize +rootfsA citadel -wi-a----- 2.00g +rootfsB citadel -wi-ao---- 2.00g +storage citadel -wi-ao---- 472.43g +
+ Bootloader +
+ LUKS +

The kernel initramfs has an /etc/crypttab file which guides the discovery of the LUKS partition. + The UUID of the LUKS partition is hardcoded to the value listed below. If citadel is installed + on more than one device on the system, the intended LUKS partition may not be chosen correctly. + This problem can be addressed by changing the UUID of other citadel LUKS partitions and passing + the UUID on kernel commandline to override /etc/crypttab. See systemd-cryptsetup-generator(8).

+ # cat /etc/crypttab +luks UUID=683a17fc-4457-42cc-a946-cde67195a101 - discard +
+
+ Mounting rootfs +

The initramfs boot stage is orchestrated by various systemd unit files which can be found + in the citadel source tree at:

+ citadel/meta-citadel/recipes-initrd/citadel-initramfs +

The same kernel and initramfs is used for the installer image. One task of these unit files + is to set up a live mode boot when a certain kernel command line option is set. For a regular + boot, a pair of unit files will attempt to mount the root filesystem partition when it becomes + available:

+ citadel-rootfs-mount.path +citadel-rootfs-mount.service +

The .path unit triggers every time /dev/mapper changes and the corresponding .service unit is + activated only when all of the LVM volumes inside

+ ConditionPathExists=/dev/mapper/citadel-rootfsA +ConditionPathExists=/dev/mapper/citadel-rootfsB +ConditionPathExists=/dev/mapper/citadel-storage +
+
+
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/citadel.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/citadel.page new file mode 100644 index 0000000..3a0ff49 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/citadel.page @@ -0,0 +1,57 @@ + + + + + Introduction to Subgraph Citadel + + Subgraph Citadel +
+ What is Citadel? +

Citadel is the base operating system of the new version of Subgraph OS.

+

Citadel runs the GNOME desktop session and a few basic system services and + nothing else. It is built and distributed as a single static disk image + rather than as a collection of software packages like a traditional Linux + distribution such as Ubuntu or Fedora. Citadel disk images are built entirely + from the source code of the individual software components. This gives us + complete control over what is included and how each component is configured.

+ +

Citadel is a modern desktop operating system based on the GNOME desktop, but if you + prefer we also include an tiling window manager called Sway as an alternative.

+
+

Since the Citadel root filesystem is immutable it is not possible to install + applications such as a web browser or text editor directly into Citadel. + Instead applications are run in a separate isolated environment called a Realm.

+

When Citadel is first installed a single primary Realm is created and while running + a single realm the system resembles and behaves similar to any other desktop Linux + system. The separation between Citadel and the realm in which user applications are + launched is mostly transparent to the user. However, a user may create as many new + realms as they like and each new realm behaves like a freshly installed Debian Linux + environment where the user may install packages and store files.

+

Realms are implemented in Subgraph OS as either containers or as virtual machines + running in a custom KVM hypervisor. Both approaches have advantages so the user is + free to choose either option for each realm they create.

+ +

Hypervisor isolation is stronger and more secure, but container isolation uses + less system resources and makes it possible to access hardware devices and other + system features directly. A Citadel user can decide which configuration makes + more sense for each Realm they create.

+
+
+ Stateless Foundation +

In the architecture of Citadel the building blocks of the system are + immutable filesystem images rather than packages. These images are mounted + read-only and this property is enforced with a Linux kernel feature (dm-verity) + which efficiently guarantees each block loaded from disk has a valid + cryptographic checksum. This means that Citadel always loads exactly the + operating system software prepared by Subgraph and rebooting the system will + always brings the computer into a known consistent state.

+

When Citadel is updated an entirely new image is loaded rather than applying + a set of changes on top of an existing filesystem. By atomically updating the + entire system from one version to the next there is only ever a single software + configuration to consider and the system can never end up in an inconsistent state. + System upgrades cannot break your computer in mysterious ways and even if an + upgrade fails to boot for some reason, the system simply reverts to the + previously working version.

+
+
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/developer.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/developer.page new file mode 100644 index 0000000..781890d --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/developer.page @@ -0,0 +1,24 @@ + + + + + + Developer Guide +
+ Make Root Filesystem Writable +

Sometimes it can be useful to make changes directly to the citadel root filesystem to + experiment with changes or to debug a problem.

+

First citadel.noverity must be added to the kernel commandline. After booting with + this command line option verify that dm-verity has been disabled with the dmsetup + command.

+ # dmsetup status rootfs +0 4194304 linear +

If the output displays verity instead of linear then dm-verity is enabled + and the disk cannot be safely written to.

+

Next remount the root filesystem with read-write flag.

+ # mount -oremount,rw,noatime / +
+
+ Debugging GNOME startup +
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/disk-layout.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/disk-layout.page new file mode 100644 index 0000000..ad84787 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/disk-layout.page @@ -0,0 +1,184 @@ + + + + + A Hands-on guide the Citadel Disk and Filesystem Layout + + Disk Layout +
+ Partitions +

During installation, two partitions are created on the disk chosen as + the target of the install.

+

For example, if the installation disk is /dev/sda:

+ + + <code>/dev/sda1</code> +

512MB EFI System Partition

+
+ + <code>/dev/sda2</code> +

Remainder of the disk

+
+
+

The partition layout of a running system can be viewed by running the lsblk command.

+ citadel:~ # lsblk /dev/sda +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +sda 8:0 0 477G 0 disk +├─sda1 8:1 0 511M 0 part +└─sda2 8:2 0 476.5G 0 part + └─luks 252:0 0 476.4G 0 crypt + ├─citadel-rootfsA 252:1 0 2G 0 lvm + │ └─rootfs 252:4 0 354M 1 crypt / + ├─citadel-rootfsB 252:2 0 2G 0 lvm + └─citadel-storage 252:3 0 472.4G 0 lvm /storage +

Several further block devices are created during boot when the main disk partition + is decrypted.

+ sda +├─sda1 (a) /boot partition +└─sda2 (b) LUKS encrypted partition + └─citadel (c) LVM volume group + ├─citadel-rootfsA (d1) rootfs partition A (Read Only) + │ └─rootfs (e) The dm-verity device created for rootfsA + ├─citadel-rootfsB (d2) rootfs partition B (Read Only) + └─citadel-storage (f) mounted as /storage (Read/Write) + + + <code>(a) /boot partition</code> +

EFI boot partition

+
+ + <code>(b) LUKS encrypted partition</code> +

Remainder of disk is an encrypted volume

+
+ + <code>(c) LVM volume group</code> +

Main partition contains several LVM volumes

+
+ + <code>(d) citadel-rootfs(A/B)</code> +

Two root partitions so one can be updated while other is in use.

+
+ + <code>(e) /dev/mapper/rootfs</code> +

verity mapper device for mounted root partion

+
+ + <code>(f) /dev/mapper/citadel-storage</code> +

The writable filesystem

+
+
+
+
+ Citadel Filesystem Layout + / +├─ /run/citadel/images/ +│ │ +│ ├─ modules.mountpoint/ (modules image mounted here) +│ └─ extra.mountpoint/ (extra image mounted here) +│ +└─ /storage + │ + ├─ resources/dev (resource images for channel 'dev') + │ │ + │ ├─base-realmfs.img + │ └─main-realmfs.img + │ + ├─ /realms (/realms is a bind mount of /storage/realms) + │ ├─skel/ + │ └─config + │ + ├─ /realms/realmfs-images + │ │ + │ ├─citadel-kernel-5.7-dev-001.img + │ └─citadel-extra-dev-001.img + │ + └─ /realms/realm-main + ├─ home + └─ config +
+
+ Resource Image Mounts +

Resource images are mounted into the system by creating loop devices. These devices can be + viewed by running the 'losetup' command inside Citadel.

+ citadel:~ # losetup -ONAME,OFFSET,RO,BACK-FILE +NAME OFFSET RO BACK-FILE +/dev/loop1 4096 1 /storage/resources/dev/citadel-extra-dev-001.img +/dev/loop2 4096 1 /storage/realms/realmfs-images/main-realmfs.img +/dev/loop0 4096 1 /storage/resources/dev/citadel-kernel-5.0.6-dev-000.img +

Resource image files are protected against accidental changes or malicious tampering by + using dm-verity so that the kernel verifies a cryptographic checksum of each block loaded + from the image.

+

You can view the verity device mapper node associated with each loop device with + the lsblk command.

+ citadel:~ # lsblk /dev/loop0 /dev/loop1 /dev/loop4 +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +loop0 7:0 0 116.9M 1 loop +└─verity-kernel 252:5 0 116M 1 crypt /run/citadel/images/kernel.mountpoint +loop1 7:1 0 938.9M 1 loop +└─verity-extra 252:6 0 931.5M 1 crypt /run/citadel/images/extra.mountpoint +loop2 7:2 0 4G 1 loop +└─verity-realmfs-main-11922f31 252:9 0 4G 1 crypt /run/citadel/realmfs/realmfs-main-11922f31.mountpoint +

Parameters of each dm-verity instance can be viewed with the veritysetup command.

+ citadel:~ # veritysetup status verity-kernel +/dev/mapper/verity-kernel is active and is in use. + type: VERITY + status: verified + hash type: 1 + data block: 4096 + hash block: 4096 + hash name: sha256 + salt: fa430cb7887de60dca6fd1974868036ea39cf5017eb55f02e3a76f82a12a0431 + data device: /dev/loop0 + data loop: /storage/resources/dev/citadel-kernel-5.0.6-dev-000.img + size: 237536 sectors + mode: readonly + hash device: /dev/loop0 + hash loop: /storage/resources/dev/citadel-kernel-5.0.6-dev-000.img + hash offset: 237544 sectors +

When a resource image file is mounted, a file in the root directory called 'manifest' lists + bind mounts to perform to integrate the image into the Citadel root filesystem.

+

Each line of this file is a directory to bind mount from the mounted image to the root + filesystem. If a directory should be mounted to a location which is different than + the source directory the source and target directories are both listed on a single + line and separated by the ':' character. In the 'extra' image below, the directory + /usr/share from the resource image is mounted to /opt/share on the Citadel filesystem.

+ citadel:~ # cat /run/citadel/images/kernel.mountpoint/manifest +/usr/lib/modules + +citadel:~ # cat /run/citadel/images/extra.mountpoint/manifest +/usr/lib/firmware +/usr/share:/opt/share +

The citadel-image utility can be used to view the metainfo variables stored in the header + section of a resource image file.

+ citadel:~ # citadel-image metainfo /storage/resources/dev/citadel-extra-dev-001.img +image-type = "extra" +channel = "dev" +version = 1 +timestamp = "20190331172025" +nblocks = 195924 +shasum = "04e6f58afa6f608aff2d6cbb47cbe704f8ab0995f4dfe8e1c03655dc9bb6635a" +verity-salt = "7bf3eec3c51ffd2e82329a9fc6fe42915743874d7c5af43589e589c037ae81e5" +verity-root = "b94eb3431c4fb95e5b9bd62b4505d089414ae660d75eee0fce54b8483d3f9571" + +citadel:~ # citadel-image metainfo /storage/resources/dev/citadel-kernel-5.0.6-dev-000.img +image-type = "kernel" +kernel-version = "5.0.6" +kernel-id = "36b7a960dcd51d1649f83a7361f9eb5c2af5741ce6cc53689b411347aa1298b6" +channel = "dev" +version = 1 +timestamp = "20190407002748" +nblocks = 29692 +shasum = "c988bd7d468c409eb6cd3af8fa9e17b0a75a72d6ad765ad1749d15628a9096be" +verity-salt = "fa430cb7887de60dca6fd1974868036ea39cf5017eb55f02e3a76f82a12a0431" +verity-root = "f4c4fbaebb59d348bd44cfb1cdef54a813728aabc5acc439c2e739b63c1b8370" +

RealmFS images also have a resource image header with a slightly different set of + metainfo variables.

+ citadel:~ # citadel-image metainfo /storage/realms/realmfs-images/main-realmfs.img +image-type = "realmfs" +realmfs-name = "main" +nblocks = 1048575 +channel = "realmfs-user" +verity-salt = "ad254e6dd385c0392ed8a6a41b849cfd4ef98ec3643e186feb011d5aa4f1d194" +verity-root = "11922f311b5a9141d65b7ef82e1c9159d75e413d1b420a7e3302ec8ec0ad8593" +
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/index.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/index.page new file mode 100644 index 0000000..e401c4c --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/index.page @@ -0,0 +1,10 @@ + + + Citadel Help +
+ User Guide +
+
+ Citadel Internals +
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/kernel-cmdline.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/kernel-cmdline.page new file mode 100644 index 0000000..842a966 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/kernel-cmdline.page @@ -0,0 +1,33 @@ + + + + + + Kernel Command Line Options + + +

citadel.noverity

+
+ +

citadel.nosignatures

+
+ +

citadel.install

+
+ +

citadel.overlay

+
+ +

citadel.channel

+
+ +

citadel.verbose

+
+ +

citadel.debug

+
+ +

citadel.sway

+
+
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realm-config.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realm-config.page new file mode 100644 index 0000000..90c4ce9 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realm-config.page @@ -0,0 +1,123 @@ + + + + + Realm configuration file reference + + Configuring Realms +

Realms are usually configured with the tools for managing realms, but the configuration + is stored in a TOML file in the realm directory and can also be edited by hand.

+
+ Options + + + <code>use-wayland</code> +

If 'true' access to Wayland display will be permitted in realm by + adding wayland socket /run/user/1000/wayland-0

+
+ + <code>use-x11</code> +

If 'true' access to X11 server will be added to realm by bind mounting directory + /tmp/.X11-unix

+
+ + <code>use-sound</code> +

If 'true' allows the use of sound inside realm. The following items will + be added to realm:

+ + +

/dev/snd

+
+ +

/dev/shm

+
+ +

/run/user/1000/pulse

+
+
+
+ + <code>use-kvm</code> +

If enabled, /dev/kvm will be added to the realm. + This option is only available for nspawn realms.

+
+ + <code>use-shared-dir</code> +

If enabled the directory /realms/Shared will be bind mounted into the home directory of the realm. + This directory is shared between all running realms that have this option enabled as a + convenient way to move files between realms.

+
+ + <code>use-ephemeral-home</code> +

If 'true' the home directory of this realm will be set up in ephemeral mode. + The ephemeral home directory is set up with the following steps

+ + +

Home directory is mounted as tmpfs

+
+ +

Any files in /realms/skel are copied into home directory

+
+ +

Any files in /realms/realm-${name}/skel are copied into home directory

+
+ +

Any directories listed in ephemeral-persistent-dirs are bind mounted + from /realms/realm-${name}/home into ephemeral home directory.

+
+
+
+ + <code>ephemeral-persistent-dirs</code> default: ["Documents"] +

A list of subdirectories of /realms/realm-${name}/home to bind mount into realm + home directory when ephemeral-home is enabled.

+
+ + <code>use-network</code> +

network

+
+ + <code>network-zone</code> default: "clear" +

network zone

+
+ + <code>use-gpu</code> +

Enables hardware graphics acceleration in relam. + if 'true' render node device /dev/dri/renderD128 will be added to realm.

+
+ + <code>use-gpu-card0</code> +

If 'true' and use-gpu is also enabled, privileged device /dev/dri/card0 + will be added to realm.

+
+ + <code>realmfs</code> default: "base" +

name of realmfs image

+
+ + <code>overlay</code> default: "storage" +

type of overlay to use

+
+ + <code>terminal-scheme</code> +

terminal color scheme

+
+ + <code>extra-bindmounts</code> +

bind mounts

+
+ + <code>extra-bindmounts-ro</code> +

read-only bind mounts

+
+ + <code>system-realm</code> default: false +

system realm

+
+ + <code>autostart</code> default: false +

autostart realm

+
+
+
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realmfs.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realmfs.page new file mode 100644 index 0000000..e7ade69 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realmfs.page @@ -0,0 +1,140 @@ + + + + + Realm root filesystem images + + RealmFS Images +

A RealmFS image contains a root filesystem for one or more realm instances. + Similar to resource images, RealmFS images are signed and mounted with dm-verity + to prevent tampering with the data on the root filesystem such as the + installation of malware or backdoors. The keys used to sign RealmFS images are + controlled by the user which makes it possible to upgrade software and install + new packages on the image.

+

RealmFS images are always mounted as read-only and this property is enforced + with dm-verity. Since RealmFS images are immutable a single image can be shared + between multiple running realm instances. By default, when a realm is launched a + temporary overlay is added to the root filesystem so that changes can be + performed that will last only until the realm is stopped or restarted. This + allows experimenting with the system configuration or installing new software + temporarily. The root filesystem can then be reverted to the original state by + simply restarting the realm.

+
+ Updates +

Since the root filesystem of realms are stored on read-only disk images, + packages cannot be permanently installed or upgraded in the usual way. Changes + to the root filesystem will succeed inside a realm, but these changes will be + lost as soon as the realm is stopped or restarted.

+

To make persistent changes to a RealmFS image, the image is first copied, then + changes are applied to the copy. After applying changes a new dm-verity hash + tree is generated for the image and the RealmFS image header is updated and + signed.

+ +

The process of generating a signature and a dm-verity hash tree for a RealmFS image + after applying some changes such as updating packages is called Sealing + the image.

+
+
+ Apt-Cacher NG Realm +

Upon booting a system utility realm is started which runs an Apt-Cacher NG + instance. Each realm is configured to use this realm as a proxy for package + installation.

+ /etc/apt/apt.conf.d/000apt-cacher-ng-proxy + +Acquire::http::Proxy "http://172.17.0.213:3142/"; +

The apt source lines use the special Apt-Cacher NG syntax.

+ /etc/apt/sources.list + +deb http://HTTPS///deb.debian.org/debian buster main contrib non-free +

Using a package cache avoids downloading and storing packages multiple times + when updating multiple RealmFS images. It also makes it possible to download and + cache packages while connected to a network before booting the system into a + safe mode without enabling the network to perform upgrades of realm packages.

+
+
+ Updates (Container method) +

First the RealmFS image is copied to a temporary file. On a filesystem such as + btrfs, the image file will be cloned as a reflink rather than copying the file. + The copy of the RealmFS will then be mounted as writable so that changes can be + made. A systemd-nspawn container is launched and a root shell opened so that the + user can update packages, install new software, or perform any other + modifications to the root filesystem.

+

Once the shell is exited a prompt asks the user if they would like to save the + current changes or discard them. If the user chooses to save the changes, the + copied image is then sealed by generating a dm-verity hash tree and the header + of the image is signed with the user RealmFS sealing key.

+
+
+ Updates with pH Hypervisor +

When a realm is launched with pH, the overlay is managed by the emulated disk + device of the hypervisor which tracks changes to blocks of the disk and stores + the changed blocks in memory. Since the hypervisor is tracking all of the + changes to the disk, it can also transparently apply the changes and generate a + new sealed RealmFS image and then discard the changed blocks and start directly + using the new image.

+

This process is initiated by the user when they decide they would like to commit + any changes they have made to the root filesystem in the running realm + permanently to the underlying RealmFS image.

+ + +

The user makes changes to the root filesystem of the realm and pH tracks the blocks that have changed.

+
+ +

A user request is made to pH to apply the changes to the RealmFS image.

+
+ +

pH opens a prompt on the desktop to ask the user to confirm that they really did make this request.

+
+ +

A copy (or reflink) of the current RealmFS is made, and pH applies the changed blocks to this copy.

+
+ +

The copy is then sealed with the RealmFS key of the user.

+
+ +

Now pH can quietly swap in the new version of the RealmFS image and discard all of the tracked block changes.

+
+
+
+
+
+ Signing RealmFS Images +

A secret key for signing RealmFS images is generated during installation and + stored on disk in an encrypted file called the User Keyring. During boot + when the user enters a passphrase to decrypt the disk, this passphrase is also + used to decrypt the keyring file and the public and secret key pair is + loaded into the kernel key storage.

+

The risk exists that an attacker who is able to compromise the kernel may + recover this secret key. This would allow the attacker to modify sealed RealmFS + images and install backdoors or other malware into realm root filesystems. Even + without obtaining the signing key an attacker who has compromised Citadel could + wait for the user to perform an update and make malicious changes at the same + time which the user will then sign.

+

For these reasons, it is also possible to configure the system so that only + the public key is retained in the kernel upon boot and the user must boot + into a special mode so that the private key is available to perform updates.

+
+ Safe Mode +

If upgrades are performed in normal operating mode, an attacker who has + compromised citadel can persistently backdoor the upgraded realmfs images. + Safe mode is a way to boot citadel without starting any realms or enabling the + network device. Since the integrity of the Citadel root filesystem is enforced + by dm-verity and no realms are running, even if the system had become compromised + at some point in the past it is assumed to now be in a safe state for performing + updates and signing them with the user sealing keys.

+

Since the network is not available in safe mode, the packages to be installed or + upgraded must be stored somewhere. By either performing the packge updates with + the --download-only flag or installing them to the temporary overlay of a realm + the user will cause them to be stored on the Apt-Cache NG service realm so that + they are available for install in safe mode.

+
+
+
+ Base RealmFS image +

Citadel ships with a RealmFS image called base-realmfs.img. There is nothing + special about this image other than that it is initially signed by Subgraph until + the user modifies or updates it. During installation, a copy of this RealmFS is + created with the name main-realmfs.img and sealed with the newly generated + user keys.

+
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realms.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realms.page new file mode 100644 index 0000000..78ad0ea --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/realms.page @@ -0,0 +1,51 @@ + + + + + + Citadel Realms +

Citadel contains only the base operating system and the Gnome desktop, it does not + include any applications. To be able to install and run applications Citadel can + create spaces which are called Realms.

+

A Realm is a container similar to a Docker or LXC container in which any Linux + distribution could be installed. We use a Debian based image but it would not be + difficult to create an image for another Linux distribution.

+

The realm containers are launched with systemd-nspawn but this is a detail of + how they are implemented and not something it is necessary to learn about in + order to use them.

+
+ The <em>current</em> realm +

Multiple realms may be launched at once but the Gnome Desktop is only associated with + one of the running realms. This realm is called the `current` realm.

+

When displaying applications available to launch from the desktop, Gnome will only + be aware of applications that are installed in the realm which is set as `current` + and any application launched from the desktop will run inside this current realm.

+

Setting another realm as current does not affect any applications that are already running. + Changing the current realm only means that any further applications which are launched + will now run in the newly chosen realm.

+
+
+ Realm directory layout +

The realms base directory is stored on the storage partition at `/storage/realms` and is bind mounted to `/realms` on the root filesystem for convenience.

+ /realms + config + /Shared + /skel + /default.realm -> realm-main + /realm-main + /realm-project + /realm-testing +
+ /realms/config +

This is the global realm configuration file. Options set in this file apply to every realm + unless the same option has been overridden with a different value in the config file for + a realm.

+
+
+ /realms/Shared +

This directory is bind mounted to `/home/user/Shared` of each running realm that has + the option `use-shared-dir` enabled. It's a convenient way to move files between + different realms and between citadel and realms.

+
+
+
diff --git a/meta-citadel/recipes-citadel/citadel-documentation/files/pages/resource-image.page b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/resource-image.page new file mode 100644 index 0000000..00add78 --- /dev/null +++ b/meta-citadel/recipes-citadel/citadel-documentation/files/pages/resource-image.page @@ -0,0 +1,361 @@ + + + + + + Citadel Resource Images +

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.

+
+ Resource Image Types +

Currently the following image types are defined for use in Citadel:

+
+ 1) Base Root Filesystem ("rootfs") +

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.

+
+
+ 2) Kernel Image ("kernel") +

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.

+
+
+ 3) Extra Resource Image ("extra") +

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.

+
+
+ 4) Realm Filesystem Image ("realmfs") +

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.

+
+
+ Image Layout +

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 ] +
+
+
+ Image 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

+
+
+ Header Fields +
+ MAGIC +

The initial 4 bytes are always set to the ascii bytes 'SGOS' so that a + valid image file can easily be identified.

+
+
+ status +

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

+
+
+
+ flags + + + + + + + + + + + + + + + + + + + + + +
+

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

+
+
+
+ metainfo-len +

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

+
+
+ metainfo +
+
+ signature +

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`

+
+
+
+ Booting +

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.

+
+
+
diff --git a/meta-citadel/recipes-citadel/images/citadel-extra-image.bb b/meta-citadel/recipes-citadel/images/citadel-extra-image.bb index fa028de..f3d714d 100644 --- a/meta-citadel/recipes-citadel/images/citadel-extra-image.bb +++ b/meta-citadel/recipes-citadel/images/citadel-extra-image.bb @@ -14,6 +14,7 @@ PACKAGE_INSTALL = "\ adwaita-icon-theme-symbolic \ adwaita-icon-theme-symbolic-hires \ " + CITADEL_IMAGE_VERSION = "${CITADEL_IMAGE_VERSION_extra}" CITADEL_IMAGE_TYPE = "extra" @@ -23,8 +24,11 @@ inherit citadel-image ROOTFS_POSTPROCESS_COMMAND += "write_manifest_file; " write_manifest_file() { + install -m 0755 -d ${IMAGE_ROOTFS}/usr/share/citadel-documentation + cat > ${IMAGE_ROOTFS}/manifest << EOF /usr/lib/firmware /usr/share:/opt/share +/sysroot/usr/share/citadel-documentation:/opt/share/citadel-documentation EOF } diff --git a/meta-citadel/recipes-citadel/packagegroups/packagegroup-citadel.bb b/meta-citadel/recipes-citadel/packagegroups/packagegroup-citadel.bb index 34c8cca..c17041d 100644 --- a/meta-citadel/recipes-citadel/packagegroups/packagegroup-citadel.bb +++ b/meta-citadel/recipes-citadel/packagegroups/packagegroup-citadel.bb @@ -10,4 +10,5 @@ RDEPENDS_${PN} = "\ citadel-tools \ citadel-tools-realms \ citadel-tools-boot \ + citadel-documentation \ " diff --git a/realmfs-builder/basic-image.conf b/realmfs-builder/basic-image.conf index 0307459..d14ec8c 100644 --- a/realmfs-builder/basic-image.conf +++ b/realmfs-builder/basic-image.conf @@ -1 +1 @@ -PACKAGES="man manpages vim-nox iputils-ping tmux vifm gnome-terminal firefox nautilus eog evince unzip x264" +PACKAGES="man manpages vim-nox iputils-ping tmux vifm gnome-terminal firefox nautilus eog evince unzip x264 yelp"