Skip to content

Commit c768d5c

Browse files
committed
Add docs/install.md
This needs a lot of explanation. Add a section on how to configure SSH keys. Signed-off-by: Colin Walters <[email protected]>
1 parent 7873787 commit c768d5c

File tree

2 files changed

+163
-7
lines changed

2 files changed

+163
-7
lines changed

README.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,11 @@ First, build a derived container using any container build tooling.
7777
#### Using `bootc install`
7878

7979
The `bootc install` command will write the current container to a disk, and set it up for booting.
80+
In brief, the idea is that every container image shipping `bootc` also comes with a simple
81+
installer that can set a system up to boot from it. Crucially, if you create a
82+
*derivative* container image from a stock OS container image, it also automatically supports `bootc install`.
8083

81-
For example, booting a Fedora-derivative (including CentOS and RHEL) system, whether a cloud guest or a live ISO, you can invoke:
82-
83-
```
84-
$ 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
85-
```
86-
87-
As noted above though, if you create a *derivative* container image, it also automatically supports `bootc install`.
84+
For more information, please see [docs/install.md](docs/install.md).
8885

8986
#### Switching from an existing ostree-based system
9087

docs/install.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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

Comments
 (0)