forked from brl/citadel
Citadel documentation system
This commit is contained in:
parent
5b8c330cb7
commit
7faf0ce39e
1
docs/.gitignore
vendored
Normal file
1
docs/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
pages/
|
25
docs/Makefile
Normal file
25
docs/Makefile
Normal file
@ -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 $@ $<
|
26
docs/README.md
Normal file
26
docs/README.md
Normal file
@ -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
|
||||||
|
|
84
docs/duck/boot.duck
Normal file
84
docs/duck/boot.duck
Normal file
@ -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
|
||||||
|
|
||||||
|
|
61
docs/duck/citadel.duck
Normal file
61
docs/duck/citadel.duck
Normal file
@ -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.
|
||||||
|
|
26
docs/duck/developer.duck
Normal file
26
docs/duck/developer.duck
Normal file
@ -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
|
198
docs/duck/disk-layout.duck
Normal file
198
docs/duck/disk-layout.duck
Normal file
@ -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"
|
||||||
|
|
||||||
|
|
11
docs/duck/index.duck
Normal file
11
docs/duck/index.duck
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
= Citadel Help
|
||||||
|
[guide]
|
||||||
|
|
||||||
|
== User Guide
|
||||||
|
[#user .2column]
|
||||||
|
|
||||||
|
== Citadel Internals
|
||||||
|
[#internals .2column]
|
||||||
|
|
13
docs/duck/kernel-cmdline.duck
Normal file
13
docs/duck/kernel-cmdline.duck
Normal file
@ -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
|
||||||
|
|
88
docs/duck/realm-config.duck
Normal file
88
docs/duck/realm-config.duck
Normal file
@ -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
|
||||||
|
|
||||||
|
|
139
docs/duck/realmfs.duck
Normal file
139
docs/duck/realmfs.duck
Normal file
@ -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.
|
56
docs/duck/realms.duck
Normal file
56
docs/duck/realms.duck
Normal file
@ -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.
|
||||||
|
|
242
docs/duck/resource-image.duck
Normal file
242
docs/duck/resource-image.duck
Normal file
@ -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.
|
247
docs/realms.md
247
docs/realms.md
@ -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 <realm name>`
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
[...]
|
|
@ -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} = "/"
|
@ -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
|
@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="boot">
|
||||||
|
<info>
|
||||||
|
<link type="guide" xref="index#internals"/>
|
||||||
|
</info>
|
||||||
|
<title>Booting Citadel </title>
|
||||||
|
<section>
|
||||||
|
<title>Disk Layout</title>
|
||||||
|
<p>When Citadel is installed two disk partitions are created on the target disk.</p>
|
||||||
|
<screen>sda 8:0 0 477G 0 disk
|
||||||
|
├─sda1 8:1 0 511M 0 part
|
||||||
|
└─sda2 8:2 0 476.5G 0 part</screen>
|
||||||
|
<p>The first partition is an EFI boot partition and the second partition is LUKS encrypted
|
||||||
|
and contains multiple LVM volumes when decrypted.</p>
|
||||||
|
<screen>
|
||||||
|
/dev/sda1 /dev/sda2
|
||||||
|
|
||||||
|
[EFI ESP Boot partition] [ LUKS encrypted partition filling remainder of disk ]
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. | .
|
||||||
|
. | .
|
||||||
|
. V .
|
||||||
|
. .
|
||||||
|
[ rootfsA ] [ rootfsB ] [ citadel-storage ]
|
||||||
|
</screen>
|
||||||
|
<p>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'.</p>
|
||||||
|
<screen type="sh"># lvs
|
||||||
|
LV VG Attr LSize
|
||||||
|
rootfsA citadel -wi-a----- 2.00g
|
||||||
|
rootfsB citadel -wi-ao---- 2.00g
|
||||||
|
storage citadel -wi-ao---- 472.43g</screen>
|
||||||
|
<section>
|
||||||
|
<title>Bootloader</title>
|
||||||
|
<section>
|
||||||
|
<title>LUKS</title>
|
||||||
|
<p>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).</p>
|
||||||
|
<screen type="sh"># cat /etc/crypttab
|
||||||
|
luks UUID=683a17fc-4457-42cc-a946-cde67195a101 - discard</screen>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Mounting rootfs</title>
|
||||||
|
<p>The initramfs boot stage is orchestrated by various systemd unit files which can be found
|
||||||
|
in the citadel source tree at:</p>
|
||||||
|
<screen>citadel/meta-citadel/recipes-initrd/citadel-initramfs</screen>
|
||||||
|
<p>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:</p>
|
||||||
|
<screen>citadel-rootfs-mount.path
|
||||||
|
citadel-rootfs-mount.service</screen>
|
||||||
|
<p>The .path unit triggers every time /dev/mapper changes and the corresponding .service unit is
|
||||||
|
activated only when all of the LVM volumes inside</p>
|
||||||
|
<screen>ConditionPathExists=/dev/mapper/citadel-rootfsA
|
||||||
|
ConditionPathExists=/dev/mapper/citadel-rootfsB
|
||||||
|
ConditionPathExists=/dev/mapper/citadel-storage</screen>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</page>
|
@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="citadel">
|
||||||
|
<info>
|
||||||
|
<link type="guide" xref="index"/>
|
||||||
|
<desc>Introduction to Subgraph Citadel</desc>
|
||||||
|
</info>
|
||||||
|
<title>Subgraph Citadel</title>
|
||||||
|
<section>
|
||||||
|
<title>What is Citadel?</title>
|
||||||
|
<p>Citadel is the base operating system of the new version of Subgraph OS.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<note style="advanced">
|
||||||
|
<p>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.</p>
|
||||||
|
</note>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<note style="advanced">
|
||||||
|
<p>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.</p>
|
||||||
|
</note>
|
||||||
|
<section>
|
||||||
|
<title>Stateless Foundation</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</page>
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="developer">
|
||||||
|
<info>
|
||||||
|
<link type="guide" xref="index#internals"/>
|
||||||
|
</info>
|
||||||
|
<title>Developer Guide</title>
|
||||||
|
<section>
|
||||||
|
<title>Make Root Filesystem Writable</title>
|
||||||
|
<p>Sometimes it can be useful to make changes directly to the citadel root filesystem to
|
||||||
|
experiment with changes or to debug a problem.</p>
|
||||||
|
<p>First <code>citadel.noverity</code> 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</code>
|
||||||
|
command.</p>
|
||||||
|
<screen># dmsetup status rootfs
|
||||||
|
0 4194304 linear</screen>
|
||||||
|
<p>If the output displays <code>verity</code> instead of <code>linear</code> then dm-verity is enabled
|
||||||
|
and the disk cannot be safely written to.</p>
|
||||||
|
<p>Next remount the root filesystem with read-write flag.</p>
|
||||||
|
<screen># mount -oremount,rw,noatime /</screen>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Debugging GNOME startup</title>
|
||||||
|
</section>
|
||||||
|
</page>
|
@ -0,0 +1,184 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="disk-layout">
|
||||||
|
<info>
|
||||||
|
<link type="guide" xref="index#internals"/>
|
||||||
|
<desc>A Hands-on guide the Citadel Disk and Filesystem Layout</desc>
|
||||||
|
</info>
|
||||||
|
<title>Disk Layout</title>
|
||||||
|
<section>
|
||||||
|
<title>Partitions</title>
|
||||||
|
<p>During installation, two partitions are created on the disk chosen as
|
||||||
|
the target of the install.</p>
|
||||||
|
<p>For example, if the installation disk is <code>/dev/sda</code>:</p>
|
||||||
|
<terms>
|
||||||
|
<item>
|
||||||
|
<title><code>/dev/sda1</code></title>
|
||||||
|
<p>512MB EFI System Partition</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>/dev/sda2</code></title>
|
||||||
|
<p>Remainder of the disk</p>
|
||||||
|
</item>
|
||||||
|
</terms>
|
||||||
|
<p>The partition layout of a running system can be viewed by running the <code>lsblk</code> command.</p>
|
||||||
|
<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</screen>
|
||||||
|
<p>Several further block devices are created during boot when the main disk partition
|
||||||
|
is decrypted.</p>
|
||||||
|
<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)</screen>
|
||||||
|
<terms>
|
||||||
|
<item>
|
||||||
|
<title><code>(a) /boot partition</code></title>
|
||||||
|
<p>EFI boot partition</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>(b) LUKS encrypted partition</code></title>
|
||||||
|
<p>Remainder of disk is an encrypted volume</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>(c) LVM volume group</code></title>
|
||||||
|
<p>Main partition contains several LVM volumes</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>(d) citadel-rootfs(A/B)</code></title>
|
||||||
|
<p>Two root partitions so one can be updated while other is in use.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>(e) /dev/mapper/rootfs</code></title>
|
||||||
|
<p>verity mapper device for mounted root partion</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>(f) /dev/mapper/citadel-storage</code></title>
|
||||||
|
<p>The writable filesystem</p>
|
||||||
|
</item>
|
||||||
|
</terms>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Citadel Filesystem Layout</title>
|
||||||
|
<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</code>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Resource Image Mounts</title>
|
||||||
|
<p>Resource images are mounted into the system by creating loop devices. These devices can be
|
||||||
|
viewed by running the 'losetup' command inside Citadel.</p>
|
||||||
|
<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</screen>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>You can view the verity device mapper node associated with each loop device with
|
||||||
|
the <code>lsblk</code> command.</p>
|
||||||
|
<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</screen>
|
||||||
|
<p>Parameters of each dm-verity instance can be viewed with the veritysetup command.</p>
|
||||||
|
<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</screen>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<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</screen>
|
||||||
|
<p>The citadel-image utility can be used to view the metainfo variables stored in the header
|
||||||
|
section of a resource image file.</p>
|
||||||
|
<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"</screen>
|
||||||
|
<p>RealmFS images also have a resource image header with a slightly different set of
|
||||||
|
metainfo variables.</p>
|
||||||
|
<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"</screen>
|
||||||
|
</section>
|
||||||
|
</page>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="guide" id="index">
|
||||||
|
<title>Citadel Help</title>
|
||||||
|
<section id="user" style="2column">
|
||||||
|
<title>User Guide</title>
|
||||||
|
</section>
|
||||||
|
<section id="internals" style="2column">
|
||||||
|
<title>Citadel Internals</title>
|
||||||
|
</section>
|
||||||
|
</page>
|
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="kernel-cmdline">
|
||||||
|
<info>
|
||||||
|
<link type="guide" xref="index#internals"/>
|
||||||
|
</info>
|
||||||
|
<title>Kernel Command Line Options</title>
|
||||||
|
<list>
|
||||||
|
<item>
|
||||||
|
<p>citadel.noverity</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>citadel.nosignatures</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>citadel.install</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>citadel.overlay</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>citadel.channel</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>citadel.verbose</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>citadel.debug</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>citadel.sway</p>
|
||||||
|
</item>
|
||||||
|
</list>
|
||||||
|
</page>
|
@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="realm-config">
|
||||||
|
<info>
|
||||||
|
<link type="guide" xref="index#user"/>
|
||||||
|
<desc>Realm configuration file reference</desc>
|
||||||
|
</info>
|
||||||
|
<title>Configuring Realms</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<section>
|
||||||
|
<title>Options</title>
|
||||||
|
<terms>
|
||||||
|
<item>
|
||||||
|
<title><code>use-wayland</code></title>
|
||||||
|
<p>If 'true' access to Wayland display will be permitted in realm by
|
||||||
|
adding wayland socket /run/user/1000/wayland-0</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>use-x11</code></title>
|
||||||
|
<p>If 'true' access to X11 server will be added to realm by bind mounting directory
|
||||||
|
/tmp/.X11-unix</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>use-sound</code></title>
|
||||||
|
<p>If 'true' allows the use of sound inside realm. The following items will
|
||||||
|
be added to realm:</p>
|
||||||
|
<list>
|
||||||
|
<item>
|
||||||
|
<p>/dev/snd</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>/dev/shm</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>/run/user/1000/pulse</p>
|
||||||
|
</item>
|
||||||
|
</list>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>use-kvm</code></title>
|
||||||
|
<p>If enabled, /dev/kvm will be added to the realm.
|
||||||
|
This option is only available for nspawn realms.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>use-shared-dir</code></title>
|
||||||
|
<p>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.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>use-ephemeral-home</code></title>
|
||||||
|
<p>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</p>
|
||||||
|
<steps>
|
||||||
|
<item>
|
||||||
|
<p>Home directory is mounted as tmpfs</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>Any files in /realms/skel are copied into home directory</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>Any files in /realms/realm-${name}/skel are copied into home directory</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>Any directories listed in <code>ephemeral-persistent-dirs</code> are bind mounted
|
||||||
|
from /realms/realm-${name}/home into ephemeral home directory.</p>
|
||||||
|
</item>
|
||||||
|
</steps>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>ephemeral-persistent-dirs</code> default: ["Documents"]</title>
|
||||||
|
<p>A list of subdirectories of /realms/realm-${name}/home to bind mount into realm
|
||||||
|
home directory when <code>ephemeral-home</code> is enabled.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>use-network</code></title>
|
||||||
|
<p>network</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>network-zone</code> default: "clear"</title>
|
||||||
|
<p>network zone</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>use-gpu</code></title>
|
||||||
|
<p>Enables hardware graphics acceleration in relam.
|
||||||
|
if 'true' render node device /dev/dri/renderD128 will be added to realm.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>use-gpu-card0</code></title>
|
||||||
|
<p>If 'true' and <code>use-gpu</code> is also enabled, privileged device /dev/dri/card0
|
||||||
|
will be added to realm.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>realmfs</code> default: "base"</title>
|
||||||
|
<p>name of realmfs image</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>overlay</code> default: "storage"</title>
|
||||||
|
<p>type of overlay to use</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>terminal-scheme</code></title>
|
||||||
|
<p>terminal color scheme</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>extra-bindmounts</code></title>
|
||||||
|
<p>bind mounts</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>extra-bindmounts-ro</code></title>
|
||||||
|
<p>read-only bind mounts</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>system-realm</code> default: false</title>
|
||||||
|
<p>system realm</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<title><code>autostart</code> default: false</title>
|
||||||
|
<p>autostart realm</p>
|
||||||
|
</item>
|
||||||
|
</terms>
|
||||||
|
</section>
|
||||||
|
</page>
|
@ -0,0 +1,140 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="realmfs">
|
||||||
|
<info>
|
||||||
|
<link type="guide" xref="index#user"/>
|
||||||
|
<desc>Realm root filesystem images</desc>
|
||||||
|
</info>
|
||||||
|
<title>RealmFS Images</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<section>
|
||||||
|
<title>Updates</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<note style="advanced">
|
||||||
|
<p>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 style="strong">Sealing</em>
|
||||||
|
the image.</p>
|
||||||
|
</note>
|
||||||
|
<section>
|
||||||
|
<title>Apt-Cacher NG Realm</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<code>/etc/apt/apt.conf.d/000apt-cacher-ng-proxy
|
||||||
|
|
||||||
|
Acquire::http::Proxy "http://172.17.0.213:3142/";</code>
|
||||||
|
<p>The apt source lines use the special Apt-Cacher NG syntax.</p>
|
||||||
|
<code>/etc/apt/sources.list
|
||||||
|
|
||||||
|
deb http://HTTPS///deb.debian.org/debian buster main contrib non-free</code>
|
||||||
|
<p>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.</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Updates (Container method)</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Updates with pH Hypervisor</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<steps>
|
||||||
|
<item>
|
||||||
|
<p>The user makes changes to the root filesystem of the realm and pH tracks the blocks that have changed.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>A user request is made to pH to apply the changes to the RealmFS image.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>pH opens a prompt on the desktop to ask the user to confirm that they really did make this request.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>A copy (or reflink) of the current RealmFS is made, and pH applies the changed blocks to this copy.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>The copy is then sealed with the RealmFS key of the user.</p>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<p>Now pH can quietly swap in the new version of the RealmFS image and discard all of the tracked block changes.</p>
|
||||||
|
</item>
|
||||||
|
</steps>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Signing RealmFS Images</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<section>
|
||||||
|
<title>Safe Mode</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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</code> 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.</p>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Base RealmFS image</title>
|
||||||
|
<p>Citadel ships with a RealmFS image called <code>base-realmfs.img</code>. 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</code> and sealed with the newly generated
|
||||||
|
user keys.</p>
|
||||||
|
</section>
|
||||||
|
</page>
|
@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="realms">
|
||||||
|
<info>
|
||||||
|
<link type="guide" xref="index#user"/>
|
||||||
|
</info>
|
||||||
|
<title>Citadel Realms</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<section>
|
||||||
|
<title>The <em>current</em> realm</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Realm directory layout</title>
|
||||||
|
<p>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.</p>
|
||||||
|
<screen>/realms
|
||||||
|
config
|
||||||
|
/Shared
|
||||||
|
/skel
|
||||||
|
/default.realm -> realm-main
|
||||||
|
/realm-main
|
||||||
|
/realm-project
|
||||||
|
/realm-testing</screen>
|
||||||
|
<section>
|
||||||
|
<title>/realms/config</title>
|
||||||
|
<p>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.</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>/realms/Shared</title>
|
||||||
|
<p>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.</p>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</page>
|
@ -0,0 +1,361 @@
|
|||||||
|
<?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>
|
@ -14,6 +14,7 @@ PACKAGE_INSTALL = "\
|
|||||||
adwaita-icon-theme-symbolic \
|
adwaita-icon-theme-symbolic \
|
||||||
adwaita-icon-theme-symbolic-hires \
|
adwaita-icon-theme-symbolic-hires \
|
||||||
"
|
"
|
||||||
|
|
||||||
CITADEL_IMAGE_VERSION = "${CITADEL_IMAGE_VERSION_extra}"
|
CITADEL_IMAGE_VERSION = "${CITADEL_IMAGE_VERSION_extra}"
|
||||||
CITADEL_IMAGE_TYPE = "extra"
|
CITADEL_IMAGE_TYPE = "extra"
|
||||||
|
|
||||||
@ -23,8 +24,11 @@ inherit citadel-image
|
|||||||
ROOTFS_POSTPROCESS_COMMAND += "write_manifest_file; "
|
ROOTFS_POSTPROCESS_COMMAND += "write_manifest_file; "
|
||||||
|
|
||||||
write_manifest_file() {
|
write_manifest_file() {
|
||||||
|
install -m 0755 -d ${IMAGE_ROOTFS}/usr/share/citadel-documentation
|
||||||
|
|
||||||
cat > ${IMAGE_ROOTFS}/manifest << EOF
|
cat > ${IMAGE_ROOTFS}/manifest << EOF
|
||||||
/usr/lib/firmware
|
/usr/lib/firmware
|
||||||
/usr/share:/opt/share
|
/usr/share:/opt/share
|
||||||
|
/sysroot/usr/share/citadel-documentation:/opt/share/citadel-documentation
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,5 @@ RDEPENDS_${PN} = "\
|
|||||||
citadel-tools \
|
citadel-tools \
|
||||||
citadel-tools-realms \
|
citadel-tools-realms \
|
||||||
citadel-tools-boot \
|
citadel-tools-boot \
|
||||||
|
citadel-documentation \
|
||||||
"
|
"
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user