Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions content/en/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,41 @@ dns = ["172.30.5.1", "9.9.9.9"]
Changing `bridge` or `subnet` affects machines started afterwards; the address
recorded for a machine is reassigned from the new subnet on its next start.

## Signature policies

What the images of a registry must carry, one table per registry (host, or
host:port). The hub's policy is built in (the project's key or the build
workflow's keyless identity, one of them required, its transparency log entry
checked): a table for `hub.nspawn.org` replaces it, and `verify = false` turns
the check off. Every other registry is verified only when it has a table.

```toml
[registries."registry.example.com"]
key = "/etc/nspawn/keys/example.pub" # a cosign public key (PEM); keys = [...] for several
identity = "https://github.com/org/repo/.github/workflows/build.yml@refs/heads/main"
issuer = "https://token.actions.githubusercontent.com" # goes with identity
required = true # default: no verifying signature fails the pull
rekor = true # default: the signature's transparency log entry must verify
trusted_root = "/etc/nspawn/trusted_root.json" # another Sigstore deployment; the public one otherwise

[registries."hub.nspawn.org"]
verify = false # nothing is checked for this registry
```

| Key | Type | Default | Meaning |
| --- | --- | --- | --- |
| `verify` | boolean | `true` | `false` turns verification off for the registry, the hub's built-in policy included. |
| `key`, `keys` | absolute path, list of absolute paths | none | Cosign public keys (PEM) the images may be signed with; `key` and `keys` add up. |
| `identity`, `issuer` | strings, both or neither | none | A keyless signer: the certificate's identity (the SAN, a URI or an email) and the issuer of the token behind it. A table names at least one key or an identity, unless `verify = false`. |
| `required` | boolean | `true` | `false` turns a missing signature into a note and lets the pull go on; a signature that is there but does not verify still fails it. |
| `rekor` | boolean | `true` | `false` trusts the key or certificate alone, without the transparency log entry (a private deployment that keeps no log). A bundle still has to carry a log entry or a timestamp, which cosign always adds. |
| `trusted_root` | absolute path | the public Sigstore root the binary embeds | The `trusted_root.json` of another Sigstore deployment. |

The keys are read by the service when a pull needs them, and checked when it
starts: a key or a trusted root it cannot read is reported in its journal and
fails the pulls of that registry. What the command line was given with
`--registry` chooses the registry, never the policy.

The file is read by the service, so a change takes effect on its next start:
`sudo systemctl restart nspawn.service` (or simply waiting for it to go idle)
picks it up. `--config` on the command line does not reach it: the command line
Expand Down
10 changes: 9 additions & 1 deletion content/en/docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,15 @@ certificate from Fulcio, recorded in the Rekor transparency log), and with the
project's key, whose public half is `cosign.pub` in that repository. The
signatures are stored on the hub next to the image, as OCI referrers of its
digest, so they cover every tag that points to it, and the hub verifies the
key one itself and shows the image as signed. To verify an image yourself:
key one itself and shows the image as signed.

`nspawn pull` verifies them too, since 1.5.0: before a single layer is
downloaded, one of the two signatures has to verify offline against the
project's key and the workflow's identity, both built into nspawn, and an
image without a valid signature is refused (`--no-verify` skips the check for
one command; see [Signed images](/docs/images/#signed-images)). Other
registries get a [policy in the configuration](/docs/configuration/#signature-policies).
To verify an image yourself, with cosign:

```shell
cosign verify \
Expand Down
13 changes: 9 additions & 4 deletions content/en/docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,18 @@ sudo nspawn pull fedora:44
```

```text
hub.nspawn.org/fedora:44: manifest 3f9c1a2b4d5e with 1 layer(s), assembling as overlay
blob 8a1e0c7f3b92: downloading
blob c0de4455aa01: downloading
hub.nspawn.org/fedora:44: signature verified (key 6wiWMtJZCUkV, keyless https://github.com/nspawn/mkosi-definitions/.github/workflows/mkosi.yml@refs/heads/master)
hub.nspawn.org/fedora:44: manifest 9240778b2c77 with 1 layer(s), assembling as overlay
blob f0c1e88e32b3: downloading
blob 28edf9a59c17: downloading
blob f0c1e88e32b3: downloaded
blob 28edf9a59c17: downloaded
image fedora-44 (boot image) is ready: nspawn start fedora-44
```

The blobs went to `/var/lib/nspawn`, the root file system of the machine is
The image's signature was checked first (every image on the hub is signed by
the workflow that builds it; see [Signed images](/docs/images/#signed-images)),
then the blobs went to `/var/lib/nspawn`, the root file system of the machine is
mounted at `/var/lib/machines/fedora-44`, `/etc/systemd/nspawn/fedora-44.nspawn`
holds the settings nspawn boots it with, and a drop-in of
`systemd-nspawn@fedora-44.service` makes the unit call nspawn around its life.
Expand Down
53 changes: 49 additions & 4 deletions content/en/docs/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,60 @@ Docker Hub limits anonymous pulls per address; logging in lifts that. `push`
authenticates before it uploads anything and, when the registry wants
credentials it does not have, says which `login` to run.

## Signed images

Every image on the hub is signed twice by the workflow that builds it
([nspawn/mkosi-definitions](https://github.com/nspawn/mkosi-definitions)) with
cosign: with the project's key, whose public half (`cosign.pub` in that
repository) is built into nspawn, and keyless, with the workflow's own identity
through Sigstore
(`https://github.com/nspawn/mkosi-definitions/.github/workflows/mkosi.yml@refs/heads/master`,
issued by GitHub). The signatures are referrers of the image on the registry:
Sigstore bundles that carry the certificate or the key's hint, the transparency
log entry and a timestamp, so `pull` verifies them offline, with nothing but the
registry consulted, against the Sigstore trusted root the binary embeds. One of
the two has to verify, and the check comes before a single layer is downloaded:

```text
$ sudo nspawn pull fedora:44
hub.nspawn.org/fedora:44: signature verified (key 6wiWMtJZCUkV, keyless https://github.com/nspawn/mkosi-definitions/.github/workflows/mkosi.yml@refs/heads/master)
hub.nspawn.org/fedora:44: manifest 9240778b2c77 with 1 layer(s), assembling as overlay
...
```

An image without a signature (a tag dated before the signing began, or one
pushed by hand with `nspawn push`) is refused with `carries no signature`, and
so is one whose signatures do not verify, with the reason of each. `--no-verify`
on `pull`, `run` and `create` skips the check for that command, like docker's
`--disable-content-trust`; `create` checks only an image it has to pull, never
a local source. `inspect` shows who signed (`signed_by`: `key <hint>, keyless
<identity>`) and when (`signed_at`), which a machine made with `create`
inherits from its source.

Images of other registries are pulled unverified until the
[configuration file](/docs/configuration/#signature-policies) says what they
must carry, one table per registry; the same table can require other keys of
the hub, or turn its check off:

```toml
[registries."hub.nspawn.org"]
verify = false
```

The policies belong to the service: `--registry` on the command line chooses
the registry, never the policy.

## Pulling

```shell
sudo nspawn pull REFERENCE [--name NAME] [--backend BACKEND] [--mode MODE] [--force]
sudo nspawn pull REFERENCE [--name NAME] [--backend BACKEND] [--mode MODE] [--force] [--no-verify]
```

It resolves the reference to the manifest for the host's
platform (image indexes are followed), downloads every layer and the config
blob that is not already in the store, three at a time as docker does, checking
platform (image indexes are followed), checks the image's signatures where its
registry has a policy (the hub's is built in; see
[Signed images](#signed-images)) before anything is downloaded, downloads every
layer and the config blob that is not already in the store, three at a time as docker does, checking
each one against its sha256 digest while it streams (a blob is written next to
its final name and renamed only once verified, so an interrupted download never
passes for a complete one, and a download cut short takes its part file with
Expand All @@ -136,7 +181,7 @@ it), says of each blob when it is downloaded, and then:
with, and the drop-in that makes `systemd-nspawn@NAME.service` call nspawn
around its life;
4. records the image (reference, manifest digest, layers, backend, mode,
network) under `/var/lib/nspawn`, and keeps the manifest and blobs so that
network, who signed it) under `/var/lib/nspawn`, and keeps the manifest and blobs so that
the image can be pushed or cloned later.

An image with the same name is not replaced unless you pass `--force`, and
Expand Down
10 changes: 6 additions & 4 deletions content/en/docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ machines with a docker-like workflow:
[mkosi](https://github.com/systemd/mkosi). Docker Hub and any other registry
work as well: `search` looks on the hub and on Docker Hub at once, and
`login` keeps credentials per registry, the way `docker login` does.
- Layers are downloaded once, verified against their digests and shared between
- An image of the hub is verified against its signature before anything comes
down; layers are downloaded once, verified against their digests and shared between
the machines that use them. A machine's root file system is assembled from
them by a [backend](/docs/images/#backends): an overlayfs mount, a native
`systemd.mstack` directory, or a flat copy. `create` makes more machines from
Expand Down Expand Up @@ -150,6 +151,7 @@ configured by systemd-networkd. See [Networking](/docs/networking/).
- It does not build images by itself: `build` needs
[mkosi](https://github.com/systemd/mkosi) installed on the host.
- It does not sign images: the hub's are signed by the workflow that builds
them (see the [FAQ](/docs/faq/#are-the-images-signed)). Every blob is
checked against the sha256 digest in the manifest while it downloads, and
registries are reached over HTTPS only.
them, and `pull` verifies one of those signatures before it downloads
anything (see [Signed images](/docs/images/#signed-images)). Every blob is
then checked against the sha256 digest in the manifest while it downloads,
and registries are reached over HTTPS only.
20 changes: 13 additions & 7 deletions content/en/docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,12 @@ Forgets the credentials stored for a registry (the hub by default).
## pull

```text
nspawn pull REFERENCE [-n NAME] [--backend BACKEND] [--mode MODE] [-f]
nspawn pull REFERENCE [-n NAME] [--backend BACKEND] [--mode MODE] [-f] [--no-verify]
```

Downloads an image from the hub or another registry and makes it available to
Downloads an image from the hub or another registry, checking its signature
first where the registry has a policy (the hub's is built in; see
[Signed images](/docs/images/#signed-images)), and makes it available to
systemd-machined. On a terminal a bar shows how far each blob got, with its
size, speed and time left; in a pipe or a log only the lines are written.

Expand All @@ -111,6 +113,7 @@ size, speed and time left; in a pipe or a log only the lines are written.
| `--backend auto\|overlay\|flat\|mstack` | How to assemble the image on this host. Default: `auto`, or the `backend` of the configuration file: `overlay`, or `flat` without overlayfs. `mstack` (systemd 261 or newer, managed user namespaces) is experimental. App images are assembled as `overlay` even when `mstack` is chosen. |
| `--mode auto\|boot\|app` | Whether the image boots an init system or runs a single program. Default: `auto`. |
| `-f`, `--force` | Replace an existing image with the same name. |
| `--no-verify` | Skip the signature check: the hub's images are signed and refused without a valid signature, and the configuration can ask the same of other registries; like docker's `--disable-content-trust`. |

## create

Expand All @@ -119,7 +122,7 @@ nspawn create SOURCE NAME [--backend BACKEND] [--network NETWORK]... [--network-
[-p [IP:]HOST:CONTAINER[/udp]]... [--entrypoint PROGRAM] [-e VAR[=VALUE]]...
[-v SOURCE:TARGET[:ro]]... [-l KEY=VALUE]... [--restart POLICY] [-m SIZE] [--cpus N]
[--pids-limit N] [HEALTHCHECK OPTIONS] [OTHER OPTIONS] [--secret SECRET]...
[-f] [-- ARGUMENTS...]
[-f] [--no-verify] [-- ARGUMENTS...]
```

Makes another machine from a local image, like `docker create`, without
Expand All @@ -141,6 +144,7 @@ the image's own name, as `run` does. The layers are shared with the source.
| `--restart`, `-m`, `--cpus`, `--pids-limit` | Restart policy and limits, as for [start](#start). Not inherited from the source. |
| the healthcheck options, the other options, `--secret` | As for [start](#start). Not inherited from the source. |
| `-f`, `--force` | Replace an existing machine with the same name. |
| `--no-verify` | Skip the signature check of an image that has to be pulled (a local source is not checked); like docker's `--disable-content-trust`. |
| `-- ARGUMENTS...` | App images: replace the image's cmd; they follow its entrypoint, as with docker. |

## build
Expand Down Expand Up @@ -243,9 +247,10 @@ nspawn inspect NAME...
Prints everything nspawn knows about machines or images, running or not, as a
JSON array with one object per name, like `docker inspect`: the record (image
reference, digest, backend, mode, networks, addresses, aliases, ports, volumes,
environment, command, labels, restart policy, limits, healthcheck, secrets and
the other flags), for a running machine its state, start time, leader PID, OS
and health, and for a stopped one the exit code of its last run. The keys are
environment, command, labels, restart policy, limits, healthcheck, secrets,
the other flags, and who signed the image and when, as the pull verified it:
`signed_by`, `signed_at`), for a running machine its state, start time, leader
PID, OS and health, and for a stopped one the exit code of its last run. The keys are
those of the
[D-Bus interface](https://github.com/nspawn/nspawn/blob/master/docs/DBUS.md).

Expand Down Expand Up @@ -315,7 +320,7 @@ Boots an image as a machine. Every option is remembered for the next start.

```text
nspawn run [-d] [--rm] [-i] [-t] [-n NAME] [--pull missing|always|never]
[--backend BACKEND] [--mode MODE] [-f] [OPTIONS OF start] [--no-wait]
[--backend BACKEND] [--mode MODE] [-f] [--no-verify] [OPTIONS OF start] [--no-wait]
REFERENCE [COMMAND [ARGUMENT...]]
```

Expand Down Expand Up @@ -346,6 +351,7 @@ image shows its console until it powers off, and Ctrl-C powers it off.
| `--backend auto\|overlay\|flat\|mstack` | How to assemble the machine, as for `pull`. |
| `--mode auto\|boot\|app` | As for `pull`; a mode other than `auto` always pulls. |
| `-f`, `--force` | Make the machine anew when one of that name exists; it must be stopped. |
| `--no-verify` | Skip the signature check of the image, as for `pull`. |
| the options of `start` | `--network`, `--network-alias`, `-p`, `--entrypoint`, `-e`, `-v`, `-l`, `--restart`, `-m`, `--cpus`, `--pids-limit`, the healthcheck options, the other options, `--secret` and `--no-wait` (with `-d` only), as for [start](#start). Options may come before or after the reference, as long as they come before the command. |

## stop
Expand Down
2 changes: 1 addition & 1 deletion hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ params:
privacy_policy: /about/#privacy

# Version of nspawn the documentation describes.
version: 1.4.0
version: 1.5.0
archived_version: false

# In-page links to open issues and suggest changes.
Expand Down