]> git.ipfire.org Git - thirdparty/systemd.git/blob - docs/PORTABLE_SERVICES.md
docs: fix grammar a bit
[thirdparty/systemd.git] / docs / PORTABLE_SERVICES.md
1 ---
2 title: Portable Services Introduction
3 category: Concepts
4 layout: default
5 SPDX-License-Identifier: LGPL-2.1-or-later
6 ---
7
8 # Portable Services Introduction
9
10 systemd (since version 239) supports a concept of "Portable Services".
11 "Portable Services" are a delivery method for system services that uses
12 two specific features of container management:
13
14 1. Applications are bundled. I.e. multiple services, their binaries and all
15 their dependencies are packaged in an image, and are run directly from it.
16
17 2. Stricter default security policies, i.e. sand-boxing of applications.
18
19 The primary tool for interacting with Portable Services is `portablectl`,
20 and they are managed by the `systemd-portabled` service.
21
22 Portable services don't bring anything inherently new to the table. All they do
23 is put together known concepts to cover a specific set of use-cases in a
24 slightly nicer way.
25
26 ## So, what *is* a "Portable Service"?
27
28 A portable service is ultimately just an OS tree, either inside of a directory,
29 or inside a raw disk image containing a Linux file system. This tree is called
30 the "image". It can be "attached" or "detached" from the system. When
31 "attached", specific systemd units from the image are made available on the
32 host system, then behaving pretty much exactly like locally installed system
33 services. When "detached", these units are removed again from the host, leaving
34 no artifacts around (except maybe messages they might have logged).
35
36 The OS tree/image can be created with any tool of your choice. For example, you
37 can use `dnf --installroot=` if you like, or `debootstrap`, the image format is
38 entirely generic, and doesn't have to carry any specific metadata beyond what
39 distribution images carry anyway. Or to say this differently: the image format
40 doesn't define any new metadata as unit files and OS tree directories or disk
41 images are already sufficient, and pretty universally available these days. One
42 particularly nice tool for creating suitable images is
43 [mkosi](https://github.com/systemd/mkosi), but many other existing tools will
44 do too.
45
46 Portable services may also be constructed from layers, similarly to container
47 environments. See [Extension Images](#extension-images) below.
48
49 If you so will, "Portable Services" are a nicer way to manage chroot()
50 environments, with better security, tooling and behavior.
51
52 ## Where's the difference to a "Container"?
53
54 "Container" is a very vague term, after all it is used for
55 systemd-nspawn/LXC-type OS containers, for Docker/rkt-like micro service
56 containers, and even certain 'lightweight' VM runtimes.
57
58 "Portable services" do not provide a fully isolated environment to the payload,
59 like containers mostly intend to. Instead, they are more like regular system
60 services, can be controlled with the same tools, are exposed the same way in
61 all infrastructure, and so on. The main difference is that they use a different
62 root directory than the rest of the system. Hence, the intent is not to run
63 code in a different, isolated environment from the host — like most containers
64 would — but to run it in the same environment, but with stricter access
65 controls on what the service can see and do.
66
67 One point of differentiation: since programs running as "portable services" are
68 pretty much regular system services, they won't run as PID 1 (like they would
69 under Docker), but as normal processes. A corollary of that is that they aren't
70 supposed to manage anything in their own environment (such as the network) as
71 the execution environment is mostly shared with the rest of the system.
72
73 The primary focus use-case of "portable services" is to extend the host system
74 with encapsulated extensions, but provide almost full integration with the rest
75 of the system, though possibly restricted by security knobs. This focus
76 includes system extensions otherwise sometimes called "super-privileged
77 containers".
78
79 Note that portable services are only available for system services, not for
80 user services (i.e. the functionality cannot be used for the stuff
81 bubblewrap/flatpak is focusing on).
82
83 ## Mode of Operation
84
85 If you have a portable service image, maybe in a raw disk image called
86 `foobar_0.7.23.raw`, then attaching the services to the host is as easy as:
87
88 ```
89 # portablectl attach foobar_0.7.23.raw
90 ```
91
92 This command does the following:
93
94 1. It dissects the image, checks and validates the `os-release` file of the
95 image, and looks for all included unit files.
96
97 2. It copies out all unit files with a suffix of `.service`, `.socket`,
98 `.target`, `.timer` and `.path`, whose name begins with the image's name
99 (with `.raw` removed), truncated at the first underscore if there is one.
100 This prefix name generated from the image name must be followed by a ".",
101 "-" or "@" character in the unit name. Or in other words, given the image
102 name of `foobar_0.7.23.raw` all unit files matching
103 `foobar-*.{service|socket|target|timer|path}`,
104 `foobar@.{service|socket|target|timer|path}` as well as
105 `foobar.*.{service|socket|target|timer|path}` and
106 `foobar.{service|socket|target|timer|path}` are copied out. These unit files
107 are placed in `/etc/systemd/system.attached/` (which is part of the normal
108 unit file search path of PID 1, and thus loaded exactly like regular unit
109 files). Within the images the unit files are looked for at the usual
110 locations, i.e. in `/usr/lib/systemd/system/` and `/etc/systemd/system/` and
111 so on, relative to the image's root.
112
113 3. For each such unit file a drop-in file is created. Let's say
114 `foobar-waldo.service` was one of the unit files copied to
115 `/etc/systemd/system.attached/`, then a drop-in file
116 `/etc/systemd/system.attached/foobar-waldo.service.d/20-portable.conf` is
117 created, containing a few lines of additional configuration:
118
119 ```
120 [Service]
121 RootImage=/path/to/foobar.raw
122 Environment=PORTABLE=foobar
123 LogExtraFields=PORTABLE=foobar
124 ```
125
126 4. For each such unit a "profile" drop-in is linked in. This "profile" drop-in
127 generally contains security options that lock down the service. By default
128 the `default` profile is used, which provides a medium level of security.
129 There's also `trusted`, which runs the service with no restrictions, i.e. in
130 the host file system root and with full privileges. The `strict` profile
131 comes with the toughest security restrictions. Finally, `nonetwork` is like
132 `default` but without network access. Users may define their own profiles
133 too (or modify the existing ones).
134
135 And that's already it.
136
137 Note that the images need to stay around (and in the same location) as long as the
138 portable service is attached. If an image is moved, the `RootImage=` line
139 written to the unit drop-in would point to an non-existent path, and break
140 access to the image.
141
142 The `portablectl detach` command executes the reverse operation: it looks for
143 the drop-ins and the unit files associated with the image, and removes them.
144
145 Note that `portablectl attach` won't enable or start any of the units it copies
146 out by default, but `--enable` and `--now` parameter are available as shortcuts.
147 The same is true for the opposite `detach` operation.
148
149 The `portablectl reattach` command combines a `detach` with an `attach`. It is
150 useful in case an image gets upgraded, as it allows performing a `restart`
151 operation on the units instead of `stop` plus `start`, thus providing lower
152 downtime and avoiding losing runtime state associated with the unit such as the
153 file descriptor store.
154
155 ## Requirements on Images
156
157 Note that portable services don't introduce any new image format, but most OS
158 images should just work the way they are. Specifically, the following
159 requirements are made for an image that can be attached/detached with
160 `portablectl`.
161
162 1. It must contain an executable that shall be invoked, along with all its
163 dependencies. Any binary code needs to be compiled for an architecture
164 compatible with the host.
165
166 2. The image must either be a plain sub-directory (or btrfs subvolume)
167 containing the binaries and its dependencies in a classic Linux OS tree, or
168 must be a raw disk image either containing only one, naked file system, or
169 an image with a partition table understood by the Linux kernel with only a
170 single partition defined, or alternatively, a GPT partition table with a set
171 of properly marked partitions following the
172 [Discoverable Partitions Specification](https://uapi-group.org/specifications/specs/discoverable_partitions_specification).
173
174 3. The image must at least contain one matching unit file, with the right name
175 prefix and suffix (see above). The unit file is searched in the usual paths,
176 i.e. primarily /etc/systemd/system/ and /usr/lib/systemd/system/ within the
177 image. (The implementation will check a couple of other paths too, but it's
178 recommended to use these two paths.)
179
180 4. The image must contain an os-release file, either in `/etc/os-release` or
181 `/usr/lib/os-release`. The file should follow the standard format.
182
183 5. The image must contain the files `/etc/resolv.conf` and `/etc/machine-id`
184 (empty files are ok), they will be bind mounted from the host at runtime.
185
186 6. The image must contain directories `/proc/`, `/sys/`, `/dev/`, `/run/`,
187 `/tmp/`, `/var/tmp/` that can be mounted over with the corresponding version
188 from the host.
189
190 7. The OS might require other files or directories to be in place. For example,
191 if the image is built based on glibc, the dynamic loader needs to be
192 available in `/lib/ld-linux.so.2` or `/lib64/ld-linux-x86-64.so.2` (or
193 similar, depending on architecture), and if the distribution implements a
194 merged `/usr/` tree, this means `/lib` and/or `/lib64` need to be symlinks
195 to their respective counterparts below `/usr/`. For details see your
196 distribution's documentation.
197
198 Note that images created by tools such as `debootstrap`, `dnf --installroot=`
199 or `mkosi` generally satisfy all of the above. If you wonder what the most
200 minimal image would be that complies with the requirements above, it could
201 consist of this:
202
203 ```
204 /usr/bin/minimald # a statically compiled binary
205 /usr/lib/systemd/system/minimal-test.service # the unit file for the service, with ExecStart=/usr/bin/minimald
206 /usr/lib/os-release # an os-release file explaining what this is
207 /etc/resolv.conf # empty file to mount over with host's version
208 /etc/machine-id # ditto
209 /proc/ # empty directory to use as mount point for host's API fs
210 /sys/ # ditto
211 /dev/ # ditto
212 /run/ # ditto
213 /tmp/ # ditto
214 /var/tmp/ # ditto
215 ```
216
217 And that's it.
218
219 Note that qualifying images do not have to contain an init system of their
220 own. If they do, it's fine, it will be ignored by the portable service logic,
221 but they generally don't have to, and it might make sense to avoid any, to keep
222 images minimal.
223
224 If the image is writable, and some of the files or directories that are
225 overmounted from the host do not exist yet they will be automatically created.
226 On read-only, immutable images (e.g. `erofs` or `squashfs` images) all files
227 and directories to over-mount must exist already.
228
229 Note that as no new image format or metadata is defined, it's very
230 straightforward to define images than can be made use of in a number of
231 different ways. For example, by using `mkosi -b` you can trivially build a
232 single, unified image that:
233
234 1. Can be attached as portable service, to run any container services natively
235 on the host.
236
237 2. Can be run as OS container, using `systemd-nspawn`, by booting the image
238 with `systemd-nspawn -i -b`.
239
240 3. Can be booted directly as VM image, using a generic VM executor such as
241 `virtualbox`/`qemu`/`kvm`
242
243 4. Can be booted directly on bare-metal systems.
244
245 Of course, to facilitate 2, 3 and 4 you need to include an init system in the
246 image. To facilitate 3 and 4 you also need to include a boot loader in the
247 image. As mentioned, `mkosi -b` takes care of all of that for you, but any
248 other image generator should work too.
249
250 The
251 [os-release(5)](https://www.freedesktop.org/software/systemd/man/os-release.html)
252 file may optionally be extended with a `PORTABLE_PREFIXES=` field listing all
253 supported portable service prefixes for the image (see above). This is useful
254 for informational purposes (as it allows recognizing portable service images
255 from their contents as such), but is also useful to protect the image from
256 being used under a wrong name and prefix. This is particularly relevant if the
257 images are cryptographically authenticated (via Verity or a similar mechanism)
258 as this way the (not necessarily authenticated) image file name can be
259 validated against the (authenticated) image contents. If the field is not
260 specified the image will work fine, but is not necessarily recognizable as
261 portable service image, and any set of units included in the image may be
262 attached, there are no restrictions enforced.
263
264 ## Extension Images
265
266 Portable services can be delivered as one or multiple images that extend the base
267 image, and are combined with OverlayFS at runtime, when they are attached. This
268 enables a workflow that splits the base 'runtime' from the daemon, so that multiple
269 portable services can share the same 'runtime' image (libraries, tools) without
270 having to include everything each time, with the layering happening only at runtime.
271 The `--extension` parameter of `portablectl` can be used to specify as many upper
272 layers as desired. On top of the requirements listed in the previous section, the
273 following must be also be observed:
274
275 1. The base/OS image must contain an `os-release file`, either in `/etc/os-release`
276 or `/usr/lib/os-release`, in the standard format.
277
278 2. The upper extension images must contain an extension-release file in
279 `/usr/lib/extension-release.d/`, with an `ID=` and `SYSEXT_LEVEL=`/`VERSION_ID=`
280 matching the base image.
281
282 3. The base/OS image does not need to have any unit files.
283
284 4. The upper extension images must contain at least one matching unit file
285 each, with the right name prefix and suffix (see above).
286
287 5. As with the base/OS image, each upper extension image must be a plain
288 sub-directory, btrfs subvolume, or a raw disk image.
289
290 ```
291 # portablectl attach --extension foobar_0.7.23.raw debian-runtime_11.1.raw foobar
292 # portablectl attach --extension barbaz_7.0.23/ debian-runtime_11.1.raw barbaz
293 ```
294
295 ## Execution Environment
296
297 Note that the code in portable service images is run exactly like regular
298 services. Hence there's no new execution environment to consider. And, unlike
299 Docker would do it, as these are regular system services they aren't run as PID
300 1 either, but with regular PID values.
301
302 ## Access to host resources
303
304 If services shipped with this mechanism shall be able to access host resources
305 (such as files or AF_UNIX sockets for IPC), use the normal `BindPaths=` and
306 `BindReadOnlyPaths=` settings in unit files to mount them in. In fact, the
307 `default` profile mentioned above makes use of this to ensure
308 `/etc/resolv.conf`, the D-Bus system bus socket or write access to the logging
309 subsystem are available to the service.
310
311 ## Instantiation
312
313 Sometimes it makes sense to instantiate the same set of services multiple
314 times. The portable service concept does not introduce a new logic for this. It
315 is recommended to use the regular systemd unit templating for this, i.e. to
316 include template units such as `foobar@.service`, so that instantiation is as
317 simple as:
318
319 ```
320 # portablectl attach foobar_0.7.23.raw
321 # systemctl enable --now foobar@instancea.service
322 # systemctl enable --now foobar@instanceb.service
323
324 ```
325
326 The benefit of this approach is that templating works exactly the same for
327 units shipped with the OS itself as for attached portable services.
328
329 ## Immutable images with local data
330
331 It's a good idea to keep portable service images read-only during normal
332 operation. In fact, all but the `trusted` profile will default to this kind of
333 behaviour, by setting the `ProtectSystem=strict` option. In this case writable
334 service data may be placed on the host file system. Use `StateDirectory=` in
335 the unit files to enable such behaviour and add a local data directory to the
336 services copied onto the host.
337
338 ## Links
339
340 [`portablectl(1)`](https://www.freedesktop.org/software/systemd/man/portablectl.html)<br>
341 [`systemd-portabled.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-portabled.service.html)<br>
342 [Walkthrough for Portable Services](https://0pointer.net/blog/walkthrough-for-portable-services.html)<br>
343 [Repo with examples](https://github.com/systemd/portable-walkthrough)