]> git.ipfire.org Git - thirdparty/systemd.git/blob - docs/BUILDING_IMAGES.md
doc: add a bunch of missing <br>
[thirdparty/systemd.git] / docs / BUILDING_IMAGES.md
1 ---
2 title: Safely Building Images
3 category: Concepts
4 layout: default
5 SPDX-License-Identifier: LGPL-2.1-or-later
6 ---
7
8 # Safely Building Images
9
10 In many scenarios OS installations are shipped as pre-built images, that
11 require no further installation process beyond simple `dd`-ing the image to
12 disk and booting it up. When building such "golden" OS images for
13 `systemd`-based OSes a few points should be taken into account.
14
15 Most of the points described here are implemented by the
16 [`mkosi`](https://github.com/systemd/mkosi) OS image builder developed and
17 maintained by the systemd project. If you are using or working on another image
18 builder it's recommended to keep the following concepts and recommendations in
19 mind.
20
21 ## Resources to Reset
22
23 Typically the same OS image shall be deployable in multiple instances, and each
24 instance should automatically acquire its own identifying credentials on first
25 boot. For that it's essential to:
26
27 1. Remove the
28 [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html)
29 file or write the string `uninitialized\n` into it. This file is supposed to
30 carry a 128bit identifier unique to the system. Only when it is reset it
31 will be auto-generated on first boot and thus be truly unique. If this file
32 is not reset, and carries a valid ID every instance of the system will come
33 up with the same ID and that will likely lead to problems sooner or later,
34 as many network-visible identifiers are commonly derived from the machine
35 ID, for example IPv6 addresses or transient MAC addresses.
36
37 2. Remove the `/var/lib/systemd/random-seed` file (see
38 [`systemd-random-seed(8)`](https://www.freedesktop.org/software/systemd/man/systemd-random-seed.service.html)),
39 which is used to seed the kernel's random pool on boot. If this file is
40 shipped pre-initialized, every instance will seed its random pool with the
41 same random data that is included in the image, and thus possibly generate
42 random data that is more similar to other instances booted off the same
43 image than advisable.
44
45 3. Remove the `/loader/random-seed` file (see
46 [`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
47 from the UEFI System Partition (ESP), in case the `systemd-boot` boot loader
48 is used in the image.
49
50 4. It might also make sense to remove
51 [`/etc/hostname`](https://www.freedesktop.org/software/systemd/man/hostname.html)
52 and
53 [`/etc/machine-info`][`systemd-random-seed(8)`](https://www.freedesktop.org/software/systemd/man/machine-info.html)
54 which carry additional identifying information about the OS image.
55
56 ## Boot Menu Entry Identifiers
57
58 The
59 [`kernel-install(8)`](https://www.freedesktop.org/software/systemd/man/kernel-install.html)
60 logic used to generate [Boot Loader Specification Type
61 1](https://systemd.io/BOOT_LOADER_SPECIFICATION) entries by default uses the
62 machine ID as stored in `/etc/machine-id` for naming boot menu entries and the
63 directories in the ESP to place kernel images in. This is done in order to
64 allow multiple installations of the same OS on the same system without
65 conflicts. However, this is problematic if the machine ID shall be generated
66 automatically on first boot: if the ID is not known before the first boot it
67 cannot be used to name the most basic resources required for the boot process
68 to complete.
69
70 Thus, for images that shall acquire their identity on first boot only, it is
71 required to use a different identifier for naming boot menu entries. To allow
72 this the `kernel-install` logic knows the generalized *entry* *token* concept,
73 which can be a freely chosen string to use for identifying the boot menu
74 resources of the OS. If not configured explicitly it defaults to the machine
75 ID. The file `/etc/kernel/entry-token` may be used to configure this string
76 explicitly. Thus, golden image builders should write a suitable identifier into
77 this file, for example the `IMAGE_ID=` or `ID=` field from
78 [`/etc/os-release`](https://www.freedesktop.org/software/systemd/man/os-release.html)
79 (also see below). It is recommended to do this before the `kernel-install`
80 functionality is invoked (i.e. before the package manager is used to install
81 packages into the OS tree being prepared), so that the selected string is
82 automatically used for all entries to be generated.
83
84 ## Booting with Empty `/var/` and/or Empty Root File System
85
86 `systemd` is designed to be able to come up safely and robustly if the `/var/`
87 file system or even the entire root file system (with exception of `/usr/`,
88 i.e. the vendor OS resources) is empty (i.e. "unpopulated"). With this in mind
89 it's relatively easy to build images that only ship a `/usr/` tree, and
90 otherwise carry no other data, populating the rest of the directory hierarchy
91 on first boot as needed.
92
93 Specifically, the following mechanisms are in place:
94
95 1. The `swich-root` logic in systemd, that is used to switch from the initrd
96 phase to the host will create the basic OS hierarchy skeleton if missing. It
97 will create a couple of directories strictly necessary to boot up
98 successfully, plus essential symlinks (such as those necessary for the
99 dynamic loader `ld.so` to function).
100
101 2. PID 1 will initialize `/etc/machine-id` automatically if not initialized yet
102 (see above).
103
104 3. The
105 [`nss-systemd(8)`](https://www.freedesktop.org/software/systemd/man/nss-systemd.html)
106 glibc NSS module ensures the `root` and `nobody` users and groups remain
107 resolvable, even without `/etc/passwd` and `/etc/group` around.
108
109 4. The
110 [`systemd-sysusers(8)`](https://www.freedesktop.org/software/systemd/man/systemd-sysusers.service.html)
111 will component automatically populate `/etc/passwd` and `/etc/group` on
112 first boot with further necessary system users.
113
114 5. The
115 [`systemd-tmpfiles(8)`](https://www.freedesktop.org/software/systemd/man/systemd-tmpfiles-setup.service.html)
116 component ensures that various files and directories below `/etc/`, `/var/`
117 and other places are created automatically at boot if missing. Unlike the
118 directories/symlinks created by the `switch-root` logic above this logic is
119 extensible by packages, and can adjust access modes, file ownership and
120 more. Among others this will also link `/etc/os-release` →
121 `/usr/lib/os-release`, ensuring that the OS release information is
122 unconditionally accessible through `/etc/os-release`.
123
124 6. The
125 [`nss-myhostname(8)`](https://www.freedesktop.org/software/systemd/man/nss-myhostname.html)
126 glibc NSS module will ensure the local host name as well as `localhost`
127 remains resolvable, even without `/etc/hosts` around.
128
129 With these mechanisms the hierarchies below `/var/` and `/etc/` can be safely
130 and robustly populated on first boot, so that the OS can safely boot up. Note
131 that some auxiliary package are not prepared to operate correctly if their
132 configuration data in `/etc/` or their state directories in `/var/` are
133 missing. This can typically be addressed via `systemd-tmpfiles` lines that
134 ensure the missing files and directories are created if missing. In particular,
135 configuration files that are necessary for operation can be automatically
136 copied or symlinked from the `/usr/share/factory/etc/` tree via the `C` or `L`
137 line types. That said, we recommend that all packages safely fall back to
138 internal defaults if their configuration is missing, making such additional
139 steps unnecessary.
140
141 Note that while `systemd` itself explicitly supports booting up with entirely
142 unpopulated images (`/usr/` being the only required directory to be populated)
143 distributions might not be there yet: depending on your distribution further,
144 manual work might be required to make this scenario work.
145
146 ## Adapting OS Images to Storage
147
148 Typically, if an image is `dd`-ed onto a target disk it will be minimal:
149 i.e. only consist of necessary vendor data, and lack "payload" data, that shall
150 be individual to the system, and dependent on host parameters. On first boot,
151 the OS should take possession of the backing storage as necessary, dynamically
152 using available space. Specifically:
153
154 1. Additional partitions should be created, that make no sense to ship
155 pre-built in the image. For example `/tmp/` or `/home/` partitions, or even
156 `/var/` or the root file system (see above).
157
158 2. Additional partitions should be created that shall function as A/B
159 secondaries for partitions shipped in the original image. In other words: if
160 the `/usr/` file system shall be updated in an A/B fashion it typically
161 makes sense to ship the original A file system in the deployed image, but
162 create the B partition on first boot.
163
164 3. Partitions covering only a part of the disk should be grown to the full
165 extent of the disk.
166
167 4. File systems in uninitialized partitions should be formatted with a file
168 system of choice.
169
170 5. File systems covering only a part of a partition should be grown to the full
171 extent of the partition.
172
173 6. Partitions should be encrypted with cryptographic keys generated locally on
174 the machine the system is first booted on, ensuring these keys remain local
175 and are not shared with any other instance of the OS image.
176
177 Or any combination of the above: i.e. first create a partition, then encrypt
178 it, then format it.
179
180 `systemd` provides multiple tools to implement the above logic:
181
182 1. The
183 [`systemd-repart(8)`](https://www.freedesktop.org/software/systemd/man/systemd-repart.service.html)
184 component may manipulate GPT partition tables automatically on boot, growing
185 partitions or adding in partitions taking the backing storage size into
186 account. It can also encrypt partitions automatically it creates (even bind
187 to TPM2, automatically) and populate partitions from various sources. It
188 does this all in a robust fashion so that aborted invocations will not leave
189 incompletely set up partitions around.
190
191 2. The
192 [`systemd-growfs@(8).service`](https://www.freedesktop.org/software/systemd/man/systemd-growfs.html)
193 tool can automatically grow a file system to the partition it is contained
194 in. The `x-systemd.growfs` mount option in `/etc/fstab` is sufficient to
195 enable this logic for specific mounts. Alternatively appropriately set up
196 partitions can set GPT partition flag 59 to request this behaviour, see the
197 [Discoverable Partitions
198 Specification](https://systemd.io/DISCOVERABLE_PARTITIONS) for details. If
199 the file system is already grown it executes no operation.
200
201 3. Similar, the `systemd-makefs@.service` and `systemd-makeswap@.service`
202 services can format file systems and swap spaces before first use, if they
203 carry no file system signature yet. The `x-systemd.makefs` mount option in
204 `/etc/fstab` may be used to request this functionality.
205
206 ## Provisioning Image Settings
207
208 While a lot of work has gone into ensuring `systemd` systems can safely boot
209 with unpopulated `/etc/` trees, it sometimes is desirable to set a couple of
210 basic settings *after* `dd`-ing the image to disk, but *before* first boot. For
211 this the tool
212 [`systemd-firstboot(1)`](https://www.freedesktop.org/software/systemd/man/systemd-firstboot.html)
213 can be useful, with its `--image=` switch. It may be used to set very basic
214 settings, such as the root password or hostname on an OS disk image or
215 installed block device.
216
217 ## Distinguishing First Boot
218
219 For various purposes it's useful to be able to distinguish the first boot-up of
220 the system from later boot-ups (for example, to set up TPM hardware
221 specifically, or register a system somewhere). `systemd` provides mechanisms to
222 implement that. Specifically, the `ConditionFirstBoot=` and `AssertFirstBoot=`
223 settings may be used to conditionalize units to only run on first boot. See
224 [`systemd.unit(5)`](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConditionFirstBoot=)
225 for details.
226
227 A special target unit `first-boot-complete.target` may be used as milestone to
228 safely handle first boots where the system is powered off too early: if the
229 first boot process is aborted before this target is reached, the following boot
230 process will be considered a first boot, too. Once the target is reached,
231 subsequent boots will not be considered first boots anymore, even if the boot
232 process is aborted immediately after. Thus, services that must complete fully
233 before a system shall be considered fully past the first boot should be ordered
234 before this target unit.
235
236 Whether a system will come up in first boot state or not is derived from the
237 initialization status of `/etc/machine-id`: if the file already carries a valid
238 ID the system is already past the first boot. If it is not initialized yet it
239 is still considered in the first boot state. For details see
240 [`machine-id(5)`](https://www.freedesktop.org/software/systemd/man/machine-id.html).
241
242 ## Image Metadata
243
244 Typically, when operating with golden disk images it is useful to be able to
245 identify them and their version. For this the two fields `IMAGE_ID=` and
246 `IMAGE_VERSION=` have been defined in
247 [`os-release(5)`](https://www.freedesktop.org/software/systemd/man/os-release.html). These
248 fields may be accessed from unit files and similar via the `%M` and `%A`
249 specifiers.
250
251 Depending on how the images are put together it might make sense to leave the
252 OS distribution's `os-release` file as is in `/usr/lib/os-release` but to
253 replace the usual `/etc/os-release` symlink with a regular file that extends
254 the distribution's file with one augmented with these two additional
255 fields.
256
257 ## Links
258
259 [`machine-id(5)`](https://www.freedesktop.org/software/systemd/man/machine-id.html)<br>
260 [`systemd-random-seed(8)`](https://www.freedesktop.org/software/systemd/man/systemd-random-seed.service.html)<br>
261 [`os-release(5)`](https://www.freedesktop.org/software/systemd/man/os-release.html)<br>
262 [Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION)<br>
263 [Discoverable Partitions Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)<br>
264 [`mkosi`](https://github.com/systemd/mkosi)<br>
265 [`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)<br>
266 [`systemd-repart(8)`](https://www.freedesktop.org/software/systemd/man/systemd-repart.service.html)<br>
267 [`systemd-growfs@(8).service`](https://www.freedesktop.org/software/systemd/man/systemd-growfs.html)<br>