|
1 | 1 | //! # APIs bridging OSTree and container images |
2 | 2 | //! |
3 | | -//! This module contains APIs to bidirectionally map between a single OSTree commit and a container image wrapping it. |
4 | | -//! Because container images are just layers of tarballs, this builds on the [`crate::tar`] module. |
| 3 | +//! This module provides the core infrastructure for bidirectionally mapping between |
| 4 | +//! OCI/Docker container images and OSTree repositories. It enables bootable container |
| 5 | +//! images to be fetched from registries, stored efficiently, and deployed as ostree |
| 6 | +//! commits. |
5 | 7 | //! |
6 | | -//! To emphasize this, the current high level model is that this is a one-to-one mapping - an ostree commit |
7 | | -//! can be exported (wrapped) into a container image, which will have exactly one layer. Upon import |
8 | | -//! back into an ostree repository, all container metadata except for its digested checksum will be discarded. |
| 8 | +//! ## Overview |
| 9 | +//! |
| 10 | +//! Container images are fundamentally layers of tarballs. This module leverages the |
| 11 | +//! [`crate::tar`] module to import container layers as ostree content, and exports |
| 12 | +//! ostree commits back to container images. The key insight is that ostree's |
| 13 | +//! content-addressed object storage maps naturally to OCI layer deduplication. |
| 14 | +//! |
| 15 | +//! When a container image is imported ("pulled"), each layer becomes an ostree commit. |
| 16 | +//! These layer commits are then merged into a single "merge commit" that represents |
| 17 | +//! the complete filesystem state. This merge commit is what gets deployed as a |
| 18 | +//! bootable system. |
| 19 | +//! |
| 20 | +//! ## On-Disk Storage Structure |
| 21 | +//! |
| 22 | +//! Container images are stored in the ostree repository (typically `/sysroot/ostree/repo/`) |
| 23 | +//! using a structured reference (ref) namespace: |
| 24 | +//! |
| 25 | +//! ### Reference Namespace |
| 26 | +//! |
| 27 | +//! - **`ostree/container/blob/<escaped-digest>`**: Each OCI layer is stored as a |
| 28 | +//! separate ostree commit. The digest (e.g., `sha256:abc123...`) is escaped using |
| 29 | +//! [`crate::refescape`] to be valid as an ostree ref. For example: |
| 30 | +//! `ostree/container/blob/sha256_3A_abc123...` |
| 31 | +//! |
| 32 | +//! - **`ostree/container/image/<escaped-image-reference>`**: Points to the "merge |
| 33 | +//! commit" for a pulled image. The image reference (e.g., `docker://quay.io/org/image:tag`) |
| 34 | +//! is escaped similarly. This is the ref that deployments point to. |
| 35 | +//! |
| 36 | +//! - **`ostree/container/baseimage/<project>/<index>`**: Used to protect base images |
| 37 | +//! from garbage collection. Tooling that builds derived images locally should write |
| 38 | +//! refs under this prefix to prevent the base layers from being pruned. |
| 39 | +//! |
| 40 | +//! ### Layer Storage |
| 41 | +//! |
| 42 | +//! Each container layer is stored as an ostree commit with a special structure: |
| 43 | +//! |
| 44 | +//! - **OSTree "chunk" layers**: Layers that are part of the base ostree commit use |
| 45 | +//! the "object set" format - the filenames in the commit *are* the object checksums. |
| 46 | +//! This enables efficient reconstruction of the original ostree commit. |
| 47 | +//! |
| 48 | +//! - **Derived layers**: Non-ostree layers (e.g., from `RUN` commands in a Containerfile) |
| 49 | +//! are imported as regular filesystem trees and stored as standard ostree commits. |
| 50 | +//! |
| 51 | +//! ### The Merge Commit |
| 52 | +//! |
| 53 | +//! The merge commit (`ostree/container/image/...`) combines all layers into a single |
| 54 | +//! filesystem tree. It contains critical metadata in its commit metadata: |
| 55 | +//! |
| 56 | +//! - `ostree.manifest-digest`: The OCI manifest digest (e.g., `sha256:...`) |
| 57 | +//! - `ostree.manifest`: The complete OCI manifest as JSON |
| 58 | +//! - `ostree.container.image-config`: The OCI image configuration as JSON |
| 59 | +//! |
| 60 | +//! This metadata enables round-tripping: an imported image can be re-exported with |
| 61 | +//! its original manifest structure preserved. |
| 62 | +//! |
| 63 | +//! ## Import Flow |
| 64 | +//! |
| 65 | +//! The import process (implemented in [`store::ImageImporter`]) follows these steps: |
| 66 | +//! |
| 67 | +//! 1. **Manifest fetch**: Contact the registry via containers-image-proxy (skopeo) |
| 68 | +//! to retrieve the image manifest and configuration. |
| 69 | +//! |
| 70 | +//! 2. **Layout parsing**: Analyze the manifest to identify: |
| 71 | +//! - The base ostree layer (identified by the `ostree.final-diffid` label) |
| 72 | +//! - Component/chunk layers (split object sets) |
| 73 | +//! - Derived layers (non-ostree content) |
| 74 | +//! |
| 75 | +//! 3. **Layer caching check**: For each layer, check if an ostree ref already exists |
| 76 | +//! for that digest. Cached layers are skipped, enabling efficient incremental updates. |
| 77 | +//! |
| 78 | +//! 4. **Layer import**: For uncached layers: |
| 79 | +//! - Fetch the compressed tarball from the registry |
| 80 | +//! - Decompress and parse the tar stream |
| 81 | +//! - Import content into ostree (handling xattrs via `bare-split-xattrs` format) |
| 82 | +//! - Create an ostree commit and write the layer ref |
| 83 | +//! |
| 84 | +//! 5. **Merge commit creation**: Overlay all layers (processing OCI whiteout files) |
| 85 | +//! to create a unified filesystem tree. Apply SELinux labeling if needed. |
| 86 | +//! Store manifest/config metadata and write the image ref. |
| 87 | +//! |
| 88 | +//! 6. **Garbage collection**: Prune layer refs that are no longer referenced by any |
| 89 | +//! image or deployment. |
| 90 | +//! |
| 91 | +//! ## Tar Stream Format |
| 92 | +//! |
| 93 | +//! The tar format used for ostree layers is documented in [`crate::tar`]. Key points: |
| 94 | +//! |
| 95 | +//! - Uses `bare-split-xattrs` repository mode to handle extended attributes |
| 96 | +//! - XAttrs are stored in separate `.file-xattrs` objects, avoiding tar xattr complexity |
| 97 | +//! - `/etc` in container images maps to `/usr/etc` in ostree (the "3-way merge" location) |
| 98 | +//! - Hardlinks are used for deduplication within layers |
| 99 | +//! |
| 100 | +//! ## Connection to Deployments |
| 101 | +//! |
| 102 | +//! When bootc deploys an image, it creates an ostree deployment whose "origin" file |
| 103 | +//! references the container image. The origin contains: |
| 104 | +//! |
| 105 | +//! - The [`OstreeImageReference`] specifying the image and signature verification method |
| 106 | +//! - The merge commit checksum |
| 107 | +//! |
| 108 | +//! On subsequent boots, bootc can compare the deployed commit against the registry |
| 109 | +//! manifest to detect available updates. |
9 | 110 | //! |
10 | 111 | //! ## Signatures |
11 | 112 | //! |
12 | | -//! OSTree supports GPG and ed25519 signatures natively, and it's expected by default that |
13 | | -//! when booting from a fetched container image, one verifies ostree-level signatures. |
14 | | -//! For ostree, a signing configuration is specified via an ostree remote. In order to |
15 | | -//! pair this configuration together, this library defines a "URL-like" string schema: |
| 113 | +//! OSTree supports GPG and ed25519 signatures natively. When fetching container images, |
| 114 | +//! signature verification can be configured via [`SignatureSource`]: |
| 115 | +//! |
| 116 | +//! - `OstreeRemote(name)`: Verify using the named ostree remote's keyring |
| 117 | +//! - `ContainerPolicy`: Defer to containers-policy.json (requires explicit allow) |
| 118 | +//! - `ContainerPolicyAllowInsecure`: Use containers-policy.json defaults (not recommended) |
| 119 | +//! |
| 120 | +//! This library defines a URL-like schema to combine signature verification with |
| 121 | +//! image references: |
| 122 | +//! |
| 123 | +//! - `ostree-remote-registry:<remotename>:<containerimage>` - Verify via ostree remote |
| 124 | +//! - `ostree-image-signed:<transport>:<image>` - Use container policy |
| 125 | +//! - `ostree-unverified-registry:<image>` - No verification (not recommended) |
| 126 | +//! |
| 127 | +//! Example: `ostree-remote-registry:fedora:quay.io/fedora/fedora-bootc:latest` |
| 128 | +//! |
| 129 | +//! See [`OstreeImageReference`] for parsing and generating these strings. |
| 130 | +//! |
| 131 | +//! ## Layering and Derived Images |
| 132 | +//! |
| 133 | +//! Container image layering is fully supported. A typical bootable image structure: |
| 134 | +//! |
| 135 | +//! 1. **Base ostree layer**: Contains the core OS as an ostree commit |
| 136 | +//! 2. **Chunk layers**: Split objects for efficient updates (optional) |
| 137 | +//! 3. **Derived layers**: Additional content from Containerfile `RUN` commands |
| 138 | +//! |
| 139 | +//! The `ostree.final-diffid` label in the image configuration marks where the |
| 140 | +//! ostree content ends and derived content begins. This enables: |
16 | 141 | //! |
17 | | -//! `ostree-remote-registry:<remotename>:<containerimage>` |
| 142 | +//! - Efficient layer sharing between images with the same base |
| 143 | +//! - Proper SELinux labeling of derived content using the base policy |
| 144 | +//! - Round-trip export preserving the layer structure |
18 | 145 | //! |
19 | | -//! A concrete instantiation might be e.g.: `ostree-remote-registry:fedora:quay.io/coreos/fedora-coreos:stable` |
| 146 | +//! ## Key Types |
20 | 147 | //! |
21 | | -//! To parse and generate these strings, see [`OstreeImageReference`]. |
| 148 | +//! - [`Transport`]: OCI/Docker transport (registry, oci-dir, containers-storage, etc.) |
| 149 | +//! - [`ImageReference`]: Container image reference with transport |
| 150 | +//! - [`OstreeImageReference`]: Image reference plus signature verification method |
| 151 | +//! - [`SignatureSource`]: How to verify image signatures |
| 152 | +//! - [`store::ImageImporter`]: Main import orchestrator |
| 153 | +//! - [`store::PreparedImport`]: Analysis of layers to fetch |
| 154 | +//! - [`store::LayeredImageState`]: State of a pulled image |
| 155 | +//! - [`ManifestDiff`]: Comparison between two image manifests |
22 | 156 | //! |
23 | | -//! ## Layering |
| 157 | +//! ## Submodules |
24 | 158 | //! |
25 | | -//! A key feature of container images is support for layering. At the moment, support |
26 | | -//! for this is [planned but not implemented](https://github.com/ostreedev/ostree-rs-ext/issues/12). |
| 159 | +//! - [`store`]: Core storage and import logic |
| 160 | +//! - [`deploy`]: Integration with ostree deployments |
| 161 | +//! - [`skopeo`]: Skopeo subprocess management for registry operations |
27 | 162 |
|
28 | 163 | use anyhow::anyhow; |
29 | 164 | use cap_std_ext::cap_std; |
|
0 commit comments