Zum Hauptinhalt springen

Container-Builds in Woodpecker CI: neues Podman-Plugin, privater Agent und Privilegien-Filter

Inhaltsverzeichnis

Drei aktuelle Releases decken zusammen den Container-Build-Workflow in Woodpecker CI ab: ein Podman-basiertes Build-Plugin für beliebige Woodpecker-Agents, ein privater Agent auf Hetzner Cloud als Heimat für Pipelines mit Secrets oder großen Builds, und ein Filter-Proxy, der entscheidet, welche Plugin-Images auf dem eigenen Agent privilegiert laufen dürfen.

Woodpecker CI Plugin Podman 0.1.0
#

Ein direkter Wrapper um podman build, podman push und podman manifest push --all. Das Plugin bietet damit dasselbe Interface, das man auch lokal von podman kennt.

Codebergs public CI: das Plugin läuft out of the box. Mehr als CLONE_NEWUSER braucht es nicht, und das ist im Default-Seccomp-Profil erlaubt. Storage fällt auf vfs zurück (kein /dev/fuse auf den public Agents), Multi-Arch-Builds funktionieren, soweit die Runner-Hosts die nötige binfmt_misc-Konfiguration mitbringen.

Self-hosted Agent (Chilly Willy Agent v0.3.0 als Referenz-Setup): overlay plus fuse-overlayfs (deutlich schneller bei Builds mit vielen Layern), echte cross-arch RUN-Steps für beliebige Manifest-Listen-Einträge und Privilegien-Force-Escalation via Agent Guard.

steps:
  - name: publish
    image: head1328/woodpecker-ci-plugin-podman
    settings:
      repo: head1328/myapp
      tags: latest
      platforms: linux/amd64,linux/arm64
      username:
        from_secret: docker_hub_user
      password:
        from_secret: docker_hub_token

Bewusste Designentscheidung: das Plugin macht keine eigene binfmt_misc-Registrierung im Container. Seit Linux 6.7 ist binfmt_misc per User-Namespace skoped, und in nested rootless Podman lassen sich qemu-Handler innerhalb der Build-Sub-Runtime nicht mehr zuverlässig setzen. Das Plugin verlässt sich stattdessen auf den Host: wenn dort die qemu-Handler mit F (fix-binary) registriert sind, klappen cross-arch RUN-Steps; wenn nicht, scheitert der Build sauber.

Chilly Willy Agent v0.3.0
#

Der Agent provisioniert einen privaten Woodpecker-Agent auf Hetzner Cloud und registriert ihn bei einer Woodpecker-Instanz (etwa der von Codeberg). Eine ausführliche Vorstellung gab es bereits zum Initial-Release ; 0.3.0 bringt drei größere Änderungen:

  • Native Multi-Arch im Host-Kernel: beim ersten Boot installiert eine systemd-Oneshot-Unit qemu-user-static als rpm-ostree-Layer und startet das System neu. Beim zweiten Boot registriert systemd-binfmt die qemu-Interpreter aus /usr/lib/binfmt.d/qemu-*-static.conf mit dem F-Flag im Host-Kernel. Der Flag hält den Interpreter-FD bereits beim Registrieren offen, was ihn jedem geschachtelten rootless Userns zugänglich macht - inklusive der Build-Sub-Runtime von Podman.

    [Unit]
    Description=Layer qemu-user-static via rpm-ostree for cross-arch builds
    After=hetzner-ipv6.service network-online.target nss-lookup.target
    Before=zincati.service boot-complete.target
    ConditionPathExists=!/var/lib/cwa-qemu-layered.stamp
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    ExecStart=/usr/bin/rpm-ostree install --allow-inactive qemu-user-static
    ExecStart=/usr/bin/touch /var/lib/cwa-qemu-layered.stamp
    ExecStart=/usr/bin/systemctl reboot
  • Zwei Presets: secure (high-security baseline, vfs+chroot, keine Privileged-Plugins) und privileged (overlay+FUSE, Privilegien-Eskalation via Allowlist). Beide erben die qemu binfmt-Handler aus dem Host-Kernel und können damit Multi-Arch-Manifests bauen.

  • Wally Walrus Agent Guard v0.2.0 Integration plus deterministisches Update-Fenster: der Agent spricht über den Guard-Socket mit Podman, Zincati-Reboots sind auf ein tägliches Fenster von 03:00 bis 05:00 UTC begrenzt statt der unkontrollierten immediate-Strategie.

Wally Walrus Agent Guard v0.2.0
#

Der Agent Guard sitzt als Filter-Reverse-Proxy zwischen Woodpecker-Agent und Podman-Socket. Jedes POST /containers/create wird inspiziert und gegen die privileged_images-Allowlist des Agent-Operators abgeglichen.

Mit 0.2.0 wird aus dem passiven Filter eine aktive Policy. Drei Fälle:

  1. Image auf der Allowlist, Server sendet Privileged=true: durchgelassen.
  2. Image auf der Allowlist, Server sendet Privileged=false: Der Agent Guard force-eskaliert auf Privileged=true (neu in 0.2.0). Der Agent-Operator entscheidet damit eigenständig, welche Plugin-Images privilegiert laufen - unabhängig von der Server-Allowlist.
  3. Image nicht auf der Allowlist, Server sendet Privileged=true: HTTP 403, der Container-Create wird abgelehnt.

Das ist relevant für die Codeberg-Welt: Codeberg pflegt WOODPECKER_PLUGINS_PRIVILEGED als zentrale Liste. Welche Plugins Codeberg privilegieren lässt, entscheiden die Codeberg-Admins, nicht die Repository-Owner. Wenn man auf seinem eigenen Agent ein anderes Plugin (oder eine andere Version) privilegiert laufen lassen will, ohne mit der Codeberg-Liste in Konflikt zu geraten, kann der Agent Guard das auf Agent-Seite force-eskalieren - streng auf die eigene Allowlist beschränkt.

Zusammenspiel im privileged-Preset
#

Die Demo-Pipeline .woodpecker/privileged.yml im Agent-Repo testet das Setup end-to-end mit vier Podman-Plugin-Steps: jeweils gegen den rollenden :next-Tag und gegen den per Digest gepinnten Release-Tag :0.1.0@sha256:c3f066..., einmal single-arch (linux/amd64), einmal multi-arch (linux/amd64,linux/arm64).

Im multi-arch-Lauf passiert konkret Folgendes:

  1. Woodpecker schickt den Container-Create an die Agent-API.
  2. Codeberg hat head1328/woodpecker-ci-plugin-podman nicht in seiner Server-Allowlist, also kommt der Request mit Privileged=false.
  3. Der Agent Guard sieht das Image in WALLY_GUARD_PRIVILEGED_IMAGES, schreibt den Request auf Privileged=true um und leitet ihn an Podman weiter.
  4. Das Plugin läuft im privilegierten Pipeline-Container und ruft podman build --platform=linux/amd64,linux/arm64 --manifest=... auf.
  5. Für den linux/arm64-Build öffnet podman build einen frischen rootless Userns. Der erste exec eines arm64-Binaries (/bin/sh aus dem Alpine-Image) triggert den binfmt_misc-Lookup im Kernel; der Kernel findet qemu-aarch64 mit F-Flag und nutzt den bereits offenen Interpreter-FD direkt.
  6. RUN apk add jq ca-certificates läuft im arm64-Container über qemu-aarch64-static. uname -m meldet aarch64.

Lizenzen
#

Alle drei Projekte stehen unter Apache 2.0.

Verwendete Projekte
#

  • Woodpecker CI - container-nativer CI-Agent
  • Codeberg - Forgejo-basierte Git-Forge mit Woodpecker-CI-Integration
  • Fedora CoreOS - minimales, transaktional aktualisiertes Container-Host-OS
  • Podman - daemonless Container-Runtime
  • QEMU - Maschinen-Emulator (qemu-user-static für die Userspace-Emulation)
  • Hetzner Cloud - Cloud-Provider mit IPv6-only-Servern

Hat dir das hier geholfen?
Unterstütze gerne via: