|
| 1 | +# Understanding `bootc install` |
| 2 | + |
| 3 | +A key goal of the bootc project is to think of bootable operating systems |
| 4 | +as container images. Docker/OCI container images are just tarballs |
| 5 | +wrapped with some JSON. But in order to boot a system (whether on bare metal |
| 6 | +or virtualized), one needs a few key components: |
| 7 | + |
| 8 | +- bootloader |
| 9 | +- kernel (and optionally initramfs) |
| 10 | +- root filesystem (xfs/ext4/btrfs etc.) |
| 11 | + |
| 12 | +The Linux kernel (and optionally initramfs) is embedded in the container image; the canonical location |
| 13 | +is `/usr/lib/modules/$kver/vmlinuz`, and the initramfs should be in `initramfs.img` |
| 14 | +in that directory. |
| 15 | + |
| 16 | +The `bootc install` command bridges the two worlds of a standard runnable OCI image |
| 17 | +and a bootable system by running tooling |
| 18 | +logic embedded in the container image to create the filesystem and |
| 19 | +bootloader setup dynamically, using tools already embedded in the container |
| 20 | +image. This requires running the container via `--privileged`; it uses |
| 21 | +the running Linux kernel to write the file content from the running container image; |
| 22 | +not the kernel inside the container. |
| 23 | + |
| 24 | +However nothing *else* (external) is required to perform a basic installation |
| 25 | +to disk. This is motivated by experience gained from the Fedora CoreOS |
| 26 | +project where today the expectation is that one boots from a pre-existing disk |
| 27 | +image (AMI, qcow2, etc) or use [coreos-installer](https://github.com/coreos/coreos-installer) |
| 28 | +for many bare metal setups. But the problem is that coreos-installer |
| 29 | +is oriented to installing raw disk images. This means that if |
| 30 | +one creates a custom derived container, then it's required for |
| 31 | +one to also generate a raw disk image to install. This is a large |
| 32 | +ergonomic hit. |
| 33 | + |
| 34 | +With `bootc install`, no extra steps are required. Every container |
| 35 | +image comes with a basic installer. |
| 36 | + |
| 37 | +## Executing `bootc install` |
| 38 | + |
| 39 | +The installation command must be run from the container image |
| 40 | +that will be installed, using `--privileged` and a few |
| 41 | +other options. |
| 42 | + |
| 43 | +Here's an example: |
| 44 | + |
| 45 | +``` |
| 46 | +$ podman run --privileged --pid=host --net=none --security-opt label=type:unconfined_t ghcr.io/cgwalters/c9s-oscore bootc install --target-no-signature-verification /path/to/disk |
| 47 | +``` |
| 48 | + |
| 49 | +Note that while `--privileged` is used, this command will not |
| 50 | +perform any destructive action on the host system. |
| 51 | + |
| 52 | +The `--pid=host --security-opt label=type:unconfined_t` today |
| 53 | +make it more convenient for bootc to perform some privileged |
| 54 | +operations; in the future these requirement may be dropped. |
| 55 | + |
| 56 | +The `--net=none` argument is just to emphasize the fact that |
| 57 | +an installation by default is not fetching anything else external |
| 58 | +from the network - the content to be installed |
| 59 | +*is the running container image content*. |
| 60 | + |
| 61 | +### Note: Today `bootc install` has a host requirement on `skopeo` |
| 62 | + |
| 63 | +The one exception to host requirements today is that the host must |
| 64 | +have `skopeo` installed. This is a bug; more information in [this issue](https://github.com/containers/bootc/issues/81). |
| 65 | + |
| 66 | + |
| 67 | +## Installing an "unconfigured" image |
| 68 | + |
| 69 | +The bootc project aims to support generic/general-purpose operating |
| 70 | +systems and distributions that will ship unconfigured images. An |
| 71 | +unconfigured image does not have a default password or SSH key, etc. |
| 72 | + |
| 73 | +There are two fundamental ways to handle this: |
| 74 | + |
| 75 | +### Using cloud-init type flows |
| 76 | + |
| 77 | +Some operating systems may come with `cloud-init` or similar tools |
| 78 | +that know how to e.g. inject SSH keys or external configuration. |
| 79 | + |
| 80 | +Other tools in this space are: |
| 81 | + |
| 82 | +- [systemd-firstboot](https://www.freedesktop.org/software/systemd/man/systemd-firstboot.html) |
| 83 | +- [gnome-initial-setup](https://gitlab.gnome.org/GNOME/gnome-initial-setup) |
| 84 | + |
| 85 | +The general idea here is that things like users, passwords and ssh keys |
| 86 | +are dynamically created on first boot (and in general managed per-system); |
| 87 | +the configuration comes from a place *external* to the image. |
| 88 | + |
| 89 | +### Injecting configuration into a custom image |
| 90 | + |
| 91 | +But a new super-power with `bootc` is that you can also easily instead |
| 92 | +create a derived container that injects your desired configuration, |
| 93 | +alongside any additional executable code (packages, etc). |
| 94 | + |
| 95 | +The expectation is that most operating systems will be designed such |
| 96 | +that user state i.e. `/root` and `/home` will be on a separate persistent data store. |
| 97 | +For example, in the default ostree model, `/root` is `/var/roothome` |
| 98 | +and `/home` is `/var/home`. Content in `/var` cannot be shipped |
| 99 | +in the image - it is per machine state. |
| 100 | + |
| 101 | +#### Injecting SSH keys in a container image |
| 102 | + |
| 103 | +In this example, we will configure OpenSSH to read the |
| 104 | +set of authorized keys for the root user from content |
| 105 | +that lives in `/usr` (i.e. is owned by the container image). |
| 106 | +We will also create a `/usr/etc-system` directory which is intentionally distinct |
| 107 | +from the default ostree `/etc` which may be locally writable. |
| 108 | + |
| 109 | +The `AuthorizedKeysFile` invocation below then configures sshd to look |
| 110 | +for keys in this location. |
| 111 | + |
| 112 | +``` |
| 113 | +FROM ghcr.io/cgwalters/c9s-oscore |
| 114 | +RUN mkdir -p /usr/etc-system/ && \ |
| 115 | + echo 'AuthorizedKeysFile /usr/etc-system/%u.keys' >> /etc/ssh/sshd_config.d/30-auth-system.conf && \ |
| 116 | + echo 'ssh-ed25519 AAAAC3Nza... [email protected]' > /usr/etc-system/root.keys && chmod 0600 /usr/etc-system/keys && \ |
| 117 | + ostree container commit |
| 118 | +``` |
| 119 | + |
| 120 | +A key point here is that now the set of authorized keys is "owned" |
| 121 | +by the container image - it will be read-only at runtime because |
| 122 | +the files are underneath `/usr`. To rotate or change the set of keys, |
| 123 | +one would build a new container image. Client systems using `bootc upgrade` |
| 124 | +will transactionally update to this new system state. |
| 125 | + |
| 126 | + |
| 127 | +## More advanced installation |
| 128 | + |
| 129 | +The basic `bootc install` logic is really a pretty small (but opinionated) wrapper |
| 130 | +for a set of lower level tools that can also be invoked independently. |
| 131 | + |
| 132 | +The `bootc install` command is effectively: |
| 133 | + |
| 134 | +- `mkfs.$fs /dev/disk` |
| 135 | +- `mount /dev/disk /mnt` |
| 136 | +- `bootc install-to-filesystem --karg=root=UUID=<uuid of /mnt> --imgref $self /mnt` |
| 137 | + |
| 138 | +There may be a bit more involved here; for example configuring |
| 139 | +`--block-setup tpm2-luks` will configure the root filesystem |
| 140 | +with LUKS bound to the TPM2 chip, currently via [systemd-cryptenroll](https://www.freedesktop.org/software/systemd/man/systemd-cryptenroll.html#). |
| 141 | + |
| 142 | +Some OS/distributions may not want to enable it at all; it |
| 143 | +can be configured off at build time via Cargo features. |
| 144 | + |
| 145 | +### Using `bootc install-to-filesystem` |
| 146 | + |
| 147 | +As noted above, there is also `bootc install-to-filesystem`, which allows |
| 148 | +an arbitrary process to create the root filesystem. |
| 149 | + |
| 150 | +The usual expected way for an external storage system to work |
| 151 | +is to provide `root=<UUID>` type kernel arguments. At the current |
| 152 | +time a separate `/boot` filesystem is also required (mainly to enable LUKS) |
| 153 | +so you will also need to provide e.g. `--boot-mount-spec UUID=...`. |
| 154 | + |
| 155 | +The `bootc install-to-filesystem` command allows an operating |
| 156 | +system or distribution to ship a separate installer that creates more complex block |
| 157 | +storage or filesystem setups, but reuses the "top half" of the logic. |
| 158 | +For example, a goal is to change [Anaconda](https://github.com/rhinstaller/anaconda/) |
| 159 | +to use this. |
0 commit comments