Skip to content

docker_*.sh: add kvm support, X11 authentication, update README.md instructions. Bump docker image to v.0.2.7 with pinned hash under CircleCI#2036

Merged
tlaurion merged 4 commits intolinuxboot:masterfrom
tlaurion:bugfix-qemu_kvm
Feb 8, 2026

Conversation

@tlaurion
Copy link
Collaborator

@tlaurion tlaurion commented Dec 15, 2025

TODO after merge:

  • push v0.2.7 docker image as latest

Docker Workflows and Reproducibility Tooling Refactor

Overview

This changeset introduces comprehensive improvements to Heads' Docker-based build system, focusing on reproducibility, maintainability, and user experience.


Files Changed

Modified Files

.circleci/config.yml

  • Updated all 4 Docker image references from v0.2.5 to digest-pinned v0.2.7
  • Changed from mutable tag format: tlaurion/heads-dev-env:v0.2.5
  • To immutable digest format: tlaurion/heads-dev-env@sha256:5f890f3d1b6b57f9e567191695df003a2ee880f084f5dfe7a5633e3e8f937479
  • Added version comments above each image reference

README.md

Major documentation expansion including:

  • Docker installation and setup guide
  • Comprehensive wrapper scripts reference (docker_repro.sh, docker_local_dev.sh, docker_latest.sh)
  • Environment variables documentation (all HEADS_* variables)
  • QEMU/Docker integration notes (no host-side QEMU install needed)
  • Docker image management commands (inspect, clean, prune)
  • qemu-img snapshot workflow
  • Reproducibility verification guide with examples
  • Maintainer workflow for updating Docker images
  • Digest pinning and supply-chain security guidance
  • Helper script reference (get_digest.sh, pin-and-run.sh, check_reproducibility.sh)

docker_latest.sh

  • Refactored to source docker/common.sh for shared logic
  • Added wrapper-specific usage() function
  • Integrated resolve_docker_image for digest resolution
  • Added SIGINT trap for cleanup
  • Uses require_docker check
  • Supports DOCKER_LATEST_DIGEST override

docker_local_dev.sh

  • Refactored to source docker/common.sh
  • Added wrapper-specific usage() function
  • Integrated maybe_rebuild_local_image for conditional rebuilds
  • Added reproducibility check support (HEADS_CHECK_REPRODUCIBILITY=1)
  • Uses shared ensure_nix_and_flakes infrastructure
  • Added SIGINT trap
  • Supports HEADS_SKIP_DOCKER_REBUILD=1

docker_repro.sh

  • Refactored to source docker/common.sh
  • Added wrapper-specific usage() function
  • Integrated digest pinning via DOCKER_REPRO_DIGEST file/env var
  • Uses resolve_docker_image with digest support
  • Added SIGINT trap
  • Cleaner help output

flake.nix

  • Minor addition (exact change not visible in summary)

initrd/bin/kexec-seal-key

  • Updated LUKS device regex to use POSIX character classes
  • Changed from [ \t] to [[:space:]] for better portability

targets/qemu.md

  • Expanded wrapper scripts documentation
  • Updated help text guidance (wrappers show focused help, common.sh shows full reference)
  • Added environment variable reference
  • Docker/QEMU integration notes
  • USB token passthrough details

New Files

docker/DOCKER_REPRO_DIGEST

  • Pinned digest file for ./docker_repro.sh
  • Contains canonical image digest: sha256:5f890f3d1b6b57f9e567191695df003a2ee880f084f5dfe7a5633e3e8f937479
  • Version comment: v0.2.7
  • Supports multiple digest formats (normalized internally)

docker/check_reproducibility.sh

  • Standalone reproducibility checker
  • Compares local Docker image digest vs remote registry
  • Usage: ./docker/check_reproducibility.sh [local_image] [remote_image]
  • Delegates to compare_image_reproducibility in common.sh
  • Supports HEADS_MAINTAINER_DOCKER_IMAGE override

docker/common.sh

Shared helper library providing:

  • ensure_nix_and_flakes() — Nix installation and flakes setup with interactive prompts
  • resolve_docker_image() — Image reference resolution with digest pinning support
  • maybe_rebuild_local_image() — Conditional Docker image rebuild when flake files are dirty
  • kill_usb_processes() — Automatic USB device cleanup (scdaemon/pcscd)
  • build_docker_opts() — Docker runtime options construction (volumes, X11, USB, etc.)
  • run_docker() — Container execution wrapper with cleanup
  • get_remote_config_digest() — Fetch remote image config digest via skopeo/API
  • get_local_image_id() — Extract local image ID (config digest)
  • compare_image_reproducibility() — Compare local vs remote image digests
  • resolve_repro_remote_image() — Default reproducibility remote image resolution
  • require_docker() — Docker availability check
  • Xauthority temp file handling with cleanup
  • Color support for TTY output
  • Comprehensive usage documentation

docker/fetch_nix_installer.sh

  • Downloads Nix single-user installer with checksum verification
  • Supports --version nix-X.Y.Z or --url <custom-url>
  • Fetches published .sha256 from releases.nixos.org when available
  • Prints sha256 for manual verification
  • Never auto-executes installer (supply-chain safety)
  • Usage: ./docker/fetch_nix_installer.sh --version nix-2.33.2

docker/get_digest.sh

  • Fetches Docker image digests from registry or local Docker
  • Methods: local inspect → skopeo → Docker Hub API → docker pull
  • Supports -y/--yes for auto-pull
  • Interactive prompt when image not local
  • Outputs both repo@digest and raw sha256:... for easy scripting
  • Usage: ./docker/get_digest.sh [-y] <image:tag>
  • Example: ./docker/get_digest.sh tlaurion/heads-dev-env:v0.2.7

docker/pin-and-run.sh

  • Convenience wrapper to pin digest and run command in one step
  • Obtains digest via get_digest.sh and exports DOCKER_LATEST_DIGEST
  • Usage: ./docker/pin-and-run.sh [-y] [-w wrapper] <image:tag> -- <command>
  • Example: ./docker/pin-and-run.sh tlaurion/heads-dev-env:v0.2.7 -- make BOARD=x230
  • Defaults to ./docker_latest.sh if no wrapper specified
  • Supports -y for non-interactive auto-pull

Key Features

1. Reproducibility Infrastructure

  • Digest Pinning: CircleCI and docker_repro.sh now use immutable @sha256:... references
  • Verification Workflow: HEADS_CHECK_REPRODUCIBILITY=1 compares local builds vs published images
  • Supply-Chain Safety: Documented workflows for verifying Docker image integrity

2. Code Consolidation

  • Shared Logic: 1,238 lines of common code extracted from wrapper scripts
  • DRY Principle: USB handling, X11 setup, Nix infrastructure now centralized
  • Maintainable: Single source of truth for Docker runtime configuration

3. Enhanced Documentation

  • Comprehensive Guide: README expanded from basic instructions to full reference
  • All Environment Variables: Complete documentation of HEADS_* and DOCKER_* variables
  • Maintainer Workflows: Step-by-step procedures for updating Docker images
  • Examples: Real commands with expected output for common tasks

4. Developer Tools

  • 4 New Helper Scripts: Digest management, reproducibility checks, Nix installer handling
  • Interactive Prompts: Nix/flakes setup with supply-chain safety guardrails
  • Flexible Overrides: Fork maintainers can override HEADS_MAINTAINER_DOCKER_IMAGE

5. Improved Help System

  • Wrapper-Specific Help: Each script shows only relevant variables (--help)
  • Full Reference: Run docker/common.sh directly for complete environment documentation
  • Contextual Guidance: TTY-aware diagnostics and user-friendly error messages

Older notes
My bad: I always only tested qemu boards under QubesOS (which as of now still doesn't permit nested xen->kvm virtualization)

This change permits to run things under qemu+kvm

Tested

  • installation with media and default partition schemes
  • sealing firmware measurements + setting default boot option + TPM Disk Unlock Key when requested (to add additional LUKS key that is unsealed from TPM when coreboot+heads measurements valid + TPM DUK passphrase)

Note: TPM DUK is mitigation for any OSes not providing efifb in their initramfs, prior of specialized drm+gpu drivers being loaded (at LUKS passphrase prompt early in init/systemd).

ISO tested:

  • ubuntu-25.10-desktop-amd64.iso:
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 QEMU_MEMORY_SIZE=16G QEMU_DISK_SIZE=30G INSTALL_IMG=ubuntu-25.10-desktop-amd64.iso
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 PUBKEY_ASC=pubkey.asc USB_TOKEN=Nitrokey3NFC inject_gpg
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 PUBKEY_ASC=pubkey.asc USB_TOKEN=Nitrokey3NFC run
  • debian-13.2.0-amd64-DVD-1.iso
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 QEMU_MEMORY_SIZE=16G QEMU_DISK_SIZE=30G INSTALL_IMG=debian-13.2.0-amd64-DVD-1.iso run
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 PUBKEY_ASC=pubkey.asc USB_TOKEN=Nitrokey3NFC inject_gpg
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 PUBKEY_ASC=pubkey.asc USB_TOKEN=Nitrokey3NFC run
  • Fedora-Workstation-Live-43-1.6.x86_64.iso
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 QEMU_MEMORY_SIZE=16G QEMU_DISK_SIZE=30G INSTALL_IMG=Fedora-Workstation-Live-43-1.6.x86_64.iso run
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 PUBKEY_ASC=pubkey.asc USB_TOKEN=Nitrokey3NFC inject_gpg
./docker_repro.sh make BOARD=qemu-coreboot-fbwhiptail-tpm2 PUBKEY_ASC=pubkey.asc USB_TOKEN=Nitrokey3NFC run

Also:

  • Docker image bumped to v0.2.7 to test docker_local_dev.sh and add needed coreboot-utils to use diffoscope on roms from within docker image to test reproducibility issues (none discovered).
  • CircleCI now uses pinned commit, superseeding Use docker "immutable identifier" instead of tag #2013
  • docker_local_dev.sh will help with deploying nix if not present on system, probing user to validate hash and guiding user from where (heuristics). This is useful to maintain docker images.

TODO:

  • make sure builds are reproducible: 404f56fdf6a63226fddc97e0059703f696ad47bfad2881838845647e8b04799a /root/heads/build/x86/EOL_t480-hotp-maximized/heads-EOL_t480-hotp-maximized-v0.2.1-2921-ge931ff1.rom

Copilot AI review requested due to automatic review settings December 15, 2025 23:57
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the Docker wrapper scripts to enable KVM acceleration and X11 GUI support for QEMU board testing. Previously tested only under QubesOS, these changes allow the scripts to work with standard Linux environments running KVM-accelerated QEMU with GTK windows.

Key changes:

  • Added /dev/kvm device passthrough to enable KVM hardware acceleration in containers
  • Added X11 socket and authentication mounting to support GUI applications (GTK windows)

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 18 comments.

File Description
docker_repro.sh Added KVM device passthrough and X11 authentication mounts to enable hardware-accelerated QEMU with GUI support
docker_local_dev.sh Added KVM device passthrough and X11 authentication mounts to enable hardware-accelerated QEMU with GUI support
docker_latest.sh Added KVM device passthrough and X11 authentication mounts to enable hardware-accelerated QEMU with GUI support

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 18 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tlaurion tlaurion marked this pull request as draft December 17, 2025 22:18
@tlaurion tlaurion force-pushed the bugfix-qemu_kvm branch 4 times, most recently from 61b1ccf to 79e115f Compare February 3, 2026 18:37
@tlaurion tlaurion marked this pull request as ready for review February 3, 2026 19:45
@tlaurion tlaurion force-pushed the bugfix-qemu_kvm branch 2 times, most recently from b26b8c5 to f4c3a85 Compare February 3, 2026 20:19
@tlaurion tlaurion requested a review from Copilot February 3, 2026 21:44
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tlaurion tlaurion force-pushed the bugfix-qemu_kvm branch 2 times, most recently from 8c27511 to c18ec25 Compare February 4, 2026 01:37
@tlaurion tlaurion requested a review from Copilot February 4, 2026 01:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 17 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Add shared Docker helpers and wrapper-specific help while keeping full env reference in common.sh

- Introduce digest tooling (get_digest, pin-and-run, fetch_nix_installer) and pin DOCKER_REPRO_DIGEST

- Rework reproducibility checks (config digest comparison, remote resolution) and improve docker load/Xauthority handling

- Update README and QEMU docs; tighten initrd kexec-seal-key parsing

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
Upgrade the Docker development environment image from v0.2.5 to v0.2.7. This version
adds coreboot-utils as a dependency, which is required by diffoscope for ROM analysis
and component extraction (ifdtool, cbfsutils).

The image has been verified to be reproducible (unchanged content after push to docker.io).

Changes:

.circleci/config.yml:
- Pin all Docker image references from :v0.2.5 to @sha256:5f890f3d1b6b57f9e567191695df003a2ee880f084f5dfe7a5633e3e8f937479 (v0.2.7)
- Add version comment for human readability (# Docker image: tlaurion/heads-dev-env:v0.2.7)
- All four job definitions (prep_env, build_and_persist, build, save_cache) updated
- Ensures reproducible CI builds matching published image exactly

docker/DOCKER_REPRO_DIGEST:
- Update pinned digest from v0.2.5 to v0.2.7
- sha256:5f890f3d1b6b57f9e567191695df003a2ee880f084f5dfe7a5633e3e8f937479
- Add version comment for human readability

flake.nix:
- Add coreboot-utils dependency
- Comment: "consumed by diffoscope for ifdtool cbfsutils etc"
- Required for ROM analysis and investigation workflows

docker/get_digest.sh, docker/pin-and-run.sh:
- Update example references from v0.2.6 to v0.2.7
- Improve documentation consistency

Testing:
- Reproducibility checks against tlaurion/heads-dev-env:v0.2.7
- CircleCI workflow validated with pinned digest
- All existing functionality confirmed working

Summary of changes:
- 1 configuration update: .circleci/config.yml (docker image pinning)
- 1 digest file update: docker/DOCKER_REPRO_DIGEST
- 1 build dependency: flake.nix (coreboot-utils)
- 2 documentation updates: docker/get_digest.sh, docker/pin-and-run.sh example references

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
…us failing because race condition without sleep

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
DO_WITH_DEBUG redirects stdout/stderr through tee for logging, which
breaks interactive password prompts by interfering with TTY access.

Both increment_tpm_counter calls already redirect output to /dev/null,
so DO_WITH_DEBUG provided no logging benefit while breaking prompts.

This allows TPM owner password prompts to display correctly on console
when TPM counters need to be created or incremented.

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
@tlaurion
Copy link
Collaborator Author

tlaurion commented Feb 8, 2026

Ok enough. Next steps will be in other pull requests.

@tlaurion tlaurion merged commit e931ff1 into linuxboot:master Feb 8, 2026
5 of 6 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 17 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1225 to +1229
docker run "${DOCKER_OPTS_ARRAY[@]}" -v "${host_workdir}:${container_workdir}" -w "${container_workdir}" "${image}" -- "$@"
local status=$?
if [ "${DOCKER_XAUTH_TEMP:-0}" = "1" ] && [ -n "${DOCKER_XAUTH_FILE}" ]; then
rm -f "${DOCKER_XAUTH_FILE}" || true
fi
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary Xauthority cleanup only happens after docker run returns. If the caller traps SIGINT (as the wrappers do) and exits early, the mktemp-created Xauthority file can be left behind. Consider registering an EXIT/signal trap in common.sh (or exposing a cleanup helper) that removes DOCKER_XAUTH_FILE when DOCKER_XAUTH_TEMP=1, so cleanup also happens on Ctrl-C and other early exits.

Copilot uses AI. Check for mistakes.
Comment on lines +181 to +199
if [ -n "${manifest_digest}" ]; then
print_digest_info "${image%@*}@${manifest_digest}" "${manifest_digest}" "registry API" ""
echo "${image%@*}@${manifest_digest}"
echo "${manifest_digest}"

# Offer to pull the exact image so the local Docker daemon has a repo@digest entry.
if [ "${auto_yes}" = 1 ]; then
echo "Auto-pull enabled: pulling ${image} (progress will be shown)..." >&2
if ! docker pull "${image}" 2>&1 | sed -u 's/^/ /'; then
exit 1
fi
local_repo_digest=$(docker inspect --format='{{index .RepoDigests 0}}' "${image}" 2>/dev/null || true)
if [ -n "${local_repo_digest}" ]; then
echo "${local_repo_digest}"
echo "${local_repo_digest#*@}"
exit 0
fi
# else fall through and print the manifest digest as best-effort
exit 0
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a manifest digest is found via the registry API, the script immediately prints repo@digest + digest to stdout, and then (if the user opts to pull / --yes) prints a second repo@digest + digest pair after the pull. This breaks the stated contract of emitting a single 2-line result and can confuse callers that parse the first/second line. Consider deferring stdout output until after the pull decision (or only printing the pulled result when a pull occurs, and writing intermediate info to stderr only).

Copilot uses AI. Check for mistakes.
Comment on lines +433 to +474
wait_for_usb_devices() {
TRACE_FUNC
if [ ! -d /sys/bus/usb/devices ] || [ ! -r /proc/uptime ]; then
DEBUG "USB sysfs or uptime not available, skipping wait"
return
fi

local start now elapsed
start=$(awk '{print $1}' /proc/uptime)
DEBUG "Waiting for USB peripheral devices (not just hubs) - max 2s timeout"

local iteration=0
while :; do
iteration=$((iteration + 1))

# Check for actual USB peripheral devices (format: bus-port like 1-1, 5-3)
# Root hubs are named usb1, usb2, etc. - we want devices downstream from them
# Pattern: /sys/bus/usb/devices/[0-9]*-[0-9]*/idVendor (e.g., 1-1, 5-3.2)
local peripheral_count=0
if [ -d /sys/bus/usb/devices ]; then
# Count devices matching bus-port pattern (not usb* root hubs)
for dev in /sys/bus/usb/devices/*-*/idVendor; do
if [ -r "$dev" ]; then
peripheral_count=$((peripheral_count + 1))
fi
done
fi

now=$(awk '{print $1}' /proc/uptime)
elapsed=$(awk -v s="$start" -v n="$now" 'BEGIN{printf "%.3f", n - s}')

if [ $peripheral_count -gt 0 ]; then
DEBUG "USB peripheral devices ready after ${elapsed}s (iteration $iteration): found $peripheral_count device(s)"
return
fi

# Timeout after 2 seconds
if awk -v s="$start" -v n="$now" 'BEGIN{exit (n - s > 2.0) ? 0 : 1}'; then
DEBUG "USB wait timeout at ${elapsed}s (iter $iteration): only found $peripheral_count peripheral device(s)"
return
fi
done
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait_for_usb_devices() uses a tight polling loop with no sleep/backoff and executes multiple awk processes per iteration. In initrd this can burn CPU for up to the full 2s timeout and slow boot on low-power devices. Consider adding a small bounded sleep/backoff (e.g., 10–50ms) or using an event-driven wait (udev settle if available) while keeping the overall timeout.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant