]>
Commit | Line | Data |
---|---|---|
93f59100 LP |
1 | --- |
2 | title: Random Seeds | |
4cdca0af | 3 | category: Concepts |
b41a3f66 | 4 | layout: default |
0aff7b75 | 5 | SPDX-License-Identifier: LGPL-2.1-or-later |
93f59100 LP |
6 | --- |
7 | ||
8 | # Random Seeds | |
9 | ||
10 | systemd can help in a number of ways with providing reliable, high quality | |
11 | random numbers from early boot on. | |
12 | ||
13 | ## Linux Kernel Entropy Pool | |
14 | ||
15 | Today's computer systems require random number generators for numerous | |
16 | cryptographic and other purposes. On Linux systems, the kernel's entropy pool | |
17 | is typically used as high-quality source of random numbers. The kernel's | |
18 | entropy pool combines various entropy inputs together, mixes them and provides | |
19 | an API to userspace as well as to internal kernel subsystems to retrieve | |
20 | it. This entropy pool needs to be initialized with a minimal level of entropy | |
21 | before it can provide high quality, cryptographic random numbers to | |
22 | applications. Until the entropy pool is fully initialized application requests | |
23 | for high-quality random numbers cannot be fulfilled. | |
24 | ||
25 | The Linux kernel provides three relevant userspace APIs to request random data | |
26 | from the kernel's entropy pool: | |
27 | ||
e2285c57 | 28 | * The [`getrandom()`](https://man7.org/linux/man-pages/man2/getrandom.2.html) |
e347d53a | 29 | system call with its `flags` parameter set to 0. If invoked, the calling |
93f59100 LP |
30 | program will synchronously block until the random pool is fully initialized |
31 | and the requested bytes can be provided. | |
32 | ||
33 | * The `getrandom()` system call with its `flags` parameter set to | |
e347d53a | 34 | `GRND_NONBLOCK`. If invoked, the request for random bytes will fail if the |
93f59100 LP |
35 | pool is not initialized yet. |
36 | ||
37 | * Reading from the | |
e2285c57 | 38 | [`/dev/urandom`](https://man7.org/linux/man-pages/man4/urandom.4.html) |
93f59100 LP |
39 | pseudo-device will always return random bytes immediately, even if the pool |
40 | is not initialized. The provided random bytes will be of low quality in this | |
e347d53a | 41 | case however. Moreover, the kernel will log about all programs using this |
93f59100 LP |
42 | interface in this state, and which thus potentially rely on an uninitialized |
43 | entropy pool. | |
44 | ||
e347d53a | 45 | (Strictly speaking, there are more APIs, for example `/dev/random`, but these |
93f59100 LP |
46 | should not be used by almost any application and hence aren't mentioned here.) |
47 | ||
48 | Note that the time it takes to initialize the random pool may differ between | |
49 | systems. If local hardware random number generators are available, | |
50 | initialization is likely quick, but particularly in embedded and virtualized | |
51 | environments available entropy is small and thus random pool initialization | |
52 | might take a long time (up to tens of minutes!). | |
53 | ||
54 | Modern hardware tends to come with a number of hardware random number | |
55 | generators (hwrng), that may be used to relatively quickly fill up the entropy | |
56 | pool. Specifically: | |
57 | ||
58 | * All recent Intel and AMD CPUs provide the CPU opcode | |
59 | [RDRAND](https://en.wikipedia.org/wiki/RdRand) to acquire random bytes. Linux | |
60 | includes random bytes generated this way in its entropy pool, but didn't use | |
61 | to credit entropy for it (i.e. data from this source wasn't considered good | |
62 | enough to consider the entropy pool properly filled even though it was | |
63 | used). This has changed recently however, and most big distributions have | |
64 | turned on the `CONFIG_RANDOM_TRUST_CPU=y` kernel compile time option. This | |
65 | means systems with CPUs supporting this opcode will be able to very quickly | |
66 | reach the "pool filled" state. | |
67 | ||
68 | * The TPM security chip that is available on all modern desktop systems has a | |
69 | hwrng. It is also fed into the entropy pool, but generally not credited | |
70 | entropy. You may use `rng_core.default_quality=1000` on the kernel command | |
71 | line to change that, but note that this is a global setting affect all | |
72 | hwrngs. (Yeah, that's weird.) | |
73 | ||
74 | * Many Intel and AMD chipsets have hwrng chips. Their Linux drivers usually | |
75 | don't credit entropy. (But there's `rng_core.default_quality=1000`, see | |
76 | above.) | |
77 | ||
78 | * Various embedded boards have hwrng chips. Some drivers automatically credit | |
79 | entropy, others do not. Some WiFi chips appear to have hwrng sources too, and | |
80 | they usually do not credit entropy for them. | |
81 | ||
82 | * `virtio-rng` is used in virtualized environments and retrieves random data | |
83 | from the VM host. It credits full entropy. | |
84 | ||
85 | * The EFI firmware typically provides a RNG API. When transitioning from UEFI | |
86 | to kernel mode Linux will query some random data through it, and feed it into | |
87 | the pool, but not credit entropy to it. What kind of random source is behind | |
88 | the EFI RNG API is often not entirely clear, but it hopefully is some kind of | |
89 | hardware source. | |
90 | ||
91 | If neither of these are available (in fact, even if they are), Linux generates | |
92 | entropy from various non-hwrng sources in various subsystems, all of which | |
93 | ultimately are rooted in IRQ noise, a very "slow" source of entropy, in | |
94 | particular in virtualized environments. | |
95 | ||
96 | ## `systemd`'s Use of Random Numbers | |
97 | ||
98 | systemd is responsible for bringing up the OS. It generally runs as the first | |
99 | userspace process the kernel invokes. Because of that it runs at a time where | |
100 | the entropy pool is typically not yet initialized, and thus requests to acquire | |
101 | random bytes will either be delayed, will fail or result in a noisy kernel log | |
102 | message (see above). | |
103 | ||
104 | Various other components run during early boot that require random bytes. For | |
55c041b4 LP |
105 | example, initrds nowadays communicate with encrypted networks or access |
106 | encrypted storage which might need random numbers. systemd itself requires | |
107 | random numbers as well, including for the following uses: | |
93f59100 LP |
108 | |
109 | * systemd assigns 'invocation' UUIDs to all services it invokes that uniquely | |
e347d53a | 110 | identify each invocation. This is useful to retain a global handle on a specific |
93f59100 LP |
111 | service invocation and relate it to other data. For example, log data |
112 | collected by the journal usually includes the invocation UUID and thus the | |
113 | runtime context the service manager maintains can be neatly matched up with | |
114 | the log data a specific service invocation generated. systemd also | |
115 | initializes `/etc/machine-id` with a randomized UUID. (systemd also makes use | |
116 | of the randomized "boot id" the kernel exposes in | |
117 | `/proc/sys/kernel/random/boot_id`). These UUIDs are exclusively Type 4 UUIDs, | |
118 | i.e. randomly generated ones. | |
119 | ||
120 | * systemd maintains various hash tables internally. In order to harden them | |
121 | against [collision | |
21385e63 | 122 | attacks](https://www.cs.auckland.ac.nz/~mcw/Teaching/refs/misc/denial-of-service.pdf) |
93f59100 LP |
123 | they are seeded with random numbers. |
124 | ||
125 | * At various places systemd needs random bytes for temporary file name | |
126 | generation, UID allocation randomization, and similar. | |
127 | ||
128 | * systemd-resolved and systemd-networkd use random number generators to harden | |
129 | the protocols they implement against packet forgery. | |
130 | ||
131 | * systemd-udevd and systemd-nspawn can generate randomized MAC addresses for | |
132 | network devices. | |
133 | ||
134 | Note that these cases generally do not require a cryptographic-grade random | |
135 | number generator, as most of these utilize random numbers to minimize risk of | |
136 | collision and not to generate secret key material. However, they usually do | |
137 | require "medium-grade" random data. For example: systemd's hash-maps are | |
138 | reseeded if they grow beyond certain thresholds (and thus collisions are more | |
139 | likely). This means they are generally fine with low-quality (even constant) | |
140 | random numbers initially as long as they get better with time, so that | |
141 | collision attacks are eventually thwarted as better, non-guessable seeds are | |
142 | acquired. | |
143 | ||
144 | ## Keeping `systemd'`s Demand on the Kernel Entropy Pool Minimal | |
145 | ||
146 | Since most of systemd's own use of random numbers do not require | |
ffa047a0 JD |
147 | cryptographic-grade RNGs, it tries to avoid blocking reads to the kernel's RNG, |
148 | opting instead for using `getrandom(GRND_INSECURE)`. After the pool is | |
149 | initialized, this is identical to `getrandom(0)`, returning cryptographically | |
150 | secure random numbers, but before it's initialized it has the nice effect of | |
151 | not blocking system boot. | |
93f59100 LP |
152 | |
153 | ## `systemd`'s Support for Filling the Kernel Entropy Pool | |
154 | ||
155 | systemd has various provisions to ensure the kernel entropy is filled during | |
156 | boot, in order to ensure the entropy pool is filled up quickly. | |
157 | ||
158 | 1. When systemd's PID 1 detects it runs in a virtualized environment providing | |
159 | the `virtio-rng` interface it will load the necessary kernel modules to make | |
160 | use of it during earliest boot, if possible — much earlier than regular | |
161 | kernel module loading done by `systemd-udevd.service`. This should ensure | |
162 | that in VM environments the entropy pool is quickly filled, even before | |
163 | systemd invokes the first service process — as long as the VM environment | |
164 | provides virtualized RNG hardware (and VM environments really should!). | |
165 | ||
166 | 2. The | |
167 | [`systemd-random-seed.service`](https://www.freedesktop.org/software/systemd/man/systemd-random-seed.service.html) | |
168 | system service will load a random seed from `/var/lib/systemd/random-seed` | |
169 | into the kernel entropy pool. By default it does not credit entropy for it | |
170 | though, since the seed is — more often than not — not reset when 'golden' | |
171 | master images of an OS are created, and thus replicated into every | |
172 | installation. If OS image builders carefully reset the random seed file | |
173 | before generating the image it should be safe to credit entropy, which can | |
d35c7741 LP |
174 | be enabled by setting the `$SYSTEMD_RANDOM_SEED_CREDIT` environment variable |
175 | for the service to `1` (or even `force`, see man page). Note however, that | |
176 | this service typically runs relatively late during early boot: long after | |
55c041b4 LP |
177 | the initrd completed, and after the `/var/` file system became |
178 | writable. This is usually too late for many applications, it is hence not | |
179 | advised to rely exclusively on this functionality to seed the kernel's | |
93f59100 LP |
180 | entropy pool. Also note that this service synchronously waits until the |
181 | kernel's entropy pool is initialized before completing start-up. It may thus | |
182 | be used by other services as synchronization point to order against, if they | |
183 | require an initialized entropy pool to operate correctly. | |
184 | ||
185 | 3. The | |
186 | [`systemd-boot`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html) | |
187 | EFI boot loader included in systemd is able to maintain and provide a random | |
188 | seed stored in the EFI System Partition (ESP) to the booted OS, which allows | |
189 | booting up with a fully initialized entropy pool from earliest boot | |
190 | on. During installation of the boot loader (or when invoking [`bootctl | |
191 | random-seed`](https://www.freedesktop.org/software/systemd/man/bootctl.html#random-seed)) | |
192 | a seed file with an initial seed is placed in a file `/loader/random-seed` | |
193 | in the ESP. In addition, an identically sized randomized EFI variable called | |
37b22b3b ZJS |
194 | the 'system token' is set, which is written to the machine's firmware NVRAM. |
195 | During boot, when `systemd-boot` finds both the random seed file and the | |
196 | system token they are combined and hashed with SHA256 (in counter mode, to | |
197 | generate sufficient data), to generate a new random seed file to store in | |
93f59100 LP |
198 | the ESP as well as a random seed to pass to the OS kernel. The new random |
199 | seed file for the ESP is then written to the ESP, ensuring this is completed | |
0be72218 JD |
200 | before the OS is invoked. |
201 | ||
202 | The kernel then reads the random seed that the boot loader passes to it, via | |
203 | the EFI configuration table entry, `LINUX_EFI_RANDOM_SEED_TABLE_GUID` | |
204 | (1ce1e5bc-7ceb-42f2-81e5-8aadf180f57b), which is allocated with pool memory | |
205 | of type `EfiACPIReclaimMemory`. Its contents have the form: | |
206 | ``` | |
207 | struct linux_efi_random_seed { | |
208 | u32 size; // of the 'seed' array in bytes | |
209 | u8 seed[]; | |
210 | }; | |
211 | ``` | |
212 | The size field is generally set to 32 bytes, and the seed field includes a | |
213 | hashed representation of any prior seed in `LINUX_EFI_RANDOM_SEED_TABLE_GUID` | |
214 | together with the new seed. | |
215 | ||
216 | This mechanism is able to safely provide an initialized entropy pool before | |
217 | userspace even starts and guarantees that different seeds are passed from | |
218 | the boot loader to the OS on every boot (in a way that does not allow | |
219 | regeneration of an old seed file from a new seed file). Moreover, when an OS | |
220 | image is replicated between multiple images and the random seed is not | |
221 | reset, this will still result in different random seeds being passed to the | |
222 | OS, as the per-machine 'system token' is specific to the physical host, and | |
223 | not included in OS disk images. If the 'system token' is properly | |
224 | initialized and kept sufficiently secret it should not be possible to | |
225 | regenerate the entropy pool of different machines, even if this seed is the | |
226 | only source of entropy. | |
93f59100 LP |
227 | |
228 | Note that the writes to the ESP needed to maintain the random seed should be | |
0be72218 JD |
229 | minimal. Because the size of the random seed file is generally set to 32 bytes, |
230 | updating the random seed in the ESP should be doable safely with a single | |
231 | sector write (since hard-disk sectors typically happen to be 512 bytes long, | |
232 | too), which should be safe even with FAT file system drivers built into | |
93f59100 LP |
233 | low-quality EFI firmwares. |
234 | ||
18d9cee0 LP |
235 | 4. A kernel command line option `systemd.random_seed=` may be used to pass in a |
236 | base64 encoded seed to initialize the kernel's entropy pool from during | |
237 | early service manager initialization. This option is only safe in testing | |
238 | environments, as the random seed passed this way is accessible to | |
239 | unprivileged programs via `/proc/cmdline`. Using this option outside of | |
240 | testing environments is a security problem since cryptographic key material | |
241 | derived from the entropy pool initialized with a seed accessible to | |
242 | unprivileged programs should not be considered secret. | |
243 | ||
244 | With the four mechanisms described above it should be possible to provide | |
93f59100 LP |
245 | early-boot entropy in most cases. Specifically: |
246 | ||
247 | 1. On EFI systems, `systemd-boot`'s random seed logic should make sure good | |
248 | entropy is available during earliest boot — as long as `systemd-boot` is | |
249 | used as boot loader, and outside of virtualized environments. | |
250 | ||
251 | 2. On virtualized systems, the early `virtio-rng` hookup should ensure entropy | |
252 | is available early on — as long as the VM environment provides virtualized | |
253 | RNG devices, which they really should all do in 2019. Complain to your | |
18d9cee0 LP |
254 | hosting provider if they don't. For VMs used in testing environments, |
255 | `systemd.random_seed=` may be used as an alternative to a virtualized RNG. | |
93f59100 | 256 | |
ffa047a0 JD |
257 | 3. In general, systemd's own reliance on the kernel entropy pool is minimal |
258 | (due to the use of `GRND_INSECURE`). | |
93f59100 LP |
259 | |
260 | 4. In all other cases, `systemd-random-seed.service` will help a bit, but — as | |
261 | mentioned — is too late to help with early boot. | |
262 | ||
263 | This primarily leaves two kind of systems in the cold: | |
264 | ||
265 | 1. Some embedded systems. Many embedded chipsets have hwrng functionality these | |
266 | days. Consider using them while crediting | |
267 | entropy. (i.e. `rng_core.default_quality=1000` on the kernel command line is | |
268 | your friend). Or accept that the system might take a bit longer to | |
269 | boot. Alternatively, consider implementing a solution similar to | |
270 | systemd-boot's random seed concept in your platform's boot loader. | |
271 | ||
18d9cee0 LP |
272 | 2. Virtualized environments that lack both virtio-rng and RDRAND, outside of |
273 | test environments. Tough luck. Talk to your hosting provider, and ask them | |
274 | to fix this. | |
93f59100 LP |
275 | |
276 | 3. Also note: if you deploy an image without any random seed and/or without | |
277 | installing any 'system token' in an EFI variable, as described above, this | |
278 | means that on the first boot no seed can be passed to the OS | |
279 | either. However, as the boot completes (with entropy acquired elsewhere), | |
280 | systemd will automatically install both a random seed in the GPT and a | |
281 | 'system token' in the EFI variable space, so that any future boots will have | |
282 | entropy from earliest boot on — all provided `systemd-boot` is used. | |
283 | ||
284 | ## Frequently Asked Questions | |
285 | ||
286 | 1. *Why don't you just use getrandom()? That's all you need!* | |
287 | ||
288 | Did you read any of the above? getrandom() is hooked to the kernel entropy | |
289 | pool, and during early boot it's not going to be filled yet, very likely. We | |
290 | do use it in many cases, but not in all. Please read the above again! | |
291 | ||
292 | 2. *Why don't you use | |
e2285c57 | 293 | [getentropy()](https://man7.org/linux/man-pages/man3/getentropy.3.html)? That's |
93f59100 LP |
294 | all you need!* |
295 | ||
296 | Same story. That call is just a different name for `getrandom()` with | |
297 | `flags` set to zero, and some additional limitations, and thus it also needs | |
298 | the kernel's entropy pool to be initialized, which is the whole problem we | |
299 | are trying to address here. | |
300 | ||
301 | 3. *Why don't you generate your UUIDs with | |
e2285c57 | 302 | [`uuidd`](https://man7.org/linux/man-pages/man8/uuidd.8.html)? That's all you |
93f59100 LP |
303 | need!* |
304 | ||
305 | First of all, that's a system service, i.e. something that runs as "payload" | |
306 | of systemd, long after systemd is already up and hence can't provide us | |
307 | UUIDs during earliest boot yet. Don't forget: to assign the invocation UUID | |
308 | for the `uuidd.service` start we already need a UUID that the service is | |
309 | supposed to provide us. More importantly though, `uuidd` needs state/a random | |
310 | seed/a MAC address/host ID to operate, all of which are not available during | |
311 | early boot. | |
312 | ||
313 | 4. *Why don't you generate your UUIDs with `/proc/sys/kernel/random/uuid`? | |
314 | That's all you need!* | |
315 | ||
316 | This is just a different, more limited interface to `/dev/urandom`. It gains | |
317 | us nothing. | |
318 | ||
319 | 5. *Why don't you use [`rngd`](https://github.com/nhorman/rng-tools), | |
320 | [`haveged`](http://www.issihosts.com/haveged/), | |
321 | [`egd`](http://egd.sourceforge.net/)? That's all you need!* | |
322 | ||
323 | Like `uuidd` above these are system services, hence come too late for our | |
324 | use-case. In addition much of what `rngd` provides appears to be equivalent | |
325 | to `CONFIG_RANDOM_TRUST_CPU=y` or `rng_core.default_quality=1000`, except | |
326 | being more complex and involving userspace. These services partly measure | |
327 | system behavior (such as scheduling effects) which the kernel either | |
328 | already feeds into its pool anyway (and thus shouldn't be fed into it a | |
329 | second time, crediting entropy for it a second time) or is at least | |
330 | something the kernel could much better do on its own. Hence, if what these | |
331 | daemons do is still desirable today, this would be much better implemented | |
332 | in kernel (which would be very welcome of course, but wouldn't really help | |
333 | us here in our specific problem, see above). | |
334 | ||
335 | 6. *Why don't you use [`arc4random()`](https://man.openbsd.org/arc4random.3)? | |
336 | That's all you need!* | |
337 | ||
338 | This doesn't solve the issue, since it requires a nonce to start from, and | |
339 | it gets that from `getrandom()`, and thus we have to wait for random pool | |
340 | initialization the same way as calling `getrandom()` | |
341 | directly. `arc4random()` is nothing more than optimization, in fact it | |
342 | implements similar algorithms that the kernel entropy pool implements | |
343 | anyway, hence besides being able to provide random bytes with higher | |
344 | throughput there's little it gets us over just using `getrandom()`. Also, | |
345 | it's not supported by glibc. And as long as that's the case we are not keen | |
346 | on using it, as we'd have to maintain that on our own, and we don't want to | |
347 | maintain our own cryptographic primitives if we don't have to. Since | |
348 | systemd's uses are not performance relevant (besides the pool initialization | |
349 | delay, which this doesn't solve), there's hence little benefit for us to | |
350 | call these functions. That said, if glibc learns these APIs one day, we'll | |
351 | certainly make use of them where appropriate. | |
352 | ||
353 | 7. *This is boring: NetBSD had [boot loader entropy seed | |
354 | support](https://netbsd.gw.com/cgi-bin/man-cgi?boot+8) since ages!* | |
355 | ||
356 | Yes, NetBSD has that, and the above is inspired by that (note though: this | |
357 | article is about a lot more than that). NetBSD's support is not really safe, | |
358 | since it neither updates the random seed before using it, nor has any | |
359 | safeguards against replicating the same disk image with its random seed on | |
360 | multiple machines (which the 'system token' mentioned above is supposed to | |
361 | address). This means reuse of the same random seed by the boot loader is | |
362 | much more likely. | |
363 | ||
364 | 8. *Why does PID 1 upload the boot loader provided random seed into kernel | |
365 | instead of kernel doing that on its own?* | |
366 | ||
367 | That's a good question. Ideally the kernel would do that on its own, and we | |
368 | wouldn't have to involve userspace in this. | |
369 | ||
370 | 9. *What about non-EFI?* | |
371 | ||
372 | The boot loader random seed logic described above uses EFI variables to pass | |
373 | the seed from the boot loader to the OS. Other systems might have similar | |
374 | functionality though, and it shouldn't be too hard to implement something | |
375 | similar for them. Ideally, we'd have an official way to pass such a seed as | |
376 | part of the `struct boot_params` from the boot loader to the kernel, but | |
377 | this is currently not available. | |
378 | ||
379 | 10. *I use a different boot loader than `systemd-boot`, I'd like to use boot | |
380 | loader random seeds too!* | |
381 | ||
382 | Well, consider just switching to `systemd-boot`, it's worth it. See | |
383 | [systemd-boot(7)](https://www.freedesktop.org/software/systemd/man/systemd-boot.html) | |
384 | for an introduction why. That said, any boot loader can re-implement the | |
385 | logic described above, and can pass a random seed that systemd as PID 1 | |
5c90c67a BF |
386 | will then upload into the kernel's entropy pool. For details see the |
387 | [Boot Loader Interface](BOOT_LOADER_INTERFACE.md) documentation. | |
93f59100 LP |
388 | |
389 | 11. *Why not pass the boot loader random seed via kernel command line instead | |
390 | of as EFI variable?* | |
391 | ||
392 | The kernel command line is accessible to unprivileged processes via | |
393 | `/proc/cmdline`. It's not desirable if unprivileged processes can use this | |
394 | information to possibly gain too much information about the current state | |
395 | of the kernel's entropy pool. | |
396 | ||
18d9cee0 LP |
397 | That said, we actually do implement this with the `systemd.random_seed=` |
398 | kernel command line option. Don't use this outside of testing environments, | |
399 | however, for the aforementioned reasons. | |
400 | ||
93f59100 LP |
401 | 12. *Why doesn't `systemd-boot` rewrite the 'system token' too each time |
402 | when updating the random seed file stored in the ESP?* | |
403 | ||
404 | The system token is stored as persistent EFI variable, i.e. in some form of | |
405 | NVRAM. These memory chips tend be of low quality in many machines, and | |
406 | hence we shouldn't write them too often. Writing them once during | |
407 | installation should generally be OK, but rewriting them on every single | |
408 | boot would probably wear the chip out too much, and we shouldn't risk that. |