diff --git a/docs/todo/base-image-version-locking.md b/docs/todo/base-image-version-locking.md new file mode 100644 index 0000000..8c8ac96 --- /dev/null +++ b/docs/todo/base-image-version-locking.md @@ -0,0 +1,146 @@ +# Base Image Package Version Locking + +## Overview + +This design proposes a mechanism for base container images and disk/cloud images to version-lock all installed packages, providing reproducible and secure package management for derived images and systems. + +## Related Work + +See [dnf5#833](https://github.com/rpm-software-management/dnf5/issues/833) which discusses a complementary approach using dependency lockfiles. This proposal takes a different approach focused on image-level version pinning through repository metadata. + +See also [bootc-tracker#59](https://gitlab.com/fedora/bootc/tracker/-/issues/59) which tracks the overall implementation of base image version locking. + +## Core Design + +### IMAGE_VERSION in os-release + +Today Fedora derivative images don't usually set the (relatively new) `IMAGE_VERSION` variable in [os-release](https://man7.org/linux/man-pages/man5/os-release.5.html) +in this proposal they will: + +``` +NAME="Fedora Linux" +VERSION="42" +ID=fedora +VERSION_ID=42 +# Version scheme the same as a "compose" today +IMAGE_VERSION=20251015.0 +... +``` + +The `IMAGE_VERSION` represents a specific snapshot of the base image + repository state that corresponds to the exact package versions installed in the base image, plus additional packages for all default-enabled rpm-md repositories. + +### BASE_IMAGE_VERSION for Derived Images + +Some image-based systems (like Fedora CoreOS) may want to set their own `IMAGE_VERSION` while still benefiting from version locking of the base packages. For these cases, DNF will also recognize `BASE_IMAGE_VERSION` which takes precedence over `IMAGE_VERSION` for repository version locking. + +This allows a derived image to: +- Set `IMAGE_VERSION` to its own version scheme +- Set `BASE_IMAGE_VERSION` to inherit the base image's package snapshot +- Maintain independent versioning while preserving reproducible package installations + +Derived images can also override the base image version by writing to `/etc/dnf/vars/imageversion`, which DNF will use in preference to values from os-release. + +### DNF Integration + +Any client can request a specific version-locked snapshot by appending $imageversion. + +**Current URL:** +``` +metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch +``` + +**URL with version locking:** +``` +metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever-$imageversion&arch=$basearch +``` + +The metalink service will redirect to a pinned, immutable rpm-md repository snapshot that corresponds to the exact state of packages at the time the base image was built. + +## Benefits + +### Reproducibility + +Derived images and systems will install the exact same package versions as the base image, regardless of when the build occurs. This eliminates the common problem where a cached container layer contains outdated packages. + +### Security and Updates + +When a new base image is released with updated `IMAGE_VERSION`, derived images automatically rebuild and pick up all security updates in a controlled manner. This provides: +- Clear update points tied to base image releases +- Ability to validate and test base image updates before adoption +- Audit trail of which package versions are in use + +### Supply Chain Security + +The image version provides a single identifier that pins all package versions, making it easy to: +- Track exactly which packages are in a given deployment +- Coordinate security updates across fleet of systems +- Verify package integrity against known-good snapshots + +### Compatibility with Existing Workflows + +Unlike lockfile approaches, this design: +- Requires no changes to Containerfiles/Dockerfiles +- Works transparently with existing package management commands +- Applies automatically to all derivative images + +## Implementation Considerations + +### Repository Infrastructure + +We propose that these snapshots are NOT stored in the donated "mirror network" but are managed by dedicated cloud-native storage backed by Fedora project sponsors. + +In general an important thing to preserve here is that if a container image (or disk image) is published, the corresponding packages must be available as well. Hence, +what will likely be the best implementation path is to publish the rpm-md snapshots before publishing images, but not mark it as the "latest". + +But the system will work most reliably if the two are co-located; e.g. container images hosted in quay.io could have content served from the same infrastructure. + +One possible implementation approach is to extend [archive-repo-manager](https://pagure.io/releng/archive-repo-manager), which already handles repository archival and versioning. Each time repodata updates, it could also be copied to a versioned name that remains immutable. However, this is just one potential implementation path - other approaches such as storing RPMs as OCI artifacts in a registry are also viable. + +### DNF Changes + +DNF needs to: +1. Check for `imageversion` in the following priority order: + - `/etc/dnf/vars/imageversion` (highest priority, allows derived images to override) + - `BASE_IMAGE_VERSION` from `/usr/lib/os-release` + - `IMAGE_VERSION` from `/usr/lib/os-release` +2. Substitute `$imageversion` in metalink URLs when a version is found +3. Provide clear messaging about which version is being used + +### Override Mechanism + +There are several ways to override or disable version locking: + +1. **One-time override**: Use `dnf --setvar imageversion=latest` to bypass version locking for a single command +2. **Persistent override**: Write a different version to `/etc/dnf/vars/imageversion` +3. **Complete removal**: Remove `$imageversion` from the metalink URL in repository configuration files + +The metalink service may also support the alias `latest` as a value for `$imageversion`, allowing users to override the variable without editing URLs. + +### Mixed Repository Sources + +Third-party and local repositories will not include `$imageversion` in their URLs by default, as they are not part of the versioned snapshot infrastructure. This is expected behavior - only official Fedora repositories that participate in the snapshot system will use `$imageversion`. + +When DNF processes repository configurations: +- Repositories with `$imageversion` in their metalink/baseurl use the locked snapshot +- Repositories without `$imageversion` use their default behavior (typically latest packages) + +This allows mixing locked base packages with latest versions from third-party sources. + +### Error Handling + +If a repository snapshot becomes unavailable (404 or similar error), DNF will fail hard rather than falling back to the latest packages. This is intentional - silently falling back would break the reproducibility guarantee and could introduce unexpected package versions. + +Users must explicitly modify repository configurations to disable version locking if a snapshot is unavailable. + +### Snapshot Lifecycle + +Package snapshots are lifecycle-bound to the base images that reference them. A snapshot must be retained at least as long as any base image using that `IMAGE_VERSION` remains supported or available. + +For example: +- Base image `fedora-bootc:42` with `IMAGE_VERSION=20251015.0` is published +- The corresponding snapshot `fedora-42-20251015.0` must remain available +- When the base image reaches end-of-life or is removed from registries, the snapshot may be retired +- In practice, snapshots should be retained longer than images to support derived images and cached layers + +This binding ensures that any published image can always install its matching package versions, maintaining the reproducibility guarantee throughout the image's lifecycle. +