Initial commit: Comprehensive Debian bootc documentation
- Complete documentation for all bootc commands and subcommands - Debian-specific adaptations and workarounds - Manual installation methods to bypass bootc reliability issues - Technical guides with Rust source code analysis - Flowcharts and external command references - Hidden command documentation (bootc internals, state, etc.) - Composefs integration analysis - Base image creation guides (with and without bootc binary) - Management scripts and automation - Comprehensive troubleshooting and examples
This commit is contained in:
commit
526f1c1afd
67 changed files with 34174 additions and 0 deletions
1162
building/base-images-wo-bootc.md
Normal file
1162
building/base-images-wo-bootc.md
Normal file
File diff suppressed because it is too large
Load diff
471
building/base-images.md
Normal file
471
building/base-images.md
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
# Building Base bootc Images
|
||||
|
||||
This document provides detailed instructions for creating base bootc images using two different approaches:
|
||||
|
||||
1. **Method 1**: Building from an existing OCI image
|
||||
2. **Method 2**: Building from scratch using debootstrap/mmdebstrap
|
||||
|
||||
## Important Note
|
||||
|
||||
**bootc does not create base images** - it only deploys and manages them. Base image creation requires separate tools and processes as described below.
|
||||
|
||||
## Method 1: Building from Existing OCI Image
|
||||
|
||||
This method takes an existing Debian OCI image and converts it into a bootc-compatible base image.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
```bash
|
||||
# Install required tools
|
||||
sudo apt update
|
||||
sudo apt install -y podman buildah skopeo
|
||||
|
||||
# Install bootc for validation
|
||||
git clone https://github.com/containers/bootc.git
|
||||
cd bootc
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
### Step 1: Create Containerfile
|
||||
|
||||
Create a `Containerfile.base`:
|
||||
|
||||
```dockerfile
|
||||
# Containerfile.base - Convert existing Debian image to bootc base
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install essential packages for bootc
|
||||
RUN apt update && apt install -y \
|
||||
systemd \
|
||||
systemd-sysusers \
|
||||
systemd-tmpfiles \
|
||||
kernel \
|
||||
initramfs-tools \
|
||||
grub2 \
|
||||
grub2-common \
|
||||
efibootmgr \
|
||||
ostree \
|
||||
&& apt clean
|
||||
|
||||
# Create essential directories
|
||||
RUN mkdir -p /usr/lib/systemd/system \
|
||||
/usr/lib/systemd/user \
|
||||
/etc/systemd/system \
|
||||
/etc/systemd/user \
|
||||
/var/lib/systemd \
|
||||
/run/systemd
|
||||
|
||||
# Configure systemd as init
|
||||
RUN ln -sf /lib/systemd/systemd /sbin/init
|
||||
|
||||
# Set up basic systemd configuration
|
||||
RUN systemctl set-default multi-user.target
|
||||
|
||||
# Create essential systemd services
|
||||
RUN systemctl enable systemd-resolved.service \
|
||||
systemd-networkd.service \
|
||||
systemd-timesyncd.service
|
||||
|
||||
# Configure basic networking
|
||||
RUN echo -e "[Match]\nName=*\n\n[Network]\nDHCP=yes" > /etc/systemd/network/80-dhcp.network
|
||||
|
||||
# Set up basic users and groups
|
||||
RUN systemd-sysusers --root=/ --create
|
||||
|
||||
# Configure tmpfiles
|
||||
RUN systemd-tmpfiles --create --root=/
|
||||
|
||||
# Install bootc (for validation)
|
||||
COPY --from=quay.io/containers/bootc:latest /usr/bin/bootc /usr/bin/bootc
|
||||
|
||||
# Validate the image
|
||||
RUN bootc container lint
|
||||
|
||||
# Required bootc labels
|
||||
LABEL containers.bootc 1
|
||||
LABEL ostree.bootable 1
|
||||
|
||||
# Set default command
|
||||
CMD ["/lib/systemd/systemd"]
|
||||
```
|
||||
|
||||
### Step 2: Build the Base Image
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# build-base-from-existing.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
IMAGE_NAME="debian-bootc-base"
|
||||
TAG="${1:-latest}"
|
||||
|
||||
echo "🏗️ Building Debian bootc Base Image from Existing OCI"
|
||||
echo "Image: ${IMAGE_NAME}:${TAG}"
|
||||
|
||||
# Build the base image
|
||||
echo "📦 Building container image..."
|
||||
podman build -f Containerfile.base -t "${IMAGE_NAME}:${TAG}" .
|
||||
|
||||
echo "✅ Base image built successfully!"
|
||||
echo "Image: ${IMAGE_NAME}:${TAG}"
|
||||
|
||||
# Show image info
|
||||
echo ""
|
||||
echo "📊 Image Information:"
|
||||
podman images "${IMAGE_NAME}:${TAG}"
|
||||
|
||||
echo ""
|
||||
echo "🚀 Ready to build application layers!"
|
||||
echo "Example: podman build -f examples/nginx/Containerfile -t ${IMAGE_NAME}:nginx"
|
||||
```
|
||||
|
||||
### Step 3: Test the Base Image
|
||||
|
||||
```bash
|
||||
# Test the base image
|
||||
podman run --rm -it debian-bootc-base:latest /bin/bash
|
||||
|
||||
# Validate with bootc
|
||||
bootc container lint debian-bootc-base:latest
|
||||
```
|
||||
|
||||
## Method 2: Building from Scratch with debootstrap/mmdebstrap
|
||||
|
||||
This method creates a completely custom Debian base image from scratch.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
```bash
|
||||
# Install required tools
|
||||
sudo apt update
|
||||
sudo apt install -y \
|
||||
debootstrap \
|
||||
mmdebstrap \
|
||||
podman \
|
||||
buildah \
|
||||
skopeo \
|
||||
qemu-user-static
|
||||
|
||||
# Install bootc for validation
|
||||
git clone https://github.com/containers/bootc.git
|
||||
cd bootc
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
### Step 1: Create Root Filesystem
|
||||
|
||||
Create a script to build the root filesystem:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# build-rootfs.sh - Build Debian rootfs from scratch
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOTFS_DIR="rootfs"
|
||||
DEBIAN_RELEASE="bookworm"
|
||||
ARCH="amd64"
|
||||
|
||||
echo "🏗️ Building Debian Root Filesystem from Scratch"
|
||||
echo "Release: ${DEBIAN_RELEASE}"
|
||||
echo "Architecture: ${ARCH}"
|
||||
echo "Rootfs: ${ROOTFS_DIR}/"
|
||||
|
||||
# Clean up previous build
|
||||
if [ -d "${ROOTFS_DIR}" ]; then
|
||||
echo "🧹 Cleaning up previous build..."
|
||||
sudo rm -rf "${ROOTFS_DIR}"
|
||||
fi
|
||||
|
||||
# Create rootfs directory
|
||||
mkdir -p "${ROOTFS_DIR}"
|
||||
|
||||
# Build rootfs with mmdebstrap (faster and more reliable)
|
||||
echo "📦 Building rootfs with mmdebstrap..."
|
||||
sudo mmdebstrap \
|
||||
--arch="${ARCH}" \
|
||||
--variant=minbase \
|
||||
--include=systemd,systemd-sysusers,systemd-tmpfiles,kernel,initramfs-tools,grub2,grub2-common,efibootmgr,ostree \
|
||||
"${DEBIAN_RELEASE}" \
|
||||
"${ROOTFS_DIR}" \
|
||||
http://deb.debian.org/debian
|
||||
|
||||
echo "✅ Rootfs built successfully!"
|
||||
|
||||
# Set up basic systemd configuration
|
||||
echo "⚙️ Configuring systemd..."
|
||||
sudo chroot "${ROOTFS_DIR}" systemctl set-default multi-user.target
|
||||
|
||||
# Create essential systemd services
|
||||
sudo chroot "${ROOTFS_DIR}" systemctl enable systemd-resolved.service
|
||||
sudo chroot "${ROOTFS_DIR}" systemctl enable systemd-networkd.service
|
||||
sudo chroot "${ROOTFS_DIR}" systemctl enable systemd-timesyncd.service
|
||||
|
||||
# Configure basic networking
|
||||
sudo tee "${ROOTFS_DIR}/etc/systemd/network/80-dhcp.network" > /dev/null <<EOF
|
||||
[Match]
|
||||
Name=*
|
||||
|
||||
[Network]
|
||||
DHCP=yes
|
||||
EOF
|
||||
|
||||
# Set up users and groups
|
||||
sudo chroot "${ROOTFS_DIR}" systemd-sysusers --create
|
||||
|
||||
# Configure tmpfiles
|
||||
sudo chroot "${ROOTFS_DIR}" systemd-tmpfiles --create
|
||||
|
||||
# Create essential directories
|
||||
sudo mkdir -p "${ROOTFS_DIR}/usr/lib/systemd/system"
|
||||
sudo mkdir -p "${ROOTFS_DIR}/usr/lib/systemd/user"
|
||||
sudo mkdir -p "${ROOTFS_DIR}/etc/systemd/system"
|
||||
sudo mkdir -p "${ROOTFS_DIR}/etc/systemd/user"
|
||||
sudo mkdir -p "${ROOTFS_DIR}/var/lib/systemd"
|
||||
sudo mkdir -p "${ROOTFS_DIR}/run/systemd"
|
||||
|
||||
# Configure systemd as init
|
||||
sudo ln -sf /lib/systemd/systemd "${ROOTFS_DIR}/sbin/init"
|
||||
|
||||
# Set proper permissions
|
||||
sudo chown -R root:root "${ROOTFS_DIR}"
|
||||
sudo chmod 755 "${ROOTFS_DIR}"
|
||||
|
||||
echo "✅ Rootfs configuration complete!"
|
||||
echo "Rootfs: ${ROOTFS_DIR}/"
|
||||
```
|
||||
|
||||
### Step 2: Create Containerfile for Scratch Build
|
||||
|
||||
Create a `Containerfile.scratch`:
|
||||
|
||||
```dockerfile
|
||||
# Containerfile.scratch - Build bootc image from scratch rootfs
|
||||
FROM scratch
|
||||
|
||||
# Copy the entire rootfs
|
||||
COPY rootfs/ /
|
||||
|
||||
# Install bootc (copy from existing image)
|
||||
COPY --from=quay.io/containers/bootc:latest /usr/bin/bootc /usr/bin/bootc
|
||||
|
||||
# Validate the image
|
||||
RUN bootc container lint
|
||||
|
||||
# Required bootc labels
|
||||
LABEL containers.bootc 1
|
||||
LABEL ostree.bootable 1
|
||||
|
||||
# Set default command
|
||||
CMD ["/lib/systemd/systemd"]
|
||||
```
|
||||
|
||||
### Step 3: Build Script
|
||||
|
||||
Create the main build script:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# build-scratch-bootc.sh - Complete build script for debian-scratch-bootc
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
IMAGE_NAME="debian-scratch-bootc"
|
||||
TAG="${1:-latest}"
|
||||
ROOTFS_DIR="rootfs"
|
||||
|
||||
echo "🏗️ Building Debian Scratch Bootc Base Image"
|
||||
echo "Image: ${IMAGE_NAME}:${TAG}"
|
||||
echo "Rootfs: ${ROOTFS_DIR}/"
|
||||
|
||||
# Check if rootfs exists, build if not
|
||||
if [ ! -d "${ROOTFS_DIR}" ]; then
|
||||
echo "📦 Building rootfs..."
|
||||
./build-rootfs.sh
|
||||
fi
|
||||
|
||||
# Build the base image
|
||||
echo "📦 Building container image..."
|
||||
podman build -f Containerfile.scratch -t "${IMAGE_NAME}:${TAG}" .
|
||||
|
||||
echo "✅ Base image built successfully!"
|
||||
echo "Image: ${IMAGE_NAME}:${TAG}"
|
||||
|
||||
# Show image info
|
||||
echo ""
|
||||
echo "📊 Image Information:"
|
||||
podman images "${IMAGE_NAME}:${TAG}"
|
||||
|
||||
echo ""
|
||||
echo "🚀 Ready to build application layers!"
|
||||
echo "Example: podman build -f examples/nginx/Containerfile -t ${IMAGE_NAME}:nginx"
|
||||
```
|
||||
|
||||
### Step 4: Create Example Application Layer
|
||||
|
||||
Create an example showing how to build on top of the base image:
|
||||
|
||||
```dockerfile
|
||||
# examples/nginx/Containerfile
|
||||
FROM debian-scratch-bootc:latest
|
||||
|
||||
# Install nginx
|
||||
RUN apt update && apt install -y nginx && apt clean
|
||||
|
||||
# Configure nginx
|
||||
RUN echo "server { listen 80; location / { return 200 'Hello from bootc!'; } }" > /etc/nginx/sites-available/default
|
||||
RUN ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/
|
||||
|
||||
# Enable nginx service
|
||||
RUN systemctl enable nginx.service
|
||||
|
||||
# Validate the image
|
||||
RUN bootc container lint
|
||||
|
||||
# Required bootc labels
|
||||
LABEL containers.bootc 1
|
||||
LABEL ostree.bootable 1
|
||||
|
||||
# Set default command
|
||||
CMD ["/lib/systemd/systemd"]
|
||||
```
|
||||
|
||||
## Complete Project Structure
|
||||
|
||||
```
|
||||
debian-bootc-base/
|
||||
├── build-rootfs.sh # Rootfs build script
|
||||
├── build-scratch-bootc.sh # Main build script
|
||||
├── Containerfile.base # From existing OCI image
|
||||
├── Containerfile.scratch # From scratch build
|
||||
├── rootfs/ # Generated rootfs (Method 2)
|
||||
├── examples/
|
||||
│ ├── nginx/
|
||||
│ │ └── Containerfile # Example nginx layer
|
||||
│ └── apache/
|
||||
│ └── Containerfile # Example apache layer
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Method 1: From Existing Image
|
||||
```bash
|
||||
# Build base image from existing Debian
|
||||
./build-base-from-existing.sh
|
||||
|
||||
# Build application layer
|
||||
podman build -f examples/nginx/Containerfile -t debian-bootc-base:nginx .
|
||||
```
|
||||
|
||||
### Method 2: From Scratch
|
||||
```bash
|
||||
# Build rootfs and base image
|
||||
./build-scratch-bootc.sh
|
||||
|
||||
# Build application layer
|
||||
podman build -f examples/nginx/Containerfile -t debian-scratch-bootc:nginx .
|
||||
```
|
||||
|
||||
## Validation and Testing
|
||||
|
||||
### Validate Base Image
|
||||
```bash
|
||||
# Validate with bootc
|
||||
bootc container lint debian-bootc-base:latest
|
||||
bootc container lint debian-scratch-bootc:latest
|
||||
```
|
||||
|
||||
### Test Base Image
|
||||
```bash
|
||||
# Test the base image
|
||||
podman run --rm -it debian-bootc-base:latest /bin/bash
|
||||
podman run --rm -it debian-scratch-bootc:latest /bin/bash
|
||||
```
|
||||
|
||||
### Deploy with bootc
|
||||
```bash
|
||||
# Install to filesystem
|
||||
bootc install to-filesystem debian-bootc-base:latest
|
||||
|
||||
# Check status
|
||||
bootc status
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Permission Errors**: Ensure scripts are executable
|
||||
```bash
|
||||
chmod +x build-*.sh
|
||||
```
|
||||
|
||||
2. **Rootfs Build Fails**: Check internet connection and mirrors
|
||||
```bash
|
||||
# Test mirror connectivity
|
||||
curl -I http://deb.debian.org/debian/
|
||||
```
|
||||
|
||||
3. **bootc Validation Fails**: Check required labels and systemd configuration
|
||||
```bash
|
||||
# Check labels
|
||||
podman inspect debian-bootc-base:latest | grep -A 10 Labels
|
||||
|
||||
# Check systemd
|
||||
podman run --rm debian-bootc-base:latest systemctl --version
|
||||
```
|
||||
|
||||
4. **Image Too Large**: Optimize by removing unnecessary packages
|
||||
```bash
|
||||
# Clean up in rootfs
|
||||
sudo chroot rootfs apt autoremove --purge -y
|
||||
sudo chroot rootfs apt clean
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom Kernel
|
||||
```bash
|
||||
# Install specific kernel version
|
||||
sudo chroot rootfs apt install -y linux-image-6.1.0-10-amd64
|
||||
```
|
||||
|
||||
### Custom Systemd Services
|
||||
```bash
|
||||
# Add custom service
|
||||
sudo tee rootfs/etc/systemd/system/myapp.service > /dev/null <<EOF
|
||||
[Unit]
|
||||
Description=My Application
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/myapp
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
sudo chroot rootfs systemctl enable myapp.service
|
||||
```
|
||||
|
||||
### Security Hardening
|
||||
```bash
|
||||
# Install security packages
|
||||
sudo chroot rootfs apt install -y \
|
||||
apparmor \
|
||||
auditd \
|
||||
ufw \
|
||||
fail2ban
|
||||
|
||||
# Configure AppArmor
|
||||
sudo chroot rootfs systemctl enable apparmor.service
|
||||
```
|
||||
|
||||
This comprehensive guide provides everything needed to create base bootc images using both methods, with practical examples and troubleshooting tips.
|
||||
119
building/bootc-runtime.md
Normal file
119
building/bootc-runtime.md
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# Container runtime vs "bootc runtime"
|
||||
|
||||
Fundamentally, `bootc` reuses the OCI image format as a way to transport serialized filesystem trees with included metadata such as a `version` label, etc.
|
||||
|
||||
A bootc container operates in two basic modes. First, when invoked by a container runtime such as `podman` or `docker` (typically as part of a build process), the bootc container behaves exactly the same as any other container. For example, although there is a kernel embedded in the container image, it is not executed - the host kernel is used. There's no additional mount namespaces, etc. Ultimately, the container runtime is in full control here.
|
||||
|
||||
The second, and most important mode of operation is when a bootc container is installed to a physical or virtual machine. Here, bootc is in control; the container runtime used to build is no longer relevant. However, it's _very_ important to understand that bootc's role is quite limited:
|
||||
|
||||
* On boot, there is code in the initramfs to do a "chroot" equivalent into the target filesystem root
|
||||
* On upgrade, bootc will fetch new content, but this will not affect the running root
|
||||
|
||||
Crucially, besides setting up some mounts, bootc itself does not act as any kind of "container runtime". It does not set up pid or other namespace, does not change cgroups, etc. That remains the role of other code (typically systemd). `bootc` is not a persistent daemon by default; it does not impose any runtime overhead.
|
||||
|
||||
Another example of this: While one can add Container configuration metadata, `bootc` generally ignores that at runtime today.
|
||||
|
||||
## Labels
|
||||
|
||||
A key aspect of OCI is the ability to use standardized (or semi-standardized) labels. These are stored and rendered by `bootc`; especially the `org.opencontainers.image.version` label.
|
||||
|
||||
## Example ignored runtime metadata, and recommendations
|
||||
|
||||
### ENTRYPOINT and CMD (OCI: Entrypoint/Cmd)
|
||||
|
||||
Ignored by bootc.
|
||||
|
||||
It's recommended for bootc containers to set `CMD /sbin/init`; but this is not required.
|
||||
|
||||
The booted host system will launch from the bootloader, to the kernel+initramfs and real root however it is "physically" configured inside the image. Typically today this is using systemd in both the initramfs and at runtime; but this is up to how you build the image.
|
||||
|
||||
### ENV (OCI: Env)
|
||||
|
||||
Ignored by bootc; to configure the global system environment you can change the systemd configuration. (Though this is generally not a good idea; instead it's usually better to change the environment of individual services)
|
||||
|
||||
### EXPOSE (OCI: exposedPorts)
|
||||
|
||||
Ignored by bootc; it is agnostic to how the system firewall and network function at runtime.
|
||||
|
||||
### USER (OCI: User)
|
||||
|
||||
Ignored by bootc; typically you should configure individual services inside the bootc container to run as unprivileged users instead.
|
||||
|
||||
### HEALTHCHECK (OCI: _no equivalent_)
|
||||
|
||||
This is currently a Docker-specific metadata, and did not make it into the OCI standards. (Note podman healthchecks)
|
||||
|
||||
It is important to understand again is that there is no "outer container runtime" when a bootc container is deployed on a host. The system must perform health checking on itself (or have an external system do it).
|
||||
|
||||
Relevant links:
|
||||
|
||||
* bootc rollback
|
||||
* CentOS Automotive SIG unattended updates (note that as of right now, greenboot does not yet integrate with bootc)
|
||||
* [systemd automatic boot assessment](https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/)
|
||||
|
||||
## Kernel
|
||||
|
||||
When run as a container, the Linux kernel binary in `/usr/lib/modules/$kver/vmlinuz` is ignored. It is only used when a bootc container is deployed to a physical or virtual machine.
|
||||
|
||||
## Security properties
|
||||
|
||||
When run as a container, the container runtime will by default apply various Linux kernel features such as namespacing to isolate the container processes from other system processes.
|
||||
|
||||
None of these isolation properties apply when a bootc system is deployed.
|
||||
|
||||
## Debian-Specific Considerations
|
||||
|
||||
### SELinux
|
||||
|
||||
For more on the intersection of SELinux and current bootc (OSTree container) images, see bootc images - SELinux. Note that Debian does not enable SELinux by default, so this may be less relevant for Debian-based bootc images.
|
||||
|
||||
### AppArmor Integration
|
||||
|
||||
Debian uses AppArmor as its primary mandatory access control system. When building Debian bootc images:
|
||||
|
||||
- AppArmor profiles should be included in the base image
|
||||
- Consider how AppArmor policies will be managed across updates
|
||||
- Test that services work correctly with AppArmor enforcement
|
||||
|
||||
### Debian Security Features
|
||||
|
||||
Debian bootc images should leverage Debian's security features:
|
||||
|
||||
- **Hardened kernel**: Consider using Debian's hardened kernel packages
|
||||
- **Security updates**: Plan for how security updates will be handled through the bootc model
|
||||
- **Package signing**: Ensure proper GPG key management for package verification
|
||||
- **Firewall configuration**: Use `ufw` or `iptables` for network security
|
||||
|
||||
### Example Debian Dockerfile with Security Considerations
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install security tools and bootc dependencies
|
||||
RUN apt update && \
|
||||
apt install -y bootc ostree podman apparmor-utils ufw && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Configure AppArmor
|
||||
RUN aa-complain /usr/sbin/sshd
|
||||
|
||||
# Set up basic firewall rules
|
||||
RUN ufw --force enable && \
|
||||
ufw default deny incoming && \
|
||||
ufw default allow outgoing
|
||||
|
||||
# Install your application packages
|
||||
RUN apt update && \
|
||||
apt install -y nginx postgresql && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Configure services with proper security settings
|
||||
RUN systemctl enable nginx postgresql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see Trademark Usage.
|
||||
|
||||
206
building/guidance.md
Normal file
206
building/guidance.md
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
# Generic guidance for building Debian bootc images
|
||||
|
||||
The bootc project intends to be operating system and distribution independent as possible, similar to its related projects podman and systemd, etc.
|
||||
|
||||
The recommendations for creating bootc-compatible images for Debian will in general need to be owned by the Debian project - in particular those who create the default bootc base image(s). However, some guidance is very generic to most Linux systems (and bootc only supports Linux).
|
||||
|
||||
Let's however restate a base goal of this project:
|
||||
|
||||
> 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.
|
||||
|
||||
Every tool and technique for creating application base images should apply to the host Linux OS as much as possible.
|
||||
|
||||
## Understanding mutability
|
||||
|
||||
When run as a container (particularly as part of a build), bootc-compatible images have all parts of the filesystem (e.g. `/usr` in particular) as fully mutable state, and writing there is encouraged (see below).
|
||||
|
||||
When "deployed" to a physical or virtual machine, the container image files are read-only by default; for more, see filesystem.
|
||||
|
||||
**Important**: Do not manually create `/usr/etc` directories or copy files there. The `/usr/etc` tree is generated client-side by bootc/ostree and contains the default container image's view of `/etc`. Manually putting files into this location can create undefined behavior and will cause `bootc container lint` to fail.
|
||||
|
||||
## Installing software
|
||||
|
||||
For Debian package management using `apt`, it is very much expected that the pattern of
|
||||
|
||||
```dockerfile
|
||||
RUN apt update && apt install -y somepackage && apt clean && rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
type flow Just Works here - the same way as it does for "application" container images. This pattern is really how Docker got started.
|
||||
|
||||
There's not much special to this that doesn't also apply to application containers; but see below.
|
||||
|
||||
### Nesting OCI containers in bootc containers
|
||||
|
||||
The OCI format uses "whiteouts" represented in the tar stream as special `.wh` files, and typically consumed by the Linux kernel `overlayfs` driver as special `0:0` character devices. Without special work, whiteouts cannot be nested.
|
||||
|
||||
Hence, an invocation like
|
||||
|
||||
```dockerfile
|
||||
RUN podman pull quay.io/exampleimage/someimage
|
||||
```
|
||||
|
||||
will create problems, as the `podman` runtime will create whiteout files inside the container image filesystem itself.
|
||||
|
||||
Special care and code changes will need to be made to container runtimes to support such nesting. Some more discussion in this tracker issue.
|
||||
|
||||
## systemd units
|
||||
|
||||
The model that is most popular with the Docker/OCI world is "microservice" style containers with the application as pid 1, isolating the applications from each other and from the host system - as opposed to "system containers" which run an init system like systemd, typically also SSH and often multiple logical "application" components as part of the same container.
|
||||
|
||||
The bootc project generally expects systemd as pid 1, and if you embed software in your derived image, the default would then be that that software is initially launched via a systemd unit.
|
||||
|
||||
```dockerfile
|
||||
RUN apt update && apt install -y postgresql && apt clean && rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
Would typically also carry a systemd unit, and that service will be launched the same way as it would on a package-based system.
|
||||
|
||||
## Users and groups
|
||||
|
||||
Note that the above `postgresql` today will allocate a user; this leads to the topic of users, groups and SSH keys.
|
||||
|
||||
## Configuration
|
||||
|
||||
A key aspect of choosing a bootc-based operating system model is that _code_ and _configuration_ can be strictly "lifecycle bound" together in exactly the same way.
|
||||
|
||||
(Today, that's by including the configuration into the base container image; however a future enhancement for bootc will also support dynamically-injected ConfigMaps, similar to kubelet)
|
||||
|
||||
You can add configuration files to the same places they're expected by typical package systems on Debian - in `/usr` (preferred where possible) or `/etc`. systemd has long advocated and supported a model where `/usr` (e.g. `/usr/lib/systemd/system`) contains content owned by the operating system image.
|
||||
|
||||
`/etc` is machine-local state. However, per filesystem.md it's important to note that the underlying OSTree system performs a 3-way merge of `/etc`, so changes you make in the container image to e.g. `/etc/postgresql/postgresql.conf` will be applied on update, assuming it is not modified locally.
|
||||
|
||||
**Important**: When building bootc images, work with `/etc` normally during the build process. Do not manually create or manipulate `/usr/etc` - this is handled automatically by bootc/ostree during deployment.
|
||||
|
||||
### Prefer using drop-in directories
|
||||
|
||||
These "locally modified" files can be a source of state drift. The best pattern to use is "drop-in" directories that are merged dynamically by the relevant software. systemd supports this comprehensively; see drop-ins for example in units.
|
||||
|
||||
And instead of modifying `/etc/sudoers`, it's best practice to add a file into `/etc/sudoers.d` for example.
|
||||
|
||||
Not all software supports this, however; and this is why there is generic support for `/etc`.
|
||||
|
||||
### Configuration in /usr vs /etc
|
||||
|
||||
Some software supports generic configuration both `/usr` and `/etc` - systemd, among others. Because bootc supports _derivation_ (the way OCI containers work) - it is supported and encouraged to put configuration files in `/usr` (instead of `/etc`) where possible, because then the state is consistently immutable.
|
||||
|
||||
One pattern is to replace a configuration file like `/etc/postgresql/postgresql.conf` with a symlink to e.g. `/usr/postgres/etc/postgresql.conf` for example, although this can run afoul of SELinux labeling.
|
||||
|
||||
### Secrets
|
||||
|
||||
There is a dedicated document for secrets, which is a special case of configuration.
|
||||
|
||||
## Handling read-only vs writable locations
|
||||
|
||||
The high level pattern for bootc systems is summarized again this way:
|
||||
|
||||
* Put read-only data and executables in `/usr`
|
||||
* Put configuration files in `/usr` (if they're static), or `/etc` if they need to be machine-local
|
||||
* Put "data" (log files, databases, etc.) underneath `/var`
|
||||
|
||||
However, some software installs to `/opt/examplepkg` or another location outside of `/usr`, and may include all three types of data underneath its single toplevel directory. For example, it may write log files to `/opt/examplepkg/logs`. A simple way to handle this is to change the directories that need to be writable to symbolic links to `/var`:
|
||||
|
||||
```dockerfile
|
||||
RUN apt update && apt install -y examplepkg && apt clean && rm -rf /var/lib/apt/lists/* && \
|
||||
mv /opt/examplepkg/logs /var/log/examplepkg && \
|
||||
ln -sr /opt/examplepkg/logs /var/log/examplepkg
|
||||
```
|
||||
|
||||
The Debian bootc puppet example is one instance of this.
|
||||
|
||||
Another option is to configure the systemd unit launching the service to do these mounts dynamically via e.g.
|
||||
|
||||
```
|
||||
BindPaths=/var/log/exampleapp:/opt/exampleapp/logs
|
||||
```
|
||||
|
||||
## Building bootc Images Correctly
|
||||
|
||||
### The Right Way to Build bootc Images
|
||||
|
||||
When building bootc images, follow these principles:
|
||||
|
||||
1. **Work with `/etc` normally**: Configure files in `/etc` during the build process as you would in any container
|
||||
2. **Don't manually create `/usr/etc`**: This directory is managed automatically by bootc/ostree
|
||||
3. **Use `bootc container lint`**: Always run this to validate your image before finalizing
|
||||
4. **Let bootc handle the transformation**: The `/etc` to `/usr/etc` conversion happens during deployment, not during build
|
||||
|
||||
### Common Mistakes to Avoid
|
||||
|
||||
- ❌ **Don't do this**: Manually creating `/usr/etc` and copying files there
|
||||
- ❌ **Don't do this**: Removing `/etc` and trying to recreate it manually
|
||||
- ❌ **Don't do this**: Assuming you need to handle `/etc` normalization yourself
|
||||
|
||||
### Correct Build Pattern
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install packages normally
|
||||
RUN apt update && apt install -y your-packages && apt clean
|
||||
|
||||
# Configure /etc files normally
|
||||
RUN echo "config" > /etc/myapp/config.conf
|
||||
RUN systemctl enable myapp
|
||||
|
||||
# Let bootc validate everything
|
||||
RUN bootc container lint
|
||||
|
||||
LABEL containers.bootc 1
|
||||
LABEL ostree.bootable 1
|
||||
```
|
||||
|
||||
## Debian-Specific Considerations
|
||||
|
||||
### Package Management Integration
|
||||
|
||||
When building Debian bootc images, consider:
|
||||
|
||||
- **apt repositories**: Ensure your base image includes the necessary Debian repositories
|
||||
- **Package selection**: Choose packages that are compatible with the bootc model
|
||||
- **Dependencies**: Handle Debian package dependencies properly in your Dockerfile
|
||||
- **Security updates**: Plan for how security updates will be handled through the bootc model
|
||||
|
||||
### Debian Configuration Patterns
|
||||
|
||||
- **dpkg configuration**: Use `debconf` for non-interactive package configuration
|
||||
- **Service management**: Leverage Debian's systemd integration
|
||||
- **User management**: Follow Debian conventions for user and group creation
|
||||
- **Logging**: Use Debian's standard logging locations in `/var/log`
|
||||
|
||||
### Example Dockerfile for Debian bootc
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install bootc and dependencies
|
||||
RUN apt update && \
|
||||
apt install -y bootc ostree podman systemd && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install your application packages
|
||||
RUN apt update && \
|
||||
apt install -y nginx postgresql && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Configure services normally - bootc handles /etc automatically
|
||||
RUN systemctl enable nginx postgresql
|
||||
|
||||
# Configure /etc files normally - don't manually create /usr/etc
|
||||
RUN echo "server_name example.com;" > /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Set up proper permissions and links
|
||||
RUN ln -sf /var/log/nginx /usr/share/nginx/logs
|
||||
|
||||
# Let bootc lint check everything is correct
|
||||
RUN bootc container lint
|
||||
|
||||
LABEL containers.bootc 1
|
||||
LABEL ostree.bootable 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see Trademark Usage.
|
||||
101
building/kernel-arguments.md
Normal file
101
building/kernel-arguments.md
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
# Kernel arguments
|
||||
|
||||
The default bootc model uses "type 1" bootloader config files stored in `/boot/loader/entries`, which define arguments provided to the Linux kernel.
|
||||
|
||||
The set of kernel arguments can be machine-specific state, but can also be managed via container updates.
|
||||
|
||||
The bootloader entries are currently written by the OSTree backend.
|
||||
|
||||
More on Linux kernel arguments: [Kernel Parameters](https://docs.kernel.org/admin-guide/kernel-parameters.html)
|
||||
|
||||
## /usr/lib/bootc/kargs.d
|
||||
|
||||
Many bootc use cases will use generic "OS/distribution" kernels. In order to support injecting kernel arguments, bootc supports a small custom config file format in `/usr/lib/bootc/kargs.d` in TOML format, that have the following structure:
|
||||
|
||||
```toml
|
||||
[kargs]
|
||||
append = ["console=ttyS0", "quiet"]
|
||||
prepend = ["rd.luks.uuid=12345678-1234-1234-1234-123456789abc"]
|
||||
```
|
||||
|
||||
The `append` and `prepend` arrays contain kernel arguments that will be added to the kernel command line. Arguments in `prepend` are added at the beginning, while `append` arguments are added at the end.
|
||||
|
||||
## Local kernel argument management
|
||||
|
||||
It is currently undefined behavior to remove kernel arguments locally that are included in the base image via `/usr/lib/bootc/kargs.d`.
|
||||
|
||||
## Injecting default arguments into custom kernels
|
||||
|
||||
The Linux kernel supports building in arguments into the kernel binary, at the time of this writing via the `config CMDLINE` build option. If you are building a custom kernel, then it often makes sense to use this instead of `/usr/lib/bootc/kargs.d` for example.
|
||||
|
||||
## Debian-Specific Considerations
|
||||
|
||||
### Debian Kernel Management
|
||||
|
||||
When working with Debian bootc images:
|
||||
|
||||
- **Kernel packages**: Debian provides multiple kernel packages (linux-image-generic, linux-image-cloud, etc.)
|
||||
- **Kernel headers**: Install `linux-headers-*` packages for development
|
||||
- **Kernel modules**: Located in `/lib/modules/$(uname -r)/`
|
||||
|
||||
### Example Debian Kernel Configuration
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install kernel and bootc dependencies
|
||||
RUN apt update && \
|
||||
apt install -y linux-image-generic linux-headers-generic bootc ostree && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Configure kernel arguments for Debian
|
||||
COPY kargs.d/99-debian.conf /usr/lib/bootc/kargs.d/
|
||||
```
|
||||
|
||||
### Debian Bootloader Integration
|
||||
|
||||
Debian uses GRUB as the default bootloader:
|
||||
|
||||
- **GRUB configuration**: `/etc/default/grub`
|
||||
- **GRUB scripts**: `/etc/grub.d/`
|
||||
- **Update GRUB**: `update-grub` command
|
||||
|
||||
### Example kernel arguments configuration
|
||||
|
||||
Create `/usr/lib/bootc/kargs.d/99-debian.conf`:
|
||||
|
||||
```toml
|
||||
[kargs]
|
||||
append = [
|
||||
"console=ttyS0",
|
||||
"quiet",
|
||||
"splash",
|
||||
"systemd.show_status=false"
|
||||
]
|
||||
prepend = [
|
||||
"rd.luks.uuid=12345678-1234-1234-1234-123456789abc"
|
||||
]
|
||||
```
|
||||
|
||||
### Debian Security Considerations
|
||||
|
||||
For Debian bootc images, consider these security-related kernel arguments:
|
||||
|
||||
- **AppArmor**: `apparmor=1 security=apparmor`
|
||||
- **SELinux**: `selinux=1 security=selinux` (if using SELinux)
|
||||
- **KASLR**: `kaslr` (Kernel Address Space Layout Randomization)
|
||||
- **SMEP/SMAP**: `nosmep nosmap` (if needed for compatibility)
|
||||
|
||||
### Hardware-Specific Arguments
|
||||
|
||||
Debian bootc images may need hardware-specific kernel arguments:
|
||||
|
||||
- **Virtualization**: `console=ttyS0` for cloud instances
|
||||
- **Storage**: `root=UUID=...` for specific root device
|
||||
- **Network**: `net.ifnames=0` for predictable network interface names
|
||||
|
||||
---
|
||||
|
||||
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see Trademark Usage.
|
||||
|
||||
254
building/management-services.md
Normal file
254
building/management-services.md
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
# Management services
|
||||
|
||||
Management services in bootc systems handle operational tasks like updates, monitoring, and system maintenance. This document covers how to implement and manage these services in Debian bootc images.
|
||||
|
||||
## Overview
|
||||
|
||||
Management services are systemd services that handle:
|
||||
|
||||
- **System updates**: Managing bootc updates and rollbacks
|
||||
- **Monitoring**: Health checks and system monitoring
|
||||
- **Maintenance**: Log rotation, cleanup, and maintenance tasks
|
||||
- **Configuration**: Dynamic configuration updates
|
||||
|
||||
## Update Management
|
||||
|
||||
### bootc Update Service
|
||||
|
||||
Create a service to handle bootc updates:
|
||||
|
||||
```dockerfile
|
||||
# Create update management service
|
||||
COPY bootc-update.service /usr/lib/systemd/system/
|
||||
COPY bootc-update.timer /usr/lib/systemd/system/
|
||||
COPY update-bootc.sh /usr/local/bin/
|
||||
```
|
||||
|
||||
Service file (`/usr/lib/systemd/system/bootc-update.service`):
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Update bootc system
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/update-bootc.sh
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
```
|
||||
|
||||
Timer file (`/usr/lib/systemd/system/bootc-update.timer`):
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Run bootc update daily
|
||||
Requires=bootc-update.service
|
||||
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
### Update Script Example
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/update-bootc.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Check for updates
|
||||
if bootc update --check; then
|
||||
echo "Updates available, applying..."
|
||||
bootc update
|
||||
echo "Update completed successfully"
|
||||
else
|
||||
echo "No updates available"
|
||||
fi
|
||||
```
|
||||
|
||||
## Monitoring Services
|
||||
|
||||
### Health Check Service
|
||||
|
||||
Create a health check service:
|
||||
|
||||
```dockerfile
|
||||
# Create health check service
|
||||
COPY health-check.service /usr/lib/systemd/system/
|
||||
COPY health-check.timer /usr/lib/systemd/system/
|
||||
COPY health-check.sh /usr/local/bin/
|
||||
```
|
||||
|
||||
Health check script example:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/health-check.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Check system health
|
||||
check_system_health() {
|
||||
# Check disk space
|
||||
df -h | awk 'NR>1 {if ($5+0 > 80) exit 1}'
|
||||
|
||||
# Check memory usage
|
||||
free | awk 'NR==2{if ($3/$2 > 0.9) exit 1}'
|
||||
|
||||
# Check critical services
|
||||
systemctl is-active --quiet sshd || exit 1
|
||||
systemctl is-active --quiet systemd-resolved || exit 1
|
||||
|
||||
echo "System health check passed"
|
||||
}
|
||||
|
||||
check_system_health
|
||||
```
|
||||
|
||||
### Log Management
|
||||
|
||||
Set up log rotation and management:
|
||||
|
||||
```dockerfile
|
||||
# Configure logrotate
|
||||
COPY logrotate.conf /etc/logrotate.d/bootc
|
||||
|
||||
# Create log cleanup service
|
||||
COPY log-cleanup.service /usr/lib/systemd/system/
|
||||
COPY log-cleanup.timer /usr/lib/systemd/system/
|
||||
COPY cleanup-logs.sh /usr/local/bin/
|
||||
```
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### Dynamic Configuration Updates
|
||||
|
||||
Create a service for configuration updates:
|
||||
|
||||
```dockerfile
|
||||
# Create config management service
|
||||
COPY config-manager.service /usr/lib/systemd/system/
|
||||
COPY config-manager.sh /usr/local/bin/
|
||||
COPY config-templates/ /etc/config-templates/
|
||||
```
|
||||
|
||||
### Configuration Template Example
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/config-manager.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Update configuration from templates
|
||||
update_config() {
|
||||
local template="$1"
|
||||
local target="$2"
|
||||
|
||||
# Process template with environment variables
|
||||
envsubst < "$template" > "$target"
|
||||
|
||||
# Reload service if needed
|
||||
if systemctl is-active --quiet "$3"; then
|
||||
systemctl reload "$3"
|
||||
fi
|
||||
}
|
||||
|
||||
# Update configurations
|
||||
update_config /etc/config-templates/nginx.conf /etc/nginx/nginx.conf nginx
|
||||
update_config /etc/config-templates/sshd.conf /etc/ssh/sshd_config sshd
|
||||
```
|
||||
|
||||
## Debian-Specific Considerations
|
||||
|
||||
### Debian Service Management
|
||||
|
||||
Debian uses systemd for service management:
|
||||
|
||||
- **Service files**: `/usr/lib/systemd/system/`
|
||||
- **User services**: `~/.config/systemd/user/`
|
||||
- **Service enablement**: `systemctl enable`
|
||||
- **Service status**: `systemctl status`
|
||||
|
||||
### Example Debian Management Services
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install management tools
|
||||
RUN apt update && \
|
||||
apt install -y curl jq logrotate cron && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create management services
|
||||
COPY management-services/ /usr/lib/systemd/system/
|
||||
COPY management-scripts/ /usr/local/bin/
|
||||
|
||||
# Set up permissions
|
||||
RUN chmod +x /usr/local/bin/*.sh
|
||||
|
||||
# Enable services
|
||||
RUN systemctl enable bootc-update.timer health-check.timer log-cleanup.timer
|
||||
```
|
||||
|
||||
### Debian Package Integration
|
||||
|
||||
Integrate with Debian package management:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Update Debian packages alongside bootc
|
||||
|
||||
# Update package lists
|
||||
apt update
|
||||
|
||||
# Check for security updates
|
||||
apt list --upgradable | grep -i security
|
||||
|
||||
# Install security updates
|
||||
apt upgrade -y
|
||||
|
||||
# Clean up
|
||||
apt autoremove -y
|
||||
apt autoclean
|
||||
```
|
||||
|
||||
### Debian Monitoring Tools
|
||||
|
||||
Use Debian's monitoring tools:
|
||||
|
||||
- **htop**: Process monitoring
|
||||
- **iotop**: I/O monitoring
|
||||
- **nethogs**: Network monitoring
|
||||
- **sysstat**: System statistics
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Service Design
|
||||
|
||||
1. **Idempotent operations**: Services should be safe to run multiple times
|
||||
2. **Error handling**: Proper error handling and logging
|
||||
3. **Resource limits**: Set appropriate resource limits
|
||||
4. **Dependencies**: Define proper service dependencies
|
||||
|
||||
### Security Considerations
|
||||
|
||||
1. **Least privilege**: Run services with minimal required privileges
|
||||
2. **Secure communication**: Use TLS for network communication
|
||||
3. **Access control**: Restrict access to management interfaces
|
||||
4. **Audit logging**: Log all management operations
|
||||
|
||||
### Operational Guidelines
|
||||
|
||||
1. **Monitoring**: Monitor service health and performance
|
||||
2. **Alerting**: Set up alerts for critical failures
|
||||
3. **Documentation**: Document all management services
|
||||
4. **Testing**: Test management services in non-production environments
|
||||
|
||||
---
|
||||
|
||||
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see Trademark Usage.
|
||||
|
||||
197
building/secrets.md
Normal file
197
building/secrets.md
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# Secrets
|
||||
|
||||
Managing secrets in bootc systems requires careful consideration of security and operational requirements. This document covers various approaches for handling secrets in Debian bootc images.
|
||||
|
||||
## Overview
|
||||
|
||||
Secrets in bootc systems can be managed through several approaches:
|
||||
|
||||
- **Build-time secrets**: Embedded in the container image (not recommended for production)
|
||||
- **Runtime secrets**: Injected at deployment time
|
||||
- **External secret management**: Using external systems like HashiCorp Vault, Kubernetes secrets, etc.
|
||||
|
||||
## Build-time Secrets (Not Recommended)
|
||||
|
||||
While it's possible to embed secrets directly in the container image, this is generally not recommended for production use:
|
||||
|
||||
```dockerfile
|
||||
# NOT RECOMMENDED for production
|
||||
RUN echo "secret-password" > /etc/myapp/password
|
||||
```
|
||||
|
||||
## Runtime Secret Injection
|
||||
|
||||
### systemd Credentials
|
||||
|
||||
systemd provides a credentials system for securely passing secrets to services:
|
||||
|
||||
```dockerfile
|
||||
# Create a service that uses credentials
|
||||
COPY myapp.service /usr/lib/systemd/system/
|
||||
```
|
||||
|
||||
Service file example (`/usr/lib/systemd/system/myapp.service`):
|
||||
```ini
|
||||
[Unit]
|
||||
Description=My Application
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/myapp
|
||||
LoadCredential=password:/etc/myapp/password
|
||||
LoadCredential=api-key:/etc/myapp/api-key
|
||||
```
|
||||
|
||||
### cloud-init Integration
|
||||
|
||||
For cloud deployments, use cloud-init to inject secrets:
|
||||
|
||||
```yaml
|
||||
# cloud-init configuration
|
||||
#cloud-config
|
||||
write_files:
|
||||
- path: /etc/myapp/config
|
||||
content: |
|
||||
password: ${PASSWORD}
|
||||
api_key: ${API_KEY}
|
||||
permissions: '0600'
|
||||
owner: root:root
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Use systemd environment files for secrets:
|
||||
|
||||
```dockerfile
|
||||
# Create environment file template
|
||||
COPY myapp.env.template /usr/lib/systemd/system/myapp.env.template
|
||||
```
|
||||
|
||||
## External Secret Management
|
||||
|
||||
### HashiCorp Vault Integration
|
||||
|
||||
Create a service that fetches secrets from Vault:
|
||||
|
||||
```dockerfile
|
||||
# Install Vault client
|
||||
RUN apt update && \
|
||||
apt install -y vault && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create Vault service
|
||||
COPY vault-fetch.service /usr/lib/systemd/system/
|
||||
COPY fetch-secrets.sh /usr/local/bin/
|
||||
```
|
||||
|
||||
### Kubernetes Secrets
|
||||
|
||||
For Kubernetes deployments, use Kubernetes secrets:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: myapp-secrets
|
||||
type: Opaque
|
||||
data:
|
||||
password: <base64-encoded-password>
|
||||
api-key: <base64-encoded-api-key>
|
||||
```
|
||||
|
||||
## Debian-Specific Considerations
|
||||
|
||||
### Debian Secret Management Tools
|
||||
|
||||
Debian provides several tools for secret management:
|
||||
|
||||
- **gnupg**: For encryption/decryption
|
||||
- **openssl**: For certificate management
|
||||
- **keyutils**: For kernel keyring management
|
||||
|
||||
### Example Debian Secret Management
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install secret management tools
|
||||
RUN apt update && \
|
||||
apt install -y gnupg openssl keyutils && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create secret management service
|
||||
COPY secret-manager.service /usr/lib/systemd/system/
|
||||
COPY secret-manager.sh /usr/local/bin/
|
||||
|
||||
# Set up proper permissions
|
||||
RUN chmod 700 /usr/local/bin/secret-manager.sh
|
||||
```
|
||||
|
||||
### Debian Keyring Integration
|
||||
|
||||
Use Debian's keyring system for managing secrets:
|
||||
|
||||
```bash
|
||||
# Add secret to kernel keyring
|
||||
echo "my-secret" | keyctl padd user myapp-secret @u
|
||||
|
||||
# Retrieve secret in application
|
||||
SECRET=$(keyctl print user:myapp-secret)
|
||||
```
|
||||
|
||||
### AppArmor Considerations
|
||||
|
||||
When using AppArmor with secrets:
|
||||
|
||||
```dockerfile
|
||||
# Create AppArmor profile for secret access
|
||||
COPY usr.bin.myapp /etc/apparmor.d/
|
||||
RUN aa-enforce /etc/apparmor.d/usr.bin.myapp
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Security Guidelines
|
||||
|
||||
1. **Never embed secrets in images**: Use external secret management
|
||||
2. **Use least privilege**: Only grant access to secrets that are needed
|
||||
3. **Rotate secrets regularly**: Implement secret rotation policies
|
||||
4. **Audit secret access**: Log all secret access and usage
|
||||
5. **Use encryption**: Encrypt secrets at rest and in transit
|
||||
|
||||
### Operational Guidelines
|
||||
|
||||
1. **Use templates**: Create secret templates that can be filled at runtime
|
||||
2. **Validate secrets**: Check that secrets are valid before use
|
||||
3. **Handle failures gracefully**: Plan for secret retrieval failures
|
||||
4. **Monitor secret usage**: Track secret access patterns
|
||||
|
||||
### Example Implementation
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install dependencies
|
||||
RUN apt update && \
|
||||
apt install -y curl jq gnupg && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create secret management system
|
||||
COPY secret-manager.service /usr/lib/systemd/system/
|
||||
COPY secret-manager.sh /usr/local/bin/
|
||||
COPY secret-templates/ /etc/secret-templates/
|
||||
|
||||
# Set up proper permissions
|
||||
RUN chmod 700 /usr/local/bin/secret-manager.sh && \
|
||||
chmod 600 /etc/secret-templates/*
|
||||
|
||||
# Enable secret manager
|
||||
RUN systemctl enable secret-manager.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see Trademark Usage.
|
||||
|
||||
222
building/users-and-groups.md
Normal file
222
building/users-and-groups.md
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
# Users and groups
|
||||
|
||||
This is one of the more complex topics. Generally speaking, bootc has nothing to do directly with configuring users or groups; it is a generic OS update/configuration mechanism. (There is currently just one small exception in that `bootc install` has a special case `--root-ssh-authorized-keys` argument, but it's very much optional).
|
||||
|
||||
## Generic base images
|
||||
|
||||
Commonly OS/distribution base images will be generic, i.e. without any configuration. It is _very strongly recommended_ to avoid hardcoded passwords and ssh keys with publicly-available private keys (as Vagrant does) in generic images.
|
||||
|
||||
### Injecting SSH keys via systemd credentials
|
||||
|
||||
The systemd project has documentation for [credentials](https://systemd.io/CREDENTIALS/) which can be used in some environments to inject a root password or SSH authorized_keys. For many cases, this is a best practice.
|
||||
|
||||
At the time of this writing this relies on SMBIOS which is mainly configurable in local virtualization environments. (qemu).
|
||||
|
||||
### Injecting users and SSH keys via cloud-init, etc.
|
||||
|
||||
Many IaaS and virtualization systems are oriented towards a "metadata server" (see e.g. [AWS instance metadata](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)) that are commonly processed by software such as cloud-init or Ignition or equivalent.
|
||||
|
||||
The base image you're using may include such software, or you can install it in your own derived images.
|
||||
|
||||
In this model, SSH configuration is managed outside of the bootable image. See e.g. [GCP oslogin](https://cloud.google.com/compute/docs/oslogin) for an example of this where operating system identities are linked to the underlying Google accounts.
|
||||
|
||||
### Adding users and credentials via custom logic (container or unit)
|
||||
|
||||
Of course, systems like `cloud-init` are not privileged; you can inject any logic you want to manage credentials via e.g. a systemd unit (which may launch a container image) that manages things however you prefer. Commonly, this would be a custom network-hosted source. For example, [FreeIPA](https://www.freeipa.org/).
|
||||
|
||||
Another example in a Kubernetes-oriented infrastructure would be a container image that fetches desired authentication credentials from a CRD hosted in the API server. (To do things like this it's suggested to reuse the kubelet credentials)
|
||||
|
||||
### System users and groups (added via packages, etc)
|
||||
|
||||
It is common for packages (deb/rpm/etc) to allocate system users or groups as part of e.g `apt|dnf install <server package>` such as Apache or MySQL, and this is often done by directly invoking `useradd` or `groupadd` as part of package pre/post installation scripts.
|
||||
|
||||
With the `shadow-utils` implementation of `useradd` and the default glibc `files` this will result in changes to the traditional `/etc/passwd` and `/etc/shadow` files as part of the container build.
|
||||
|
||||
#### System drift from local /etc/passwd modifications
|
||||
|
||||
When the system is initially installed, the `/etc/passwd` in the container image will be applied and contain desired users.
|
||||
|
||||
By default (without `etc = transient`, see below), the `/etc` directory is machine-local persistent state. If subsequently `/etc/passwd` is modified local to the machine (as is common for e.g. setting a root password) then any new changes in the container image (such as users from new packages) _will not appear_ on subsequent updates by default (they will be in `/usr/etc/passwd` instead - the default image version).
|
||||
|
||||
The general best fix for this is to use `systemd-sysusers` instead of allocating a user/group at build time at all.
|
||||
|
||||
##### Using systemd-sysusers
|
||||
|
||||
See [systemd-sysusers](https://www.freedesktop.org/software/systemd/man/sysusers.d.html). For example in your derived build:
|
||||
|
||||
```dockerfile
|
||||
COPY mycustom-user.conf /usr/lib/sysusers.d
|
||||
```
|
||||
|
||||
A key aspect of how this works is that `sysusers` will make changes to the traditional `/etc/passwd` file as necessary on boot instead of at build time. If `/etc` is persistent, this can avoid uid/gid drift (but in the general case it does mean that uid/gid allocation can depend on how a specific machine was upgraded over time).
|
||||
|
||||
Note that the default `sysusers` design is that users are allocated on the client side (per machine). Avoid having non-root owned files managed by `sysusers` inside your image, especially underneath `/usr`. With the exception of `setuid` or `setgid` binaries (which should also be strongly avoided), there is generally no valid reason for having non-root owned files in `/usr` or other runtime-immutable directories.
|
||||
|
||||
#### User and group home directories and /var
|
||||
|
||||
For systems configured with persistent `/home` → `/var/home`, any changes to `/var` made in the container image after initial installation _will not be applied on subsequent updates_. If for example you inject `/var/home/someuser/.ssh/authorized_keys` into a container build, existing systems will _not_ get the updated authorized keys file.
|
||||
|
||||
#### Using DynamicUser=yes for systemd units
|
||||
|
||||
For "system" users it's strongly recommended to use systemd `DynamicUser=yes` where possible.
|
||||
|
||||
This is significantly better than the pattern of allocating users/groups at "package install time" (e.g. [Fedora package user/group guidelines](https://docs.fedoraproject.org/en-US/packaging-guidelines/UsersAndGroups/)) because it avoids potential UID/GID drift (see below).
|
||||
|
||||
#### Using systemd JSON user records
|
||||
|
||||
See [JSON user records](https://systemd.io/USER_RECORD/). Unlike `sysusers`, the canonical state for these live in `/usr` - if a subsequent image drops a user record, then it will also vanish from the system - unlike `sysusers.d`.
|
||||
|
||||
#### nss-altfiles
|
||||
|
||||
The [nss-altfiles](https://github.com/altfiles/nss-altfiles) project (long) predates systemd JSON user records. It aims to help split "system" users into `/usr/lib/passwd` and `/usr/lib/group`. It's very important to understand that this aligns with the way the OSTree project handles the "3 way merge" for `/etc` as it relates to `/etc/passwd`. Currently, if the `/etc/passwd` file is modified in any way on the local system, then subsequent changes to `/etc/passwd` in the container image _will not be applied_.
|
||||
|
||||
Some base images may have `nss-altfiles` enabled by default; this is currently the case for base images built by rpm-ostree.
|
||||
|
||||
Commonly, base images will have some "system" users pre-allocated and managed via this file again to avoid uid/gid drift.
|
||||
|
||||
In a derived container build, you can also append users to `/usr/lib/passwd` for example. (At the time of this writing there is no command line to do so though).
|
||||
|
||||
Typically it is more preferable to use `sysusers.d` or `DynamicUser=yes`.
|
||||
|
||||
### Machine-local state for users
|
||||
|
||||
At this point, it is important to understand the filesystem layout - the default is up to the base image.
|
||||
|
||||
The default Linux concept of a user has data stored in both `/etc` (`/etc/passwd`, `/etc/shadow` and groups) and `/home`. The choice for how these work is up to the base image, but a common default for generic base images is to have both be machine-local persistent state. In this model `/home` would be a symlink to `/var/home/someuser`.
|
||||
|
||||
#### Injecting users and SSH keys via at system provisioning time
|
||||
|
||||
For base images where `/etc` and `/var` are configured to persist by default, it will then be generally supported to inject users via "installers" such as Anaconda (interactively or via kickstart) or any others.
|
||||
|
||||
Typically generic installers such as this are designed for "one time bootstrap" and again then the configuration becomes mutable machine-local state that can be changed "day 2" via some other mechanism.
|
||||
|
||||
The simple case is a user with a password - typically the installer helps set the initial password, but to change it there is a different in-system tool (such as `passwd` or a GUI as part of Cockpit, GNOME/KDE/etc).
|
||||
|
||||
It is intended that these flows work equivalently in a bootc-compatible system, to support users directly installing "generic" base images, without requiring changes to the tools above.
|
||||
|
||||
#### Transient home directories
|
||||
|
||||
Many operating system deployments will want to minimize persistent, mutable and executable state - and user home directories are that
|
||||
|
||||
But it is also valid to default to having e.g. `/home` be a `tmpfs` to ensure user data is cleaned up across reboots (and this pairs particularly well with a transient `/etc` as well):
|
||||
|
||||
In order to set up the user's home directory to e.g. inject SSH `authorized_keys` or other files, a good approach is to use systemd `tmpfiles.d` snippets:
|
||||
|
||||
```
|
||||
f~ /home/someuser/.ssh/authorized_keys 600 someuser someuser - <base64 encoded data>
|
||||
```
|
||||
|
||||
which can be embedded in the image as `/usr/lib/tmpfiles.d/someuser-keys.conf`.
|
||||
|
||||
Or a service embedded in the image can fetch keys from the network and write them; this is the pattern used by cloud-init and afterburn.
|
||||
|
||||
### UID/GID drift
|
||||
|
||||
Any invocation of `useradd` or `groupadd` that does not allocate a _fixed_ UID/GID may be subject to "drift" in subsequent rebuilds by default.
|
||||
|
||||
One possibility is to explicitly force these user/group allocations into a static state, via `systemd-sysusers` (per above) or explicitly adding the users with static IDs _before_ a dpkg installation script operates on it:
|
||||
|
||||
```dockerfile
|
||||
RUN <<EORUN
|
||||
set -xeuo pipefail
|
||||
groupadd -g 10044 mycustom-group
|
||||
useradd -u 10044 -g 10044 -d /dev/null -M mycustom-user
|
||||
apt install -y mycustom-package.deb
|
||||
bootc container lint
|
||||
EORUN
|
||||
```
|
||||
|
||||
Ultimately the `/etc/passwd` and similar files are a mapping between names and numeric identifiers. A problem then becomes when this mapping is dynamic and mixed with "stateless" container image builds.
|
||||
|
||||
For example today the Debian `postgresql` package allocates a static uid of 26.
|
||||
|
||||
This means that
|
||||
|
||||
```dockerfile
|
||||
RUN apt install -y postgresql
|
||||
```
|
||||
|
||||
will always result in a change to `/etc/passwd` that allocates uid 26 and data in `/var/lib/postgres` will always be owned by that UID.
|
||||
|
||||
However in contrast, some Debian packages allocate floating users.
|
||||
|
||||
This means that each container image build (without additional work, unlike the example at the beginning of this section), may (due to package installation ordering or other reasons) result in the uid changing.
|
||||
|
||||
This can be a problem if that user maintains persistent state. Such cases are best handled by being converted to use `sysusers.d` - or again even better, using `DynamicUser=yes` (see above).
|
||||
|
||||
#### tmpfiles.d use for setting ownership
|
||||
|
||||
Systemd's tmpfiles.d provides a way to define files and directories in a way that will be processed at startup as needed. One way to work around SELinux security context and user or group ownership of a directory or file can be by using the z or Z directives.
|
||||
|
||||
These directives will adjust the access mode, user and group ownership and the SELinux security context as stated on the doc linked above.
|
||||
|
||||
For example, if we need `/var/lib/my_file.conf` to be part of the `tss` group but owned by `root` we could create a tmpfiles.d entry with:
|
||||
|
||||
```
|
||||
+z /var/lib/my_file 0640 root tss -
|
||||
```
|
||||
|
||||
## Debian-Specific Considerations
|
||||
|
||||
### Debian User Management
|
||||
|
||||
When building Debian bootc images, consider these Debian-specific patterns:
|
||||
|
||||
- **adduser vs useradd**: Debian provides both `adduser` (Debian-specific) and `useradd` (POSIX). Use `adduser` for interactive user creation and `useradd` for scripts
|
||||
- **Debian package user creation**: Many Debian packages create users via `adduser` in postinst scripts
|
||||
- **Home directory structure**: Debian uses `/home` for user home directories by default
|
||||
|
||||
### Example Debian User Management
|
||||
|
||||
```dockerfile
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install systemd and bootc dependencies
|
||||
RUN apt update && \
|
||||
apt install -y systemd bootc ostree podman && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create system users using adduser (Debian way)
|
||||
RUN adduser --system --group --disabled-password --home /var/lib/myservice myservice
|
||||
|
||||
# Or use systemd-sysusers for better integration
|
||||
COPY myservice-user.conf /usr/lib/sysusers.d/
|
||||
|
||||
# Install packages that may create users
|
||||
RUN apt update && \
|
||||
apt install -y postgresql nginx && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Configure services
|
||||
RUN systemctl enable postgresql nginx
|
||||
```
|
||||
|
||||
### Debian SSH Configuration
|
||||
|
||||
For SSH key management in Debian bootc images:
|
||||
|
||||
```dockerfile
|
||||
# Create SSH directory structure
|
||||
RUN mkdir -p /etc/ssh/sshd_config.d
|
||||
|
||||
# Add SSH configuration
|
||||
COPY sshd_config.d/99-bootc.conf /etc/ssh/sshd_config.d/
|
||||
|
||||
# Set up authorized keys via tmpfiles
|
||||
COPY ssh-keys.conf /usr/lib/tmpfiles.d/
|
||||
```
|
||||
|
||||
### Debian Group Management
|
||||
|
||||
Debian uses specific group conventions:
|
||||
|
||||
- **sudo group**: `sudo` (not `wheel` like some other distributions)
|
||||
- **docker group**: For Docker access
|
||||
- **systemd groups**: For systemd-resolved, systemd-timesyncd, etc.
|
||||
|
||||
---
|
||||
|
||||
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see Trademark Usage.
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue