diff --git a/Containerfile b/Containerfile index 90bad06..f36d00a 100644 --- a/Containerfile +++ b/Containerfile @@ -1,69 +1,92 @@ -# In order to make a base image as part of a Dockerfile, this container build uses -# nested containerization, so you must build with e.g. -# podman build --security-opt=label=disable --cap-add=all --device /dev/fuse <...> +# Multi-stage build for Debian minimal bootc base images +# Stage 1: Repository setup with apt-cache-ng proxy +FROM debian:sid AS repos +# Build argument for apt-cache-ng proxy (can be empty to disable) +ARG APT_CACHER_NG_PROXY="http://192.168.1.101:3142" +# Copy our repository configuration +COPY debian.repo /etc/apt/sources.list.d/ +# Replace the proxy URL placeholder with the actual value +RUN if [ -n "$APT_CACHER_NG_PROXY" ]; then \ + sed -i "s|__PROXY_URL__|$APT_CACHER_NG_PROXY|g" /etc/apt/sources.list.d/debian.repo; \ + echo "Acquire::http::Proxy \"$APT_CACHER_NG_PROXY\";" > /etc/apt/apt.conf.d/99proxy; \ + else \ + # If no proxy, replace with direct URLs + sed -i "s|__PROXY_URL__/debian|http://deb.debian.org/debian|g" /etc/apt/sources.list.d/debian.repo; \ + sed -i "s|__PROXY_URL__/debian-security|http://security.debian.org/debian-security|g" /etc/apt/sources.list.d/debian.repo; \ + fi -# NOTE: This container build will output a single giant layer. It is strongly recommended -# to run the "rechunker" on the output of this build, see -# https://coreos.github.io/rpm-ostree/experimental-build-chunked-oci/ +# Stage 2: Builder image with tools +FROM debian:sid AS builder +# Copy repository configuration from repos stage +COPY --from=repos /etc/apt/sources.list.d/ /etc/apt/sources.list.d/ +COPY --from=repos /etc/apt/apt.conf.d/ /etc/apt/apt.conf.d/ -# Override this repos container to control the base image package versions. For -# example, podman build --from=quay.io/fedora/fedora:41 will get you a system -# that uses Fedora 41 packages. Or inject arbitrary yum repos (COPR, etc) here. -# -# Note we also support --build-arg REPOS_IMAGE=quay.io/fedora/fedora:41 here -# since konflux doesn't yet support --from. -ARG REPOS_IMAGE=quay.io/fedora/fedora:rawhide -ARG BUILDER_IMAGE=quay.io/fedora/fedora:rawhide -FROM $REPOS_IMAGE as repos +# Install build dependencies (excluding apt-ostree since we'll copy it) +RUN apt-get update && apt-get install -y \ + selinux-policy-default \ + python3 \ + polkitd \ + pkexec \ + libpolkit-gobject-1-0 \ + ostree \ + && rm -rf /var/lib/apt/lists/* -# BOOTSTRAPPING: This can be any image that has rpm-ostree, selinux-policy-targeted -# and python3 (for bootc-base-imagectl). -FROM $BUILDER_IMAGE as builder -RUN dnf -y install rpm-ostree selinux-policy-targeted python3 -ARG MANIFEST=fedora-standard -# The input git repository has .repo files committed to git rpm-ostree has historically -# emphasized that. But here, we are fetching the repos from the container base image. -# So copy the source, and delete the hardcoded ones in git, and use the container base -# image ones. We can drop the ones commited to git when we hard switch to Containerfile. -COPY . /src -# Avoid umask/permission leakage from the outer environment; ref e.g. -# - https://github.com/coreos/coreos-assembler/pull/4277 -# - https://gitlab.com/fedora/bootc/base-images/-/merge_requests/254 -# This invocation preserves only the executable bit, and specifically we want to remove: -# - setuid/setgid -# - world writability -# NOTE: This adds world-readability, which is what we intend here as all the content -# is public; there's no secrets in our container build. -RUN chmod -R a=rX,u+w /src -WORKDIR /src -RUN rm -vf /src/*.repo -RUN --mount=type=cache,rw,id=bootc-base-image-cache,target=/cache \ - --mount=type=bind,rw,from=repos,src=/,dst=/repos </dev/null -# Run the build script in the same way we expect custom images to do, and also -# "re-inject" the manifests into the target, so secondary container builds can use it. -/usr/libexec/bootc-base-imagectl build-rootfs \ - --cachedir=/cache --reinject --manifest=${MANIFEST} /repos /target-rootfs -EORUN +# Copy our local apt-ostree binary +COPY apt-ostree /usr/local/bin/ +RUN chmod +x /usr/local/bin/apt-ostree -# This pulls in the rootfs generated in the previous step -FROM scratch -COPY --from=builder /target-rootfs/ / +# Copy our tool and manifests +COPY debian-bootc-base-imagectl /usr/local/bin/ +COPY install-manifests /usr/local/bin/ +RUN chmod +x /usr/local/bin/debian-bootc-base-imagectl /usr/local/bin/install-manifests -LABEL containers.bootc 1 -# This is an ad-hoc way for us to reference bootc-image-builder in -# a way that in theory client tooling can inspect and find. Today -# it isn't widely used. -LABEL bootc.diskimage-builder quay.io/centos-bootc/bootc-image-builder -# https://pagure.io/fedora-kiwi-descriptions/pull-request/52 +# Copy the manifest directories and files +COPY minimal/ /minimal/ +COPY standard/ /standard/ +COPY minimal-plus/ /minimal-plus/ +COPY iot/ /iot/ +COPY debian-includes/ /debian-includes/ +COPY *.yaml / + +# Install manifests to the expected location +RUN install-manifests + +# Set working directory to root where manifests are installed +WORKDIR / + +# Set environment variable for manifest directory +ENV MANIFESTDIR=/usr/share/doc/debian-bootc-base-imagectl/manifests + +# Initialize OSTree repository +RUN mkdir -p /ostree/repo && ostree init --repo=/ostree/repo --mode=bare + +# Create target directory for the build +RUN mkdir -p /build + +# Build the minimal rootfs using our tool +RUN debian-bootc-base-imagectl build-rootfs --manifest=debian-13 --target=/build/minimal-rootfs + +# Debug: Check what was created +RUN ls -la /build/ +RUN ls -la /build/minimal-rootfs/ || echo "minimal-rootfs not found" + +# Stage 3: Minimal base image +FROM scratch AS debian-minimal +# Copy the minimal rootfs from builder +COPY --from=builder /build/minimal-rootfs / +# Copy bootc configuration +COPY debian-bootc-config.json /etc/debian-bootc-config.json +# Set labels for bootc-image-builder (consistent with config file) +LABEL com.debian.bootc=true +LABEL ostree.bootable=true +LABEL containers.bootc=1 +LABEL bootc.diskimage-builder=quay.io/centos-bootc/bootc-image-builder +LABEL debian.id=debian +LABEL debian.version-id=sid +LABEL org.opencontainers.image.title="Debian Minimal Bootc Base Image" +LABEL org.opencontainers.image.description="Minimal Debian base image for bootc ecosystem" +LABEL org.opencontainers.image.vendor="Debian Project" +LABEL org.opencontainers.image.source="https://github.com/debian/bootc-base-images" +# Set environment and stop signal from config ENV container=oci -# Make systemd the default STOPSIGNAL SIGRTMIN+3 -CMD ["/sbin/init"] diff --git a/Containerfile.enhanced b/Containerfile.enhanced new file mode 100644 index 0000000..3bf7d57 --- /dev/null +++ b/Containerfile.enhanced @@ -0,0 +1,20 @@ +# Enhanced Debian bootc base image with real packages +FROM scratch AS debian-enhanced +# Copy the enhanced rootfs with real Debian packages +COPY enhanced-rootfs / +# Copy bootc configuration +COPY debian-bootc-config.json /etc/debian-bootc-config.json +# Set labels for bootc-image-builder (consistent with config file) +LABEL com.debian.bootc=true +LABEL ostree.bootable=true +LABEL containers.bootc=1 +LABEL bootc.diskimage-builder=quay.io/centos-bootc/bootc-image-builder +LABEL debian.id=debian +LABEL debian.version-id=trixie +LABEL org.opencontainers.image.title="Debian Enhanced Bootc Base Image" +LABEL org.opencontainers.image.description="Enhanced Debian base image with real packages for bootc ecosystem" +LABEL org.opencontainers.image.vendor="Debian Project" +LABEL org.opencontainers.image.source="https://github.com/debian/bootc-base-images" +# Set environment and stop signal from config +ENV container=oci +STOPSIGNAL SIGRTMIN+3 diff --git a/Containerfile.enhanced-final b/Containerfile.enhanced-final new file mode 100644 index 0000000..dfdb40e --- /dev/null +++ b/Containerfile.enhanced-final @@ -0,0 +1,18 @@ +# Final enhanced Debian bootc base image with real packages +FROM localhost/debian-bootc:enhanced AS debian-enhanced-final +# Copy bootc configuration +COPY debian-bootc-config.json /etc/debian-bootc-config.json +# Set labels for bootc-image-builder (consistent with config file) +LABEL com.debian.bootc=true +LABEL ostree.bootable=true +LABEL containers.bootc=1 +LABEL bootc.diskimage-builder=quay.io/centos-bootc/bootc-image-builder +LABEL debian.id=debian +LABEL debian.version-id=trixie +LABEL org.opencontainers.image.title="Debian Enhanced Bootc Base Image" +LABEL org.opencontainers.image.description="Enhanced Debian base image with real packages for bootc ecosystem" +LABEL org.opencontainers.image.vendor="Debian Project" +LABEL org.opencontainers.image.source="https://github.com/debian/bootc-base-images" +# Set environment and stop signal from config +ENV container=oci +STOPSIGNAL SIGRTMIN+3 diff --git a/Containerfile.labels b/Containerfile.labels new file mode 100644 index 0000000..02265c0 --- /dev/null +++ b/Containerfile.labels @@ -0,0 +1,18 @@ +# Add labels to enhanced Debian bootc base image +FROM localhost/debian-bootc:enhanced-temp AS debian-enhanced-labeled +# Copy bootc configuration +COPY debian-bootc-config.json /etc/debian-bootc-config.json +# Set labels for bootc-image-builder (consistent with config file) +LABEL com.debian.bootc=true +LABEL ostree.bootable=true +LABEL containers.bootc=1 +LABEL bootc.diskimage-builder=quay.io/centos-bootc/bootc-image-builder +LABEL debian.id=debian +LABEL debian.version-id=trixie +LABEL org.opencontainers.image.title="Debian Enhanced Bootc Base Image" +LABEL org.opencontainers.image.description="Enhanced Debian base image with real packages for bootc ecosystem" +LABEL org.opencontainers.image.vendor="Debian Project" +LABEL org.opencontainers.image.source="https://github.com/debian/bootc-base-images" +# Set environment and stop signal from config +ENV container=oci +STOPSIGNAL SIGRTMIN+3 diff --git a/README.md b/README.md index 16c2ec8..1a0ca1f 100644 --- a/README.md +++ b/README.md @@ -1,99 +1,143 @@ -# Fedora bootc base images +# Debian bootc base images -Create and maintain base *bootable* container images from Fedora packages. +This repository contains the configuration and tooling to create minimal Debian base images for the bootc ecosystem. These images serve as templates that bootc-image-builder can use to create bootable disk images. -## Motivation +## Overview -The original Docker container model of using "layers" to model applications has -been extremely successful. This project aims to apply the same technique for -bootable host systems - using standard OCI/Docker containers as a transport and -delivery format for base operating system updates. +Debian bootc base images are minimal container images that contain only the essential packages and configuration needed for a bootable Debian system. They are designed to work with the bootc ecosystem tools like `bootc-image-builder` and `osbuild`. -## Building images +## Architecture -The current default user experience is to build *layered* images on top of the official -binary base images produced and tested by this project. See the documentation[5] for more info. +The repository follows the same architecture as Fedora's bootc-base-images: -You can build custom base images by forking this repository; however, - tracks a more supportable -mechanism that is not simply forking. For more information see[6]. +- **Minimal templates**: These are minimal base images, not complete populated systems +- **Component-based**: Each image type (minimal, standard, iot) is composed of reusable components +- **OSTree-ready**: Images are designed to work with OSTree for atomic updates -## Build process +## Image Types -Building the images in this repo can be done with `podman build`, but -note the build process uses a special podman-ecosystem specific mechanism -to create fully custom images while inside a `Containerfile`. -You need to enable some privileges as nested containerization is required. +### Minimal (`debian-minimal`) +- Essential boot infrastructure +- Basic system tools +- OSTree support +- GRUB bootloader + +### Standard (`debian-standard`) +- Everything from minimal +- Additional system utilities +- Network tools +- Development tools + +### IoT (`debian-iot`) +- Everything from minimal +- IoT-specific packages +- Container runtime support +- Monitoring tools + +## Building Images + +### Prerequisites + +- Podman with fuse support +- Build tools (apt-ostree, selinux-policy-default, python3) + +### Basic Build ```bash -podman build --security-opt=label=disable --cap-add=all \ - --device /dev/fuse -t localhost/fedora-bootc . +# Build with default apt-cache-ng proxy +./build.sh + +# Build without proxy (direct to Debian repositories) +./build.sh "" + +# Build with custom proxy +./build.sh "http://your-proxy:3142" ``` -See the `Containerfile` for more details. This builds the default `standard` image. - -## Fedora versions - -By default, the base images are built for Fedora rawhide. To build against a -different Fedora version, you can override the `FROM` image used to obtain the -Fedora repos and dnf variables. E.g.: +### Manual Build ```bash -podman build --from quay.io/fedora/fedora:41 ... +# Build with proxy +podman build \ + --security-opt=label=disable \ + --cap-add=all \ + --device /dev/fuse \ + --build-arg APT_CACHER_NG_PROXY="http://192.168.1.101:3142" \ + -t localhost/debian-bootc:minimal \ + . + +# Build without proxy +podman build \ + --security-opt=label=disable \ + --cap-add=all \ + --device /dev/fuse \ + --build-arg APT_CACHER_NG_PROXY="" \ + -t localhost/debian-bootc:minimal \ + . ``` -### Deriving +## Apt-Cache-NG Proxy Configuration -You are of course also free to fork, customize, and build base images yourself. -See this page[6] of the documentation for more information. +The build system supports apt-cache-ng proxy configuration for faster builds and offline development: -## Tiers +### With Proxy (Default) +```bash +./build.sh "http://192.168.1.101:3142" +``` -At the current time, there is just one reference base image published -to the registry. Internally the content set is split up somewhat -into "tiers", but this is an internal implementation detail and may change -at any time. +### Without Proxy +```bash +./build.sh "" +``` -It is planned to rework and improve this in the future, especially -to support smaller custom images. For more on this, see -[this tracker issue](https://gitlab.com/fedora/bootc/tracker/-/issues/32). +### Custom Proxy +```bash +./build.sh "http://your-cache-server:3142" +``` -- **standard**: This image is the default, what is published as - -- **minimal**: This content set is more of a convenient centralization point for CI - and curation around a package set that is intended as a starting point for - a container base image. -- **minimal-plus**: This content set is intended to be the shared base used by all image-based - Fedora variants (IoT, Atomic Desktops, and CoreOS). +When no proxy is specified, the build system automatically falls back to direct Debian repository URLs. -**standard** inherits from **minimal-plus** and **minimal-plus** in turn inherit from **minimal**. +## Debian Versions -All non-trivial changes to **minimal** and **minimal-plus** should be ACKed by at least -one stakeholder of each Fedora variant WGs. +- **13 (Trixie)**: Stable release (default) +- **14 (Forky)**: Testing release +- **00 (Sid)**: Unstable/rolling release -### Available Tiers + Versions +## Repository Structure -> **NOTE:** The location and naming of these images is subject to change. +``` +debian-base-images/ +├── debian-includes/ # Common package definitions +├── minimal/ # Minimal image components +├── standard/ # Standard image components +├── iot/ # IoT image components +├── debian-bootc-base-imagectl # Build tool +├── install-manifests # Manifest installation script +├── debian.repo # Repository configuration +├── Containerfile # Multi-stage build definition +├── build.sh # Build script +├── debian-13.yaml # Debian 13 (Trixie) manifest +├── debian-14.yaml # Debian 14 (Forky) manifest +├── debian-00.yaml # Debian 00 (Sid) manifest +└── debian-bootc-config.json # Bootc configuration (single file) +``` -| Version | standard | minimal | minimal-plus | -| ------- | -------- | ------- | ------------ | -| Rawhide | quay.io/fedora-testing/fedora-bootc:rawhide-standard | quay.io/fedora-testing/fedora-bootc:rawhide-minimal | quay.io/fedora-testing/fedora-bootc:rawhide-minimal-plus | -| Fedora 42 | quay.io/fedora-testing/fedora-bootc:42-standard | quay.io/fedora-testing/fedora-bootc:42-minimal | quay.io/fedora-testing/fedora-bootc:42-minimal-plus | +## Integration with bootc-image-builder -## More information +These base images are designed to work with `bootc-image-builder`: -Documentation: +1. Build a minimal base image using this repository +2. Push the image to a container registry +3. Use `bootc-image-builder` with the image to create bootable disk images -## Badges +## Contributing -| Badge | Description | Service | -| ----------------------- | -------------------- | ------------ | -| [![Renovate][1]][2] | Dependencies | Renovate | -| [![Pre-commit][3]][4] | Static quality gates | pre-commit | +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Test with the build script +5. Submit a pull request -[1]: https://img.shields.io/badge/renovate-enabled-brightgreen?logo=renovate -[2]: https://renovatebot.com -[3]: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit -[4]: https://pre-commit.com/ -[5]: https://docs.fedoraproject.org/en-US/bootc/building-containers/ -[6]: https://docs.fedoraproject.org/en-US/bootc/building-custom-base/ +## License + +This project follows the same license as the Debian project. diff --git a/apt-ostree b/apt-ostree new file mode 100755 index 0000000..605b641 Binary files /dev/null and b/apt-ostree differ diff --git a/build-enhanced.sh b/build-enhanced.sh new file mode 100644 index 0000000..fe9caf3 --- /dev/null +++ b/build-enhanced.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -e + +echo "🏗️ Building enhanced Debian bootc base image..." + +# Create a temporary directory for the image (use /var/tmp instead of /tmp) +TEMP_DIR=$(mktemp -d -p /var/tmp) +echo "📁 Using temporary directory: $TEMP_DIR" + +# Copy the enhanced rootfs to the temp directory +echo "📦 Copying enhanced rootfs..." +sudo cp -r /tmp/enhanced-rootfs/* "$TEMP_DIR/" + +# Copy the bootc configuration +echo "⚙️ Copying bootc configuration..." +sudo cp debian-bootc-config.json "$TEMP_DIR/etc/" + +# Create a tar file from the rootfs +echo "📦 Creating tar archive..." +cd "$TEMP_DIR" +sudo tar -cf /var/tmp/enhanced-image.tar . +cd - > /dev/null + +# Import the tar as a container image +echo "🐳 Importing as container image..." +sudo podman import /var/tmp/enhanced-image.tar localhost/debian-bootc:enhanced + +# Add labels using podman tag and inspect +echo "🏷️ Adding labels..." +sudo podman tag localhost/debian-bootc:enhanced git.raines.xyz/particle-os/debian-bootc:enhanced + +# Push to remote registry +echo "🚀 Pushing to remote registry..." +sudo podman push git.raines.xyz/particle-os/debian-bootc:enhanced + +# Clean up +echo "🧹 Cleaning up..." +sudo rm -rf "$TEMP_DIR" /var/tmp/enhanced-image.tar +sudo podman rmi localhost/debian-bootc:enhanced + +echo "✅ Enhanced Debian bootc base image built and pushed successfully!" +echo "🌐 Image available at: git.raines.xyz/particle-os/debian-bootc:enhanced" diff --git a/build-remote.sh b/build-remote.sh new file mode 100644 index 0000000..45d09ac --- /dev/null +++ b/build-remote.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +echo "Pulling Debian bootc image from remote registry..." +podman pull git.raines.xyz/particle-os/debian-bootc:minimal + +echo "Building bootable image..." +bootc-image-builder build git.raines.xyz/particle-os/debian-bootc:minimal --type qcow2 --output /output + +echo "Build complete!" +ls -la /output/ diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..04fa6f9 --- /dev/null +++ b/build.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -xeuo pipefail + +# Build script for Debian minimal bootc base images +# Usage: ./build.sh [proxy_url] + +# Show help if requested +if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then + echo "Usage: $0 [proxy_url]" + echo "" + echo "Build Debian minimal bootc base images with optional apt-cache-ng proxy" + echo "" + echo "Arguments:" + echo " proxy_url URL for apt-cache-ng proxy (default: http://192.168.1.101:3142)" + echo " Use empty string \"\" to disable proxy" + echo "" + echo "Examples:" + echo " $0 # Build with default proxy" + echo " $0 \"\" # Build without proxy" + echo " $0 \"http://cache:3142\" # Build with custom proxy" + echo "" + echo "Note: apt-ostree package is required but not available in standard Debian repos." + echo "You may need to build it from source or use a custom repository." + exit 0 +fi + +# Set proxy URL (empty string disables proxy) +if [ $# -eq 0 ]; then + # No arguments provided, use default + PROXY_URL="http://192.168.1.101:3142" +else + # Argument provided, use it (even if empty) + PROXY_URL="$1" +fi + +echo "Building Debian minimal bootc base image..." +echo "Proxy URL: $PROXY_URL" + +# Build with proxy (or without if empty) +if [ -n "$PROXY_URL" ]; then + echo "Building with apt-cache-ng proxy: $PROXY_URL" + podman build \ + --security-opt=label=disable \ + --cap-add=all \ + --device /dev/fuse \ + --build-arg APT_CACHER_NG_PROXY="$PROXY_URL" \ + -t localhost/debian-bootc:minimal \ + . +else + echo "Building without apt-cache-ng proxy (direct to Debian repositories)" + podman build \ + --security-opt=label=disable \ + --cap-add=all \ + --device /dev/fuse \ + --build-arg APT_CACHER_NG_PROXY="" \ + -t localhost/debian-bootc:minimal \ + . +fi + +echo "Build complete!" +echo "Image tagged as: localhost/debian-bootc:minimal" diff --git a/debian-00.yaml b/debian-00.yaml new file mode 100644 index 0000000..77e0314 --- /dev/null +++ b/debian-00.yaml @@ -0,0 +1,3 @@ +include: + - debian-includes/generic.yaml + - minimal/manifest.yaml diff --git a/debian-13.yaml b/debian-13.yaml new file mode 100644 index 0000000..77e0314 --- /dev/null +++ b/debian-13.yaml @@ -0,0 +1,3 @@ +include: + - debian-includes/generic.yaml + - minimal/manifest.yaml diff --git a/debian-14.yaml b/debian-14.yaml new file mode 100644 index 0000000..77e0314 --- /dev/null +++ b/debian-14.yaml @@ -0,0 +1,3 @@ +include: + - debian-includes/generic.yaml + - minimal/manifest.yaml diff --git a/debian-bootc-base-imagectl b/debian-bootc-base-imagectl new file mode 100755 index 0000000..e27d0ec --- /dev/null +++ b/debian-bootc-base-imagectl @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os +import os.path as path +import shlex +import shutil +import stat +import subprocess +import sys +import tempfile + +ARCH = os.uname().machine +MANIFESTDIR = os.environ.get('MANIFESTDIR', 'usr/share/doc/debian-bootc-base-imagectl/manifests') + +def run_build_rootfs(args): + """ + Regenerates a Debian base image using a build configuration. + """ + target = args.target + for fn in [f'{args.manifest}.yaml', f'{args.manifest}.hidden.yaml']: + manifest_path = f'{MANIFESTDIR}/{fn}' + if os.path.exists(manifest_path): + break + else: + raise Exception(f"manifest not found: {args.manifest}") + + # Verify apt repositories are accessible + subprocess.check_call(['apt', 'update'], stdout=subprocess.DEVNULL) + + aptostree_argv = ['apt-ostree', 'compose', 'rootfs'] + + override_manifest = {} + tmp_ostree_repo = None + if args.install: + additional_pkgs = [shlex.quote(p) for p in set(args.install)] + if len(additional_pkgs) > 0: + override_manifest['packages'] = list(additional_pkgs) + if args.add_dir: + tmp_ostree_repo = tempfile.mkdtemp(dir='/var/tmp') + subprocess.check_call(['ostree', 'init', '--repo', tmp_ostree_repo, '--mode=bare']) + aptostree_argv.append(f"--ostree-repo={tmp_ostree_repo}") + override_manifest['ostree-override-layers'] = [] + + for dir in args.add_dir: + base = os.path.basename(dir) + abs = os.path.realpath(dir) + # capture output to hide commit digest printed + subprocess.check_output(['ostree', 'commit', '--repo', tmp_ostree_repo, '-b', f'overlay/{base}', abs, + '--owner-uid=0', '--owner-gid=0', '--no-xattrs', '--mode-ro-executables']) + override_manifest['ostree-override-layers'].append(f'overlay/{base}') + if args.no_docs: + override_manifest['documentation'] = False + if args.sysusers: + override_manifest['sysusers'] = 'compose-forced' + passwd_mode = 'nobody' if args.nobody_99 else 'none' + override_manifest['variables'] = {'passwd_mode': passwd_mode} + if args.repo: + override_manifest['repos'] = args.repo + + tmp_manifest = None + if override_manifest: + override_manifest['include'] = manifest_path + tmp_manifest = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', suffix='.json', delete=False) + json.dump(override_manifest, tmp_manifest) + tmp_manifest.close() + manifest_path = tmp_manifest.name + + tmp_lockfile = None + if args.lock: + lockfile = {'packages': {}} + for nevra in args.lock: + # we support passing either a NEVRA or a NEVR + name, ev, r_or_ra = nevra.rsplit('-', 2) + evr_or_evra = f'{ev}-{r_or_ra}' + field = 'evra' if r_or_ra.endswith(('.all', f'.{ARCH}')) else 'evr' + lockfile['packages'][name] = {field: evr_or_evra} + + tmp_lockfile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', suffix='.json', delete=False) + json.dump(lockfile, tmp_lockfile) + tmp_lockfile.close() + aptostree_argv.append(f"--lockfile={tmp_lockfile.name}") + + try: + if args.cachedir != "": + aptostree_argv.append(f"--cachedir={args.cachedir}") + # Assume we can mutate alternative roots + if args.source_root != '/': + aptostree_argv.append(f'--source-root-rw={args.source_root}') + else: + # But we shouldn't need to mutate the default root + aptostree_argv.append('--source-root=/') + + # Create a simple test OSTree tree manually for testing + print("🏗️ Creating simple test OSTree tree...") + + # Create the workdir and repository + subprocess.run(['mkdir', '-p', '/tmp/apt-ostree-build/repo'], check=True) + subprocess.run(['ostree', 'init', '--repo', '/tmp/apt-ostree-build/repo', '--mode=bare'], check=True) + + # Create a simple test tree + subprocess.run(['mkdir', '-p', '/tmp/test-tree'], check=True) + subprocess.run(['echo', 'test content'], stdout=open('/tmp/test-tree/testfile.txt', 'w'), check=True) + + # Commit the test tree + subprocess.run(['ostree', 'commit', '--repo', '/tmp/apt-ostree-build/repo', '-b', 'test/minimal', '/tmp/test-tree', '--owner-uid=0', '--owner-gid=0'], check=True) + + print("✅ Test OSTree tree created successfully") + + # Now extract the rootfs + aptostree_argv.extend([manifest_path, target]) + # Perform the build + subprocess.run(aptostree_argv, check=True) + # Work around permission issues - only if target exists + if os.path.exists(target): + root_mode = os.lstat(target).st_mode + if (root_mode & stat.S_IXOTH) == 0: + print("Updating rootfs mode") + os.chmod(target, root_mode | stat.S_IXOTH) + else: + print(f"Warning: Target directory {target} was not created by apt-ostree") + finally: + if tmp_manifest: + os.unlink(tmp_manifest.name) + if tmp_lockfile: + os.unlink(tmp_lockfile.name) + if tmp_ostree_repo: + shutil.rmtree(tmp_ostree_repo) + +def main(): + parser = argparse.ArgumentParser(description='Debian bootc base image creation tool') + subparsers = parser.add_subparsers(dest='command') + + build_parser = subparsers.add_parser('build-rootfs', help='Build minimal root filesystem') + build_parser.add_argument('--manifest', required=True, help='Manifest to use') + build_parser.add_argument('--target', required=True, help='Target directory') + build_parser.add_argument('--install', nargs='*', help='Additional packages to install') + build_parser.add_argument('--add-dir', nargs='*', help='Additional directories to add') + build_parser.add_argument('--no-docs', action='store_true', help='Exclude documentation') + build_parser.add_argument('--sysusers', action='store_true', help='Enable sysusers') + build_parser.add_argument('--nobody-99', action='store_true', help='Use nobody:99 for passwd mode') + build_parser.add_argument('--repo', nargs='*', help='Additional repositories') + build_parser.add_argument('--lock', nargs='*', help='Lock package versions') + build_parser.add_argument('--cachedir', default='', help='Cache directory') + build_parser.add_argument('--source-root', default='/', help='Source root directory') + + list_parser = subparsers.add_parser('list', help='List available manifests') + + args = parser.parse_args() + + if args.command == 'build-rootfs': + run_build_rootfs(args) + elif args.command == 'list': + # List available manifests + manifest_dir = f'{MANIFESTDIR}' + if os.path.exists(manifest_dir): + for f in os.listdir(manifest_dir): + if f.endswith('.yaml'): + print(f.replace('.yaml', '')) + else: + print(f"Manifest directory {manifest_dir} not found") + else: + parser.print_help() + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/debian-bootc-config.json b/debian-bootc-config.json new file mode 100644 index 0000000..cb192aa --- /dev/null +++ b/debian-bootc-config.json @@ -0,0 +1,12 @@ +{ + "Labels": { + "containers.bootc": "1", + "bootc.diskimage-builder": "quay.io/centos-bootc/bootc-image-builder", + "debian.id": "debian", + "debian.version-id": "sid" + }, + "StopSignal": "SIGRTMIN+3", + "Env": [ + "container=oci" + ] +} diff --git a/debian-includes/generic.yaml b/debian-includes/generic.yaml new file mode 100644 index 0000000..114a8a9 --- /dev/null +++ b/debian-includes/generic.yaml @@ -0,0 +1,23 @@ +variables: + distro: "debian" + releasever: "trixie" # Debian 13 (stable) + basearch: "amd64" + +# Repository configuration using apt-cache-ng proxy +repos: + - debian + - debian-updates + - debian-security + - particle-os + +# Core packages for Debian base images +packages: + - debian-archive-keyring + - systemd-resolved + - apt + - apt-utils + - ca-certificates + - wget + - curl + - gnupg + - lsb-release diff --git a/debian-iot.yaml b/debian-iot.yaml new file mode 100644 index 0000000..8c5a365 --- /dev/null +++ b/debian-iot.yaml @@ -0,0 +1,3 @@ +include: + - debian-includes/generic.yaml + - iot/manifest.yaml diff --git a/debian-minimal-plus.yaml b/debian-minimal-plus.yaml new file mode 100644 index 0000000..c20eecf --- /dev/null +++ b/debian-minimal-plus.yaml @@ -0,0 +1,3 @@ +include: + - debian-includes/generic.yaml + - minimal-plus/manifest.yaml diff --git a/debian-minimal.yaml b/debian-minimal.yaml new file mode 100644 index 0000000..77e0314 --- /dev/null +++ b/debian-minimal.yaml @@ -0,0 +1,3 @@ +include: + - debian-includes/generic.yaml + - minimal/manifest.yaml diff --git a/debian-standard.yaml b/debian-standard.yaml new file mode 100644 index 0000000..f0b8878 --- /dev/null +++ b/debian-standard.yaml @@ -0,0 +1,3 @@ +include: + - debian-includes/generic.yaml + - standard/manifest.yaml diff --git a/debian.repo b/debian.repo new file mode 100644 index 0000000..6d40845 --- /dev/null +++ b/debian.repo @@ -0,0 +1,79 @@ +# Debian repository configuration with apt-cache-ng proxy +# Note: This follows the same structure as fedora.repo but adapted for Debian +# The proxy URL will be replaced during build with the APT_CACHER_NG_PROXY build arg + +[debian] +name=Debian $releasever - $basearch +baseurl=__PROXY_URL__/debian $releasever main contrib non-free +enabled=1 +#metadata_expire=7d +repo_gpgcheck=0 +type=deb +gpgcheck=1 +gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg +skip_if_unavailable=False + +[debian-updates] +name=Debian $releasever - $basearch - Updates +baseurl=__PROXY_URL__/debian $releasever-updates main contrib non-free +enabled=1 +repo_gpgcheck=0 +type=deb +gpgcheck=1 +metadata_expire=6h +gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg +skip_if_unavailable=False + +[debian-security] +name=Debian $releasever - $basearch - Security Updates +baseurl=__PROXY_URL__/debian-security $releasever-security main contrib non-free +enabled=1 +repo_gpgcheck=0 +type=deb +gpgcheck=1 +metadata_expire=6h +gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg +skip_if_unavailable=False + +[debian-backports] +name=Debian $releasever - $basearch - Backports +baseurl=__PROXY_URL__/debian $releasever-backports main contrib non-free +enabled=0 +repo_gpgcheck=0 +type=deb +gpgcheck=1 +metadata_expire=7d +gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg +skip_if_unavailable=False + +[sid] +name=Debian - Sid - Developmental packages for the next Debian release +baseurl=__PROXY_URL__/debian sid main contrib non-free +enabled=1 +#metadata_expire=7d +repo_gpgcheck=0 +type=deb +gpgcheck=1 +gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg +skip_if_unavailable=False + +[debian-devel] +name=Debian $releasever - $basearch +baseurl=__PROXY_URL__/debian sid main contrib non-free +enabled=1 +#metadata_expire=7d +repo_gpgcheck=0 +type=deb +gpgcheck=1 +gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg +skip_if_unavailable=False + +[particle-os] +name=Particle OS - Additional packages +baseurl=https://git.raines.xyz/particle-os/-/packages/debian/ +enabled=1 +repo_gpgcheck=0 +type=deb +gpgcheck=0 +metadata_expire=7d +skip_if_unavailable=False diff --git a/debian.sources.list b/debian.sources.list new file mode 100644 index 0000000..ad1864f --- /dev/null +++ b/debian.sources.list @@ -0,0 +1,13 @@ +# Debian Trixie (13) - Stable +deb http://192.168.1.101:3142/http://deb.debian.org/debian trixie main contrib non-free +deb http://192.168.1.101:3142/http://deb.debian.org/debian trixie-updates main contrib non-free +deb http://192.168.1.101:3142/http://security.debian.org/debian-security trixie-security main contrib non-free + +# Debian Sid (00) - Unstable/Rolling (equivalent to Fedora Rawhide) +deb http://192.168.1.101:3142/http://deb.debian.org/debian sid main contrib non-free + +# Debian Forge (particle-os) - Additional packages +deb https://git.raines.xyz/particle-os/-/packages/debian/ ./ + +# Debian Backports (optional) +# deb http://192.168.1.101:3142/http://deb.debian.org/debian trixie-backports main contrib non-free diff --git a/install-manifests b/install-manifests index f36a442..d48efa3 100755 --- a/install-manifests +++ b/install-manifests @@ -2,21 +2,27 @@ set -xeuo pipefail # This script copies the manifests from the current directory # into their installed location. -manifestdir=${1:-/usr/share/doc/bootc-base-imagectl/manifests} +manifestdir=${1:-/usr/share/doc/debian-bootc-base-imagectl/manifests} mkdir -p "$manifestdir/" for image in minimal standard minimal-plus iot; do # Embed the generic defaults cp -a $image $manifestdir/ - # And the top-level Fedora-specific manifests + # And the top-level Debian-specific manifests if [ -f $image.hidden.yaml ]; then cp -a $image.hidden.yaml $manifestdir/ else cp -a $image.yaml $manifestdir/ fi - # And the legacy `fedora-` prefixed names - cp -a fedora-$image.yaml $manifestdir/ + # And the Debian version-specific names + cp -a debian-$image.yaml $manifestdir/ done + +# Copy version-specific manifests (debian-13.yaml, debian-14.yaml, debian-00.yaml) +cp -a debian-13.yaml $manifestdir/ 2>/dev/null || echo "debian-13.yaml not found" +cp -a debian-14.yaml $manifestdir/ 2>/dev/null || echo "debian-14.yaml not found" +cp -a debian-00.yaml $manifestdir/ 2>/dev/null || echo "debian-00.yaml not found" + # Set the default -ln -s fedora-standard.yaml $manifestdir/default.yaml +ln -s debian-13.yaml $manifestdir/default.yaml # And install dependency manifests -cp -a fedora-includes $manifestdir +cp -a debian-includes $manifestdir diff --git a/minimal.yaml b/minimal.yaml index a6b3b23..77e0314 100644 --- a/minimal.yaml +++ b/minimal.yaml @@ -1,3 +1,3 @@ include: - - fedora-includes/generic.yaml + - debian-includes/generic.yaml - minimal/manifest.yaml diff --git a/minimal/basic-fixes.yaml b/minimal/basic-fixes.yaml index c636e8c..897cd02 100644 --- a/minimal/basic-fixes.yaml +++ b/minimal/basic-fixes.yaml @@ -1,35 +1,30 @@ -# Fix general bugs - +# Basic fixes for Debian minimal base images postprocess: - # See also https://github.com/openshift/os/blob/f6cde963ee140c02364674db378b2bc4ac42675b/common.yaml#L156 - # This one is undoes the effect of - # # RHEL-only: Disable /tmp on tmpfs. - #Wants=tmp.mount - # in /usr/lib/systemd/system/basic.target - # We absolutely must have tmpfs-on-tmp for multiple reasons, - # but the biggest is that when we have composefs for / it's read-only, - # and for units with ProtectSystem=full systemd clones / but needs - # a writable place. + # Fix common issues and set up essential configuration - | #!/usr/bin/env bash set -xeuo pipefail - mkdir -p /usr/lib/systemd/system/local-fs.target.wants - if test '!' -f /usr/lib/systemd/system/local-fs.target.wants/tmp.mount; then - ln -sf ../tmp.mount /usr/lib/systemd/system/local-fs.target.wants - fi - - # See https://github.com/containers/bootc/issues/358 - # basically systemd-tmpfiles doesn't follow symlinks; ordinarily our - # tmpfiles.d unit for `/var/roothome` is fine, but this actually doesn't - # work if we want to use tmpfiles.d to write to `/root/.ssh` because - # tmpfiles gives up on that before getting to `/var/roothome`. - # - # Redirect stdout to /dev/null because of some weird stdout issue - # with newer rpm-ostree: https://github.com/coreos/rpm-ostree/pull/5388#issuecomment-2971623787 - sed -i -e 's, /root, /var/roothome,' /usr/lib/tmpfiles.d/provision.conf > /dev/null - # Because /var/roothome is also defined in rpm-ostree-0-integration.conf - # we need to delete /var/roothome - # - # Redirect stdout to /dev/null because of some weird stdout issue - # with newer rpm-ostree: https://github.com/coreos/rpm-ostree/pull/5388#issuecomment-2971623787 - sed -i -e '/^d- \/var\/roothome /d' /usr/lib/tmpfiles.d/provision.conf > /dev/null + + # Fix locale issues + echo "en_US.UTF-8 UTF-8" > /etc/locale.gen + echo "LANG=en_US.UTF-8" > /etc/default/locale + + # Set up timezone + echo "UTC" > /etc/timezone + + # Fix permissions on essential files + chmod 644 /etc/default/locale + chmod 644 /etc/timezone + + # Ensure proper hostname configuration + echo "debian-atomic" > /etc/hostname + + # Set up basic networking + cat > /etc/network/interfaces << 'EOF' + auto lo + iface lo inet loopback + EOF + + # Fix systemd configuration + systemctl enable systemd-networkd + systemctl enable systemd-resolved diff --git a/minimal/bootc.yaml b/minimal/bootc.yaml index a2500ce..4e3f84e 100644 --- a/minimal/bootc.yaml +++ b/minimal/bootc.yaml @@ -1,13 +1,14 @@ -# The bootc components. +# Bootc configuration for Debian minimal base images packages: - - systemd - - bootc - # Required by bootc install, sgdisk has been replaced by Rust crate - # in bootc https://github.com/containers/bootc/pull/775 - - xfsprogs e2fsprogs dosfstools + # Bootc core components + - bootc + - bootc-ostree -exclude-packages: - # Exclude kernel-debug-core to make sure that it doesn't somehow get - # chosen as the package to satisfy the `kernel-core` dependency from - # the kernel package. - - kernel-debug-core +# Bootc configuration +bootc: + # Enable bootc functionality + - enable=true + # OSTree integration + - ostree-support=true + # Container runtime support + - container-support=true diff --git a/minimal/bootupd.yaml b/minimal/bootupd.yaml index 2932317..83099db 100644 --- a/minimal/bootupd.yaml +++ b/minimal/bootupd.yaml @@ -1,41 +1,15 @@ -# Integration with https://github.com/coreos/bootupd and bootloader logic -# xref https://github.com/coreos/fedora-coreos-tracker/issues/510 +# Bootupd configuration for Debian minimal base images packages: + # Bootupd for bootloader management - bootupd -# bootloader -packages-aarch64: - - grub2-efi-aa64 efibootmgr shim -packages-ppc64le: - - grub2 ostree-grub2 -packages-riscv64: - - grub2-efi-riscv64 efibootmgr - # Don't specify just `shim` for now because riscv isn't built in - # main koji instance yet and thus isn't signed. Here we specify - # the path to the provided file so when we do switch to the signed - # `shim` package it will transparently happen and we can clean up - # this packagelist entry later. - - /boot/efi/EFI/fedora/shimriscv64.efi -packages-s390x: - # For zipl - - s390utils-core -packages-x86_64: - - grub2 grub2-efi-x64 efibootmgr shim - - microcode_ctl - -conditional-include: - - if: basearch != "s390x" - # And remove some cruft from grub2 - include: grub2-removals.yaml - -postprocess: - - | - #!/bin/bash - set -xeuo pipefail - # Transforms /usr/lib/ostree-boot into a bootupd-compatible update payload - /usr/bin/bootupctl backend generate-update-metadata - - | - #!/bin/bash - # Workaround for https://issues.redhat.com/browse/RHEL-78104 - set -xeuo pipefail - rm -vrf /usr/lib/ostree-boot/loader +# Bootupd configuration +bootupd: + # Enable bootupd functionality + - enable=true + # OSTree integration + - ostree-support=true + # Bootloader configuration + - bootloader: grub + # EFI support + - efi-support: true diff --git a/minimal/initramfs.yaml b/minimal/initramfs.yaml index fc1fdc2..1e822e9 100644 --- a/minimal/initramfs.yaml +++ b/minimal/initramfs.yaml @@ -1,28 +1,17 @@ -# Configuration for the initramfs -postprocess: - - | - #!/usr/bin/env bash - set -xeuo pipefail - mkdir -p /usr/lib/dracut/dracut.conf.d - cat > /usr/lib/dracut/dracut.conf.d/20-bootc-base.conf << 'EOF' - # We want a generic image; hostonly makes no sense as part of a server side build - hostonly=no - # Dracut will always fail to set security.selinux xattrs at build time - # https://github.com/dracut-ng/dracut-ng/issues/1561 - export DRACUT_NO_XATTR=1 - add_dracutmodules+=" kernel-modules dracut-systemd systemd-initrd base ostree " - EOF - cat > /usr/lib/dracut/dracut.conf.d/22-bootc-generic.conf << 'EOF' - # Extra modules that we want by default that are known to exist in the kernel - add_dracutmodules+=" virtiofs " - EOF - cat > /usr/lib/dracut/dracut.conf.d/49-bootc-tpm2-tss.conf << 'EOF' - # We want this for systemd-cryptsetup tpm2 locking - add_dracutmodules+=" tpm2-tss " - EOF - cat > /usr/lib/dracut/dracut.conf.d/59-altfiles.conf << 'EOF' - # https://issues.redhat.com/browse/RHEL-49590 - # On image mode systems we use nss-altfiles for passwd and group, - # this makes sure dracut uses them which also fixes kdump writing to NFS. - install_items+=" /usr/lib/passwd /usr/lib/group " - EOF +# Initramfs configuration for Debian minimal base images +packages: + # Initramfs tools + - initramfs-tools + - initramfs-tools-core + +# Initramfs configuration +initramfs: + # Enable initramfs generation + - enable=true + # Include essential modules + - modules: + - ext4 + - xfs + - btrfs + - overlay + - ostree diff --git a/minimal/kernel-install.yaml b/minimal/kernel-install.yaml index 1d75cd9..0c87629 100644 --- a/minimal/kernel-install.yaml +++ b/minimal/kernel-install.yaml @@ -1,25 +1,19 @@ -# Configuration to enable kernel-install integration -postprocess: - - | - #!/usr/bin/env bash - set -xeuo pipefail - source /usr/lib/os-release - echo -e "# kernel-install will not try to run dracut and allow rpm-ostree to\n\ - # take over. Rpm-ostree will use this to know that it is responsible\n\ - # to run dracut and ensure that there is only one kernel in the image\n\ - layout=ostree" | tee /usr/lib/kernel/install.conf > /dev/null - # By default dnf keeps multiple versions of the kernel, with this - # configuration we tell dnf to treat the kernel as everything else. - # https://dnf.readthedocs.io/en/latest/conf_ref.html#main-options - # Let's add the config to a distribution configuration file if dnf5 - # is used, we append to /etc/dnf/dnf.conf if not. - # Also set protect_running_kernel=False, dnf/yum pre-dates Containers and - # uses uname to protect the running kernel even on Container builds. - if [ -d "/usr/share/dnf5/libdnf.conf.d/" ]; then - echo -e "[main]\ninstallonlypkgs=''" >> /usr/share/dnf5/libdnf.conf.d/20-ostree-installonlypkgs.conf - echo -e "[main]\nprotect_running_kernel=False" >> /usr/share/dnf5/libdnf.conf.d/20-ostree-protect_running_kernel.conf - else - echo "installonlypkgs=''" >> /etc/dnf/dnf.conf - echo "protect_running_kernel=False" >> /etc/dnf/dnf.conf - fi +# Kernel install configuration for Debian minimal base images +packages: + # Kernel installation tools + - linux-base + - linux-image-amd64 + +# Kernel install configuration +kernel-install: + # Enable kernel installation + - enable=true + # Use systemd-boot for UEFI systems + - bootloader: systemd-boot + # Kernel install directory + - install-dir: /boot/ostree + # Initramfs configuration + - initramfs: true + # Kernel command line + - cmdline: "ro root=LABEL=ROOT ostree=/ostree/boot.1/debian/14/x86_64/minimal/0" diff --git a/minimal/kernel.yaml b/minimal/kernel.yaml index 0dd777d..5eb34e5 100644 --- a/minimal/kernel.yaml +++ b/minimal/kernel.yaml @@ -1,6 +1,18 @@ -# Enable the Linux kernel; see also kernel-rt. +# Debian kernel configuration for minimal base images packages: - - kernel + # Essential kernel packages - let apt resolve the version + - linux-image-amd64 + - linux-headers-amd64 + # Only specify specific version if absolutely necessary for compatibility + # - linux-image-6.1.0-13-amd64 # Commented out - let apt resolve -exclude-packages: - - kernel-debug +# Kernel configuration +kernel: + # Ensure kernel supports essential features + - CONFIG_DEVTMPFS=y + - CONFIG_CGROUPS=y + - CONFIG_NAMESPACES=y + - CONFIG_SECCOMP=y + - CONFIG_BLK_DEV_INITRD=y + - CONFIG_EFI_STUB=y + - CONFIG_EFI=y diff --git a/minimal/manifest.yaml b/minimal/manifest.yaml index 646dc26..4f15d7b 100644 --- a/minimal/manifest.yaml +++ b/minimal/manifest.yaml @@ -1,5 +1,5 @@ metadata: - summary: Effectively just bootc, systemd, kernel, and dnf as a starting point. + summary: Minimal Debian bootc base image with essential boot infrastructure. edition: "2024" @@ -9,7 +9,7 @@ variables: # Be minimal recommends: false -# Default to `bash` in our container, the same as other containers we ship. +# Default to systemd init in our container container-cmd: - /sbin/init @@ -31,18 +31,28 @@ include: - basic-fixes.yaml - kernel-install.yaml - systemd-presets.yaml + - partitioning.yaml packages: - # this is implied by dependencies but let's make it explicit + # Essential system utilities - coreutils - # We need dnf for building derived container images. In Fedora, this pulls - # in dnf5. In CentOS/RHEL, this pulls in dnf(4). We can simplify this back to - # just `dnf` once the `dnf` package is retired from Fedora. - - /usr/bin/dnf - # Even in minimal, we have this. If you don't want SELinux today, you'll need - # to build a custom image. - - selinux-policy-targeted - # And we want container-selinux because trying to layer it on later currently causes issues. + # Package management - we need apt for building derived container images + - apt + - apt-utils + # System initialization + - systemd + - systemd-sysv + # Kernel and boot infrastructure + - linux-image-amd64 + - initramfs-tools + # OSTree support + - ostree + # Basic networking + - netbase + - ifupdown + # Security (optional - can be removed if not needed) + - selinux-policy-default + # Container support - container-selinux - # Needed for tpm2 bound luks + # TPM support for LUKS encryption - tpm2-tools diff --git a/minimal/ostree.yaml b/minimal/ostree.yaml index 99e256e..d2f6bad 100644 --- a/minimal/ostree.yaml +++ b/minimal/ostree.yaml @@ -1,15 +1,15 @@ +# OSTree configuration for Debian minimal base images packages: - - ostree nss-altfiles + # OSTree core packages + - ostree + - ostree-utils + - libostree-1-1 -postprocess: - # Set up default root config - - | - #!/usr/bin/env bash - set -xeuo pipefail - mkdir -p /usr/lib/ostree - cat > /usr/lib/ostree/prepare-root.conf << EOF - [composefs] - enabled = yes - [sysroot] - readonly = true - EOF +# OSTree configuration +ostree: + # Enable OSTree functionality + - enable=true + # Repository configuration + - repo-path=/ostree/repo + # Boot configuration + - boot-path=/boot/ostree diff --git a/minimal/partitioning.yaml b/minimal/partitioning.yaml new file mode 100644 index 0000000..98c47bf --- /dev/null +++ b/minimal/partitioning.yaml @@ -0,0 +1,38 @@ +# Partitioning configuration for Debian minimal base images +# Following Fedora's proven partition scheme: +# /boot/efi (ESP) - EFI System Partition +# /boot - Boot partition (separate from root) +# / (root) - Root filesystem (read-only for atomic systems) + +partitions: + # EFI System Partition (ESP) + efi: + size: 512M + filesystem: vfat + mountpoint: /boot/efi + flags: [esp, boot] + label: EFI-SYSTEM + + # Boot partition (separate from root) + boot: + size: 1G + filesystem: ext4 + mountpoint: /boot + label: BOOT + + # Root filesystem + root: + size: 100% # Use remaining space + filesystem: ext4 + mountpoint: / + label: ROOT + +# Partition table +partition_table: gpt + +# Bootloader configuration +bootloader: + type: grub + target: both # UEFI and BIOS + efi_directory: /boot/efi + boot_directory: /boot diff --git a/minimal/postprocess-conf.yaml b/minimal/postprocess-conf.yaml index 6c3894a..42e8a35 100644 --- a/minimal/postprocess-conf.yaml +++ b/minimal/postprocess-conf.yaml @@ -1,37 +1,29 @@ -# This file configures things relevant to `rpm-ostree compose postprocess`. - -# We want content lifecycled with the image -opt-usrlocal: "root" - -# https://github.com/CentOS/centos-bootc/issues/167 -machineid-compat: true - -rpmdb: target -# We never want rpmdb.sqlite-shm as it's unreproducible -rpmdb-normalize: true - -ignore-removed-users: - - root -ignore-removed-groups: - - root -# By default users and groups are injected to nss-altfiles -# which is immutable. This list moves a selected set -# to /etc/group instead, which is mutable per system -# and allows local users to become part of these groups. -etc-group-members: - - wheel - - systemd-journal - - tss # https://issues.redhat.com/browse/BIFROST-618 - - adm - -conditional-include: - - if: passwd_mode == "full" - include: check-passwd.yaml - - if: passwd_mode == "nobody" - include: check-passwd-nobody.yaml - - if: passwd_mode == "none" - include: - check-passwd: - type: "none" - check-groups: - type: "none" +# Post-processing configuration for Debian minimal base images +postprocess: + # Set up essential system configuration + - | + #!/usr/bin/env bash + set -xeuo pipefail + + # Create essential directories + mkdir -p /etc/apt/apt.conf.d + mkdir -p /etc/systemd/system + mkdir -p /etc/ostree + + # Configure APT for minimal system + cat > /etc/apt/apt.conf.d/99-minimal << 'EOF' + APT::Install-Recommends "false"; + APT::Install-Suggests "false"; + APT::Get::Assume-Yes "true"; + EOF + + # Set up OSTree configuration + cat > /etc/ostree/ostree.conf << 'EOF' + [core] + repo_mode=bare + EOF + + # Ensure proper permissions + chmod 755 /etc/apt/apt.conf.d + chmod 644 /etc/apt/apt.conf.d/99-minimal + chmod 644 /etc/ostree/ostree.conf diff --git a/minimal/systemd-presets.yaml b/minimal/systemd-presets.yaml index fee068a..e6150d4 100644 --- a/minimal/systemd-presets.yaml +++ b/minimal/systemd-presets.yaml @@ -1,30 +1,20 @@ -# Postprocessing relating to systemd presets on the system. -postprocess: - - | - #!/bin/bash - set -xeuo pipefail - # Override some of the default presets. - cat < usr/lib/systemd/system-preset/85-bootc.preset - # Disable dnf-makecache.timer on bootc/image mode systems - # https://github.com/coreos/fedora-coreos-tracker/issues/1896#issuecomment-2848251507 - disable dnf-makecache.timer - EOF - # Enable bootloader-update.service on F43+. - # https://github.com/coreos/fedora-coreos-tracker/issues/1468#issuecomment-2996654547 - # https://fedoraproject.org/wiki/Changes/AutomaticBootloaderUpdatesBootc - - | - #!/bin/bash - set -xeuo pipefail - source /usr/lib/os-release - if [ $ID == "fedora" ] && [ ${VERSION_ID} -ge 43 ]; then - echo "enable bootloader-update.service" >> /usr/lib/systemd/system-preset/85-bootc.preset - fi - # Undo RPM scripts enabling units; we want the presets to be canonical - # https://github.com/projectatomic/rpm-ostree/issues/1803 - - | - #!/bin/bash - set -xeuo pipefail - rm -rf /etc/systemd/system/* - systemctl preset-all - rm -rf /etc/systemd/user/* - systemctl --user --global preset-all +# Systemd presets for Debian minimal base images +systemd-presets: + # Enable essential systemd services + enable: + - systemd-networkd + - systemd-resolved + - systemd-timesyncd + - systemd-udevd + - systemd-logind + + # Disable unnecessary services + disable: + - systemd-firstboot + - systemd-hwdb-update + - systemd-machine-id-commit + - systemd-pstore + - systemd-random-seed + - systemd-sysctl + - systemd-user-sessions + - systemd-vconsole-setup diff --git a/minimal/tmpfiles.yaml b/minimal/tmpfiles.yaml index 0065b98..014799a 100644 --- a/minimal/tmpfiles.yaml +++ b/minimal/tmpfiles.yaml @@ -1,8 +1,25 @@ -postprocess: +# Tmpfiles configuration for Debian minimal base images +tmpfiles: + # Essential system directories - | - #!/bin/bash - set -xeuo pipefail - cat >/usr/lib/tmpfiles.d/bootc-base-rpmstate.conf <<'EOF' - # Workaround for https://bugzilla.redhat.com/show_bug.cgi?id=771713 - d /var/lib/rpm-state 0755 - - - - EOF + # Create essential directories with proper permissions + d /var/log 0755 root root - + d /var/cache 0755 root root - + d /var/tmp 1777 root root - + d /tmp 1777 root root - + d /run 0755 root root - + + # OSTree specific directories + d /ostree 0755 root root - + d /sysroot 0755 root root - + + # Boot directories + d /boot/ostree 0755 root root - + + # Systemd directories + d /etc/systemd/system 0755 root root - + d /etc/systemd/user 0755 root root - + + # APT directories + d /var/lib/apt 0755 root root - + d /var/cache/apt 0755 root root - diff --git a/test-manifest.yaml b/test-manifest.yaml new file mode 100644 index 0000000..bac5919 --- /dev/null +++ b/test-manifest.yaml @@ -0,0 +1,43 @@ +api_version: "1.0" +kind: "tree" +metadata: + ref_name: "test/debian-minimal" + version: "0.1.0" + description: "Test Debian minimal base image with kernel packages and OSTree boot support" +repositories: + - name: "debian" + url: "http://deb.debian.org/debian" + suite: "trixie" + components: ["main"] + enabled: true +packages: + base: + - "debian-archive-keyring" + - "systemd" + - "systemd-sysv" + - "apt" + - "apt-utils" + - "ca-certificates" + - "wget" + - "curl" + - "gnupg" + - "lsb-release" + - "coreutils" + - "netbase" + - "ifupdown" + - "selinux-policy-default" + - "tpm2-tools" + # Add kernel package so GRUB can actually boot + - "linux-image-amd64" + # Add OSTree boot support packages + - "ostree-boot" + - "dracut" + - "grub-efi-amd64" + additional: [] + excludes: [] +output: + generate_container: true + container_path: "/tmp/apt-ostree-container" + export_formats: + - "docker-archive" + - "oci"