]> git.ipfire.org Git - thirdparty/systemd.git/blob - docs/BUILDING_IMAGES.md
Merge pull request #22821 from poettering/udev-tweaklets
[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 image
43 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 `/etc/hostname` and `/etc/machine-info`
51 which carry additional identifying information about the OS image.
52
53 ## Boot Menu Entry Identifiers
54
55 The `kernel-install` logic used to generate [Boot Loader Specification Type
56 1](https://systemd.io/BOOT_LOADER_SPECIFICATION) entries by default uses the
57 machine ID as stored in `/etc/machine-id` for naming boot menu entries and the
58 directories in the ESP to place kernel images in. This is done in order to
59 allow multiple installations of the same OS on the same system without
60 conflicts. However, this is problematic if the machine ID shall be generated
61 automatically on first boot: if the ID is not known before the first boot it
62 cannot be used to name the most basic resources required for the boot process
63 to complete.
64
65 Thus, for images that shall acquire their identity on first boot only, it is
66 required to use a different identifier for naming boot menu entries. To allow
67 this the `kernel-install` logic knows the generalized *entry* *token* concept,
68 which can be a freely chosen string to use for identifying the boot menu
69 resources of the OS. If not configured explicitly it defaults to the machine
70 ID. The file `/etc/kernel/entry-token` may be used to configure this string
71 explicitly. Thus, golden image builders should write a suitable identifier into
72 this file, for example the `IMAGE_ID=` or `ID=` field from
73 `/etc/os-release`. It is recommended to do this before the `kernel-install`
74 functionality is invoked (i.e. before the package manager is used to install
75 packages into the OS tree being prepared), so that the selected string is
76 automatically used for all entries to be generated.
77
78 ## Booting with Empty `/var/` and/or Empty Root File System
79
80 `systemd` is designed to be able to come up safely and robustly if the `/var/`
81 file system or even the entire root file system (with exception of `/usr/`,
82 i.e. the vendor OS resources) is empty (i.e. "unpopulated"). With this in mind
83 it's relatively easy to build images that only ship a `/usr/` tree, and
84 otherwise carry no other data, populating the rest of the directory hierarchy
85 on first boot as needed.
86
87 Specifically, the following mechanisms are in place:
88
89 1. The `swich-root` logic in systemd, that is used to switch from the initrd
90 phase to the host will create the basic OS hierarchy skeleton if missing. It
91 will create a couple of directories strictly necessary to boot up
92 successfully, plus essential symlinks (such as those necessary for the
93 dynamic loader `ld.so` to function).
94
95 2. PID 1 will initialize `/etc/machine-id` automatically if not initialized yet
96 (see above).
97
98 3. The `nss-systemd` glibc NSS module ensures the `root` and `nobody` users and
99 groups remain resolvable, even without `/etc/passwd` and `/etc/group` around.
100
101 4. The
102 [`systemd-sysusers`](https://www.freedesktop.org/software/systemd/man/systemd-sysusers.service.html)
103 will component automatically populate `/etc/passwd` and `/etc/group` on
104 first boot with further necessary system users.
105
106 5. The
107 [`systemd-tmpfiles`](https://www.freedesktop.org/software/systemd/man/systemd-tmpfiles-setup.service.html)
108 component ensures that various files and directories below `/etc/`, `/var/`
109 and other places are created automatically at boot if missing. Unlike the
110 directories/symlinks created by the `switch-root` logic above this logic is
111 extensible by packages, and can adjust access modes, file ownership and
112 more. Among others this will also link `/etc/os-release` →
113 `/usr/lib/os-release`, ensuring that the OS release information is
114 unconditionally accessible through `/etc/os-release`.
115
116 6. The `nss-myhostname` glibc NSS module will ensure the local host name as
117 well as `localhost` remains resolvable, even without `/etc/hosts` around.
118
119 With these mechanisms the hierarchies below `/var/` and `/etc/` can be safely
120 and robustly populated on first boot, so that the OS can safely boot up. Note
121 that some auxiliary package are not prepared to operate correctly if their
122 configuration data in `/etc/` or their state directories in `/var/` are
123 missing. This can typically be addressed via `systemd-tmpfiles` lines that
124 ensure the missing files and directories are created if missing. In particular,
125 configuration files that are necessary for operation can be automatically
126 copied or symlinked from the `/usr/share/factory/etc/` tree via the `C` or `L`
127 line types. That said, we recommend that all packages safely fall back to
128 internal defaults if their configuration is missing, making such additional
129 steps unnecessary.
130
131 Note that while `systemd` itself explicitly supports booting up with entirely
132 unpopulated images (`/usr/` being the only required directory to be populated)
133 distributions might not be there yet: depending on your distribution further,
134 manual work might be required to make this scenario work.
135
136 ## Adapting OS Images to Storage
137
138 Typically, if an image is `dd`-ed onto a target disk it will be minimal:
139 i.e. only consist of necessary vendor data, and lack "payload" data, that shall
140 be individual to the system, and dependent on host parameters. On first boot,
141 the OS should take possession of the backing storage as necessary, dynamically
142 using available space. Specifically:
143
144 1. Additional partitions should be created, that make no sense to ship
145 pre-built in the image. For example `/tmp/` or `/home/` partitions, or even
146 `/var/` or the root file system (see above).
147
148 2. Additional partitions should be created that shall function as A/B
149 secondaries for partitions shipped in the original image. In other words: if
150 the `/usr/` file system shall be updated in an A/B fashion it typically
151 makes sense to ship the original A file system in the deployed image, but
152 create the B partition on first boot.
153
154 3. Partitions covering only a part of the disk should be grown to the full
155 extent of the disk.
156
157 4. File systems in uninitialized partitions should be formatted with a file
158 system of choice.
159
160 5. File systems covering only a part of a partition should be grown to the full
161 extent of the partition.
162
163 6. Partitions should be encrypted with cryptographic keys generated locally on
164 the machine the system is first booted on, ensuring these keys remain local
165 and are not shared with any other instance of the OS image.
166
167 Or any combination of the above: i.e. first create a partition, then encrypt
168 it, then format it.
169
170 `systemd` provides multiple tools to implement the above logic:
171
172 1. The
173 [`systemd-repart`](https://www.freedesktop.org/software/systemd/man/systemd-repart.service.html)
174 component may manipulate GPT partition tables automatically on boot, growing
175 partitions or adding in partitions taking the backing storage size into
176 account. It can also encrypt partitions automatically it creates (even bind
177 to TPM2, automatically) and populate partitions from various sources. It
178 does this all in a robust fashion so that aborted invocations will not leave
179 incompletely set up partitions around.
180
181 2. The
182 [`systemd-makefs@(8).service`](https://www.freedesktop.org/software/systemd/man/systemd-growfs.html)
183 tool can automatically grow a file system to the partition it is contained
184 in. The `x-systemd.growfs` `/etc/fstab` mount option is sufficient to enable
185 this logic for specific mounts. If the file system is already grown it
186 executes no operation.
187
188 3. Similar, the `systemd-makefs@.service` and `systemd-makeswap@.service`
189 services can format file systems and swap spaces before first use, if they
190 carry no file system signature yet. The `x-systemd.makefs` mount option in
191 `/etc/fstab` may be used to request this functionality.
192
193 ## Provisioning Image Settings
194
195 While a lot of work has gone into ensuring `systemd` systems can safely boot
196 with unpopulated `/etc/` trees, it sometimes is desirable to set a couple of
197 basic settings *after* `dd`-ing the image to disk, but *before* first boot. For
198 this the tool
199 [`systemd-firstboot`](https://www.freedesktop.org/software/systemd/man/systemd-firstboot.html)
200 can be useful, with its `--image=` switch. It may be used to set very basic
201 settings, such as the root password or hostname on an OS disk image or
202 installed block device.
203
204 ## Distinguishing First Boot
205
206 For various purposes it's useful to be able to distinguish the first boot-up of
207 the system from later boot-ups (for example, to set up TPM hardware
208 specifically, or register a system somewhere). `systemd` provides mechanisms to
209 implement that. Specifically, the `ConditionFirstBoot=` and `AssertFirstBoot=`
210 settings may be used to conditionalize units to only run on first boot. See
211 [`systemd.unit(5)`](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConditionFirstBoot=)
212 for details.
213
214 A special target unit `first-boot-complete.target` may be used as milestone to
215 safely handle first boots where the system is powered off too early: if the
216 first boot process is aborted before this target is reached, the following boot
217 process will be considered a first boot, too. Once the target is reached,
218 subsequent boots will not be considered first boots anymore, even if the boot
219 process is aborted immediately after. Thus, services that must complete fully
220 before a system shall be considered fully past the first boot should be ordered
221 before this target unit.
222
223 Whether a system will come up in first boot state or not is derived from the
224 initialization status of `/etc/machine-id`: if the file already carries a valid
225 ID the system is already past the first boot. If it is not initialized yet it
226 is still considered in the first boot state. For details see
227 [`machine-id(5)`](https://www.freedesktop.org/software/systemd/man/machine-id.html).