
Buildah construit des images de conteneurs OCI sans démon Docker et en mode rootless. Créé par Red Hat en 2017, cet outil permet de builder des images avec de simples commandes shell (from, run, commit) ou via Dockerfiles (commande bud). Contrairement à docker build, Buildah n’exige aucun processus en arrière-plan et peut s’exécuter avec un utilisateur non privilégié — idéal pour sécuriser vos pipelines CI/CD.
Ce que vous allez apprendre :
- Pourquoi choisir Buildah : mode rootless, absence de démon, sécurité renforcée
- Construction from scratch : workflow
from→run→commit→push - Construction avec Dockerfile : commande
bud(build-using-dockerfile) - Intégration CI/CD : GitLab CI et GitHub Actions avec Buildah
- Bonnes pratiques : multi-stage builds, cache, attestations
Prérequis : Linux (Ubuntu/Fedora/RHEL), connaissances de base en Dockerfile et conteneurs.
Pourquoi Buildah plutôt que Docker build ?
Section intitulée « Pourquoi Buildah plutôt que Docker build ? »Le problème du démon Docker
Section intitulée « Le problème du démon Docker »docker build nécessite un démon Docker root en permanence :
# Docker build classiquedocker build -t myapp:1.0.0 .# → Envoie le contexte au démon Docker (root)# → Le démon construit l'image# → Privilèges root requisInconvénients :
- Démon root permanent : surface d’attaque élevée (CVE-2019-5736, CVE-2025-49713)
- Privilèges élevés : tout utilisateur du groupe
docker= root effectif - CI/CD complexe : montage du socket Docker (
/var/run/docker.sock) dans les conteneurs - Rootless incomplet :
docker buildrootless reste expérimental et limité
La solution Buildah
Section intitulée « La solution Buildah »Buildah élimine le démon et fonctionne directement avec les images OCI :
# Buildah : pas de démonbuildah from alpine:3.21 # Créer conteneur temporairebuildah run <ID> apk add nginx # Installer paquetsbuildah commit <ID> myapp:1.0.0 # Créer image OCI# → Aucun démon, exécutable en tant qu'utilisateurGains immédiats :
| Critère | Docker Build | Buildah |
|---|---|---|
| Démon requis | ✅ Oui (root) | ❌ Non |
| Rootless natif | ⚠️ Expérimental | ✅ Stable |
| Surface d’attaque | Élevée (démon + socket) | Faible (CLI directe) |
| Scriptable | ❌ Dockerfile uniquement | ✅ from/run/commit + Dockerfile |
| CI/CD | Docker-in-Docker (DinD) ou socket mount | Image Buildah standalone |
Cas d’usage recommandés
Section intitulée « Cas d’usage recommandés »Le choix entre Buildah et Docker build dépend principalement de vos contraintes de sécurité et de votre environnement d’exécution. Buildah excelle dans les environnements où la sécurité est prioritaire (CI/CD, Kubernetes) grâce à son architecture rootless, tandis que Docker build reste pertinent pour le développement local avec des scripts existants.
| Situation | Outil recommandé | Raison |
|---|---|---|
| CI/CD sécurisé (GitLab, GitHub Actions) | Buildah | Pas de socket Docker, rootless stable |
| Kubernetes (build in-cluster) | Buildah | Pas de démon, pods non privilégiés |
| Builds scriptés complexes | Buildah | from/run/commit = shell natif |
| Développement local avec Dockerfiles | Docker build ou Buildah bud | Les deux fonctionnent, Buildah = rootless |
| Compatibilité legacy (scripts existants) | Docker build | Moins de refactoring |
Installation et configuration
Section intitulée « Installation et configuration »-
Installer Buildah (Ubuntu/Debian)
Fenêtre de terminal sudo apt updatesudo apt install buildahAutres distributions :
Fenêtre de terminal sudo dnf install buildahFenêtre de terminal sudo pacman -S buildah -
Vérifier l’installation
Fenêtre de terminal buildah --versionSortie attendue (version 1.42.0 ou supérieure) :
buildah version 1.42.0 (image-spec 1.2.0, runtime-spec 1.2.0) -
Configurer le mode rootless (recommandé)
Buildah fonctionne en mode rootless grâce aux user namespaces. Vérifiez que votre utilisateur a les plages UID/GID configurées :
Fenêtre de terminal cat /etc/subuid# bob:100000:65536cat /etc/subgid# bob:100000:65536Si les fichiers sont vides, ajoutez votre utilisateur :
Fenêtre de terminal sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USERVérifier le mode rootless :
Fenêtre de terminal buildah info | grep rootless# rootless: true -
Configurer le stockage (optionnel)
Par défaut, Buildah stocke les images dans
~/.local/share/containers/storage. Pour changer :Fenêtre de terminal mkdir -p ~/.config/containerscat > ~/.config/containers/storage.conf <<EOF[storage]driver = "overlay"graphroot = "/home/$USER/buildah-storage"EOF
Construction from scratch (workflow natif)
Section intitulée « Construction from scratch (workflow natif) »Le mode from scratch est la signature de Buildah : construire des images avec des commandes shell simples, sans Dockerfile.
Étapes de construction
Section intitulée « Étapes de construction »-
Créer un conteneur de travail
Fenêtre de terminal buildah from alpine:3.21Sortie :
alpine-working-containerBuildah crée un conteneur temporaire (nom généré automatiquement). Récupérez son ID :
Fenêtre de terminal buildah containersSortie :
CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAMEea12f417f4f4 * 39477eca89b9 docker.io/library/alpine:3.21 alpine-working-container -
Modifier le conteneur avec
runFenêtre de terminal buildah run alpine-working-container apk add --no-cache nginxSortie :
fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gzfetch https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz(1/2) Installing pcre (8.45-r3)(2/2) Installing nginx (1.26.3-r0)Executing nginx-1.26.3-r0.pre-installExecuting nginx-1.26.3-r0.post-installExecuting busybox-1.37.0-r13.triggerOK: 8 MiB in 17 packagesCommandes disponibles : toutes les commandes shell standard (
apk,apt,yum,cp,mkdir, etc.) -
Configurer l’image (metadata)
Fenêtre de terminal # Définir le CMDbuildah config --cmd "nginx -g 'daemon off;'" alpine-working-container# Ajouter des labels OCIbuildah config \--label "org.opencontainers.image.source=https://github.com/me/app" \--label "org.opencontainers.image.version=1.0.0" \alpine-working-container# Exposer un port (documentation)buildah config --port 80 alpine-working-container -
Créer l’image finale
Fenêtre de terminal buildah commit alpine-working-container myapp-nginx:1.0.0Sortie :
Getting image source signaturesCopying blob sha256:922ec217407c0fd31cb18b46090bf62e439fb53ecd01f09406d62e25a906e09bCopying blob sha256:76544e3ee9871ab833c328242c056a68d37558048e2b842ff2e119ff6cedae08Copying config sha256:a85c85cb198dec139803c48618a0f96e0af53780736b5586dc4bde71efe35d38Writing manifest to image destinationa85c85cb198dec139803c48618a0f96e0af53780736b5586dc4bde71efe35d38 -
Vérifier l’image créée
Fenêtre de terminal buildah imagesSortie :
REPOSITORY TAG IMAGE ID CREATED SIZElocalhost/myapp-nginx 1.0.0 6d9817bc292f 2 minutes ago 9.63 MB -
Nettoyer le conteneur temporaire
Fenêtre de terminal buildah rm alpine-working-container
Script automatisé
Section intitulée « Script automatisé »Exemple de script build-nginx.sh pour automatiser le workflow :
#!/bin/bashset -e
VERSION="1.0.0"IMAGE_NAME="myapp-nginx"
# 1. Créer conteneurcontainer=$(buildah from alpine:3.21)
# 2. Installer paquetsbuildah run "$container" apk add --no-cache nginx
# 3. Configurerbuildah config --cmd "nginx -g 'daemon off;'" "$container"buildah config --port 80 "$container"buildah config --label "version=$VERSION" "$container"
# 4. Créer imagebuildah commit "$container" "$IMAGE_NAME:$VERSION"
# 5. Nettoyerbuildah rm "$container"
echo "✅ Image $IMAGE_NAME:$VERSION créée avec succès"Exécution :
chmod +x build-nginx.sh./build-nginx.shConstruction avec Dockerfile (commande bud)
Section intitulée « Construction avec Dockerfile (commande bud) »Buildah supporte les Dockerfiles via la commande bud (Build Using Dockerfile) — alias de buildah build.
Exemple Dockerfile
Section intitulée « Exemple Dockerfile »FROM alpine:3.21
# Installer nginxRUN apk add --no-cache nginx
# ConfigurerRUN mkdir -p /run/nginx
# MetadataLABEL org.opencontainers.image.source="https://github.com/me/app"LABEL org.opencontainers.image.version="1.0.0"
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Construction
Section intitulée « Construction »buildah bud -f Dockerfile -t myapp-nginx:1.0.0 .Sortie :
[1/2] STEP 1/6: FROM alpine:3.21Resolved "alpine" as an alias (/etc/containers/registries.conf.d/shortnames.conf)Trying to pull docker.io/library/alpine:3.21...Getting image source signaturesCopying blob 96526aa774ef doneCopying config 8ca4688f4f doneWriting manifest to image destination[1/2] STEP 2/6: RUN apk add --no-cache nginxfetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz(1/5) Installing nginx (1.24.0-r15)OK: 12 MiB in 20 packages[1/2] STEP 3/6: RUN mkdir -p /run/nginx[1/2] STEP 4/6: LABEL org.opencontainers.image.source="https://github.com/me/app"[1/2] STEP 5/6: LABEL org.opencontainers.image.version="1.0.0"[1/2] STEP 6/6: CMD ["nginx", "-g", "daemon off;"][1/2] COMMIT myapp-nginx:1.0.0Getting image source signaturesCopying blob cc2447e1835a skipped: already existsCopying blob 5f70bf18a086 doneCopying config 2e05f739c6 doneWriting manifest to image destinationStoring signatures--> 2e05f739c67Successfully tagged localhost/myapp-nginx:1.0.02e05f739c674c1050c1700cf9d889b157c79c23c3f2fb4e066e044922c57402bOptions utiles de bud
Section intitulée « Options utiles de bud »La commande bud accepte de nombreuses options pour personnaliser vos builds. Les plus utiles permettent de cibler une étape spécifique dans un multi-stage build (--target), de passer des arguments dynamiques (--build-arg), de forcer la reconstruction sans cache (--no-cache), ou de construire pour plusieurs architectures simultanément (--platform).
# Multi-stage buildbuildah bud --target production -t myapp:1.0.0 .
# Build argsbuildah bud --build-arg VERSION=1.0.0 -t myapp:1.0.0 .
# Désactiver le cachebuildah bud --no-cache -t myapp:1.0.0 .
# Multi-platform (nécessite QEMU)buildah bud --platform linux/amd64,linux/arm64 -t myapp:1.0.0 .
# Sortie directe vers registrybuildah bud --manifest myapp:1.0.0 \ --platform linux/amd64,linux/arm64 \ -t ghcr.io/me/myapp:1.0.0 .buildah manifest push --all myapp:1.0.0 docker://ghcr.io/me/myapp:1.0.0Gestion des images
Section intitulée « Gestion des images »Pousser vers un registry
Section intitulée « Pousser vers un registry »Buildah utilise un système de transports pour gérer la destination des images. Cette approche flexible permet de pousser vers des registries OCI distants, de charger dans un démon Docker local pour des tests, ou d’exporter en fichiers pour la distribution offline. Le transport le plus courant est docker:// pour les registries standards (GHCR, Docker Hub, Harbor).
| Transport | Syntaxe | Usage |
|---|---|---|
| docker:// | docker://ghcr.io/me/app:1.0.0 | Registry OCI (Docker Hub, GHCR, Harbor) |
| docker-daemon: | docker-daemon:myapp:1.0.0 | Charger dans le démon Docker local |
| oci-archive: | oci-archive:/tmp/myapp.tar | Fichier tar OCI |
| dir: | dir:/tmp/myapp-dir | Dossier avec layout OCI |
| containers-storage: | containers-storage:myapp:1.0.0 | Stockage Podman/Buildah local |
Exemples :
# Authentificationecho $GITHUB_TOKEN | buildah login ghcr.io -u $GITHUB_USER --password-stdin
# Pushbuildah push myapp-nginx:1.0.0 docker://ghcr.io/$GITHUB_USER/myapp-nginx:1.0.0# Authentificationecho $DOCKER_PASSWORD | buildah login docker.io -u $DOCKER_USER --password-stdin
# Pushbuildah push myapp-nginx:1.0.0 docker://$DOCKER_USER/myapp-nginx:1.0.0# Charger l'image dans Docker (pour tests)buildah push myapp-nginx:1.0.0 docker-daemon:myapp-nginx:1.0.0
# Vérifierdocker images | grep myapp-nginx# Exporter en archive OCIbuildah push myapp-nginx:1.0.0 oci-archive:/tmp/myapp-nginx-1.0.0.tar
# Importer ailleursbuildah pull oci-archive:/tmp/myapp-nginx-1.0.0.tarInspecter les images
Section intitulée « Inspecter les images »Buildah offre plusieurs commandes pour analyser vos images : lister toutes les images disponibles, inspecter les métadonnées OCI complètes (labels, environnement, entrypoint), ou examiner la structure des layers pour comprendre la composition de l’image.
# Lister toutes les imagesbuildah images
# Inspecter les métadonnéesbuildah inspect --type image myapp-nginx:1.0.0
# Voir les layersbuildah inspect --format '{{.Docker.RootFS.Layers}}' myapp-nginx:1.0.0Supprimer des images
Section intitulée « Supprimer des images »Le nettoyage régulier des images inutilisées libère de l’espace disque et facilite la gestion. Buildah propose trois niveaux de nettoyage : suppression ciblée d’une image spécifique, pruning des images non utilisées, ou nettoyage complet incluant conteneurs et cache.
# Supprimer une imagebuildah rmi myapp-nginx:1.0.0
# Supprimer toutes les images non utiliséesbuildah rmi --prune
# Nettoyer complètement (images + conteneurs + cache)buildah system prune -aIntégration CI/CD
Section intitulée « Intégration CI/CD »GitLab CI
Section intitulée « GitLab CI »Exemple .gitlab-ci.yml avec Buildah rootless :
variables: IMAGE_NAME: $CI_REGISTRY_IMAGE IMAGE_TAG: $CI_COMMIT_SHORT_SHA
stages: - build - push
build-image: stage: build image: quay.io/buildah/stable:v1.42.0 script: # Configurer rootless - buildah info | grep rootless
# Build - buildah bud -f Dockerfile -t $IMAGE_NAME:$IMAGE_TAG .
# Vérifier - buildah images artifacts: reports: # Optionnel : générer SBOM cyclonedx: buildah-sbom.json
push-image: stage: push image: quay.io/buildah/stable:v1.42.0 dependencies: - build-image script: # Authentification - echo $CI_REGISTRY_PASSWORD | buildah login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
# Push - buildah push $IMAGE_NAME:$IMAGE_TAG docker://$IMAGE_NAME:$IMAGE_TAG
# Tag latest (si main) - | if [ "$CI_COMMIT_BRANCH" == "main" ]; then buildah tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest buildah push $IMAGE_NAME:latest docker://$IMAGE_NAME:latest fi only: - main - tagsGitHub Actions
Section intitulée « GitHub Actions »Exemple .github/workflows/build-image.yml :
name: Build Image with Buildah
on: push: branches: [main] pull_request:
env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}
jobs: build: runs-on: ubuntu-24.04 permissions: contents: read packages: write attestations: write id-token: write
steps: - uses: actions/checkout@v4.2.2
- name: Install Buildah run: | sudo apt update sudo apt install -y buildah
- name: Verify rootless run: buildah info | grep "rootless: true"
- name: Build image run: | buildah bud \ -f Dockerfile \ -t $REGISTRY/$IMAGE_NAME:${{ github.sha }} \ -t $REGISTRY/$IMAGE_NAME:latest \ .
- name: Login to GitHub Container Registry if: github.event_name != 'pull_request' run: | echo ${{ secrets.GITHUB_TOKEN }} | buildah login $REGISTRY -u ${{ github.actor }} --password-stdin
- name: Push image if: github.event_name != 'pull_request' run: | buildah push $REGISTRY/$IMAGE_NAME:${{ github.sha }} docker://$REGISTRY/$IMAGE_NAME:${{ github.sha }} buildah push $REGISTRY/$IMAGE_NAME:latest docker://$REGISTRY/$IMAGE_NAME:latest
- name: Generate SBOM (optionnel) if: github.event_name != 'pull_request' run: | buildah inspect --format '{{.OCIv1.Config}}' $REGISTRY/$IMAGE_NAME:${{ github.sha }} > sbom.json
- name: Attest image (GitHub) if: github.event_name != 'pull_request' uses: actions/attest-build-provenance@v2.1.0 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} subject-digest: ${{ steps.push.outputs.digest }} push-to-registry: trueKubernetes (Tekton/Argo Workflows)
Section intitulée « Kubernetes (Tekton/Argo Workflows) »Exemple de Task Tekton :
apiVersion: tekton.dev/v1kind: Taskmetadata: name: buildah-buildspec: params: - name: IMAGE description: Image de sortie (ex: ghcr.io/me/app:1.0.0) - name: DOCKERFILE default: Dockerfile - name: CONTEXT default: . workspaces: - name: source steps: - name: build image: quay.io/buildah/stable:v1.42.0 workingDir: $(workspaces.source.path) script: | #!/bin/bash set -e buildah bud \ -f $(params.DOCKERFILE) \ -t $(params.IMAGE) \ $(params.CONTEXT) securityContext: runAsUser: 1000 # Rootless runAsGroup: 1000 volumeMounts: - name: varlibcontainers mountPath: /var/lib/containers
- name: push image: quay.io/buildah/stable:v1.42.0 script: | #!/bin/bash set -e buildah push $(params.IMAGE) docker://$(params.IMAGE) securityContext: runAsUser: 1000 runAsGroup: 1000
volumes: - name: varlibcontainers emptyDir: {}Dépannage
Section intitulée « Dépannage »Problèmes courants et solutions
Section intitulée « Problèmes courants et solutions »La plupart des erreurs Buildah proviennent d’une mauvaise configuration du mode rootless (user namespaces, plages UID/GID). Le tableau ci-dessous récapitule les symptômes les plus fréquents avec leur diagnostic et la solution rapide à appliquer.
| Symptôme | Cause probable | Solution |
|---|---|---|
permission denied lors du build | Rootless mal configuré | Vérifier /etc/subuid et /etc/subgid, relancer session utilisateur |
error creating build container | Pas assez d’UIDs mappés | Augmenter la plage dans /etc/subuid (65536 minimum) |
ERRO[0000] cannot find UID/GID | User namespaces désactivés | Vérifier sysctl user.max_user_namespaces (doit être > 0) |
storage driver not supported | Overlay2 non disponible | Vérifier buildah info | grep graphDriverName, installer fuse-overlayfs |
failed to resolve image | Registry non accessible | Vérifier /etc/containers/registries.conf, tester buildah pull alpine:3.21 |
| Multi-platform build échoue | QEMU binfmt non installé | Installer qemu-user-static : sudo apt install qemu-user-static |
Commandes de diagnostic
Section intitulée « Commandes de diagnostic »Avant de chercher une solution complexe, commencez toujours par vérifier la configuration de base : est-ce que le mode rootless est activé ? Les registries sont-ils accessibles ? Les user namespaces sont-ils correctement configurés ? Ces commandes de diagnostic vous donnent rapidement les informations essentielles.
# Informations système Buildahbuildah info
# Vérifier le mode rootlessbuildah info | grep rootless
# Lister les registries configuréscat /etc/containers/registries.conf
# Tester la résolution d'imagebuildah pull alpine:3.21
# Vérifier les user namespacescat /proc/sys/user/max_user_namespaces# Doit être > 0 (typiquement 65536)
# Logs détaillésbuildah --log-level debug bud -t test:1.0.0 .Activer user namespaces (si désactivés)
Section intitulée « Activer user namespaces (si désactivés) »Sur certaines distributions (notamment Ubuntu Server), les user namespaces sont désactivés par défaut pour des raisons de sécurité. Si sysctl user.max_user_namespaces retourne 0, vous devez les activer manuellement. Cette configuration est indispensable pour le mode rootless de Buildah.
# Vérifier l'état actuelsysctl user.max_user_namespaces
# Activer temporairementsudo sysctl user.max_user_namespaces=65536
# Activer au démarrageecho "user.max_user_namespaces=65536" | sudo tee -a /etc/sysctl.confsudo sysctl -pBonnes pratiques
Section intitulée « Bonnes pratiques »Sécurité
Section intitulée « Sécurité »La sécurité des images de conteneurs commence dès le build. Ces quatre pratiques fondamentales réduisent drastiquement les risques : mode rootless (aucun privilège root), utilisateur non privilégié dans l’image finale, scan des vulnérabilités avant publication, et signature cryptographique pour garantir l’intégrité.
-
Toujours utiliser rootless en production
Fenêtre de terminal # Vérifier avant de builderbuildah info | grep "rootless: true" -
Éviter les privilèges élevés dans les conteneurs
FROM alpine:3.21RUN adduser -D -u 1000 appuserUSER appuser -
Scanner les images avec Trivy
Fenêtre de terminal buildah bud -t myapp:1.0.0 .trivy image --severity HIGH,CRITICAL myapp:1.0.0 -
Signer les images avec Cosign
Fenêtre de terminal # Build + pushbuildah bud -t ghcr.io/me/app:1.0.0 .buildah push ghcr.io/me/app:1.0.0 docker://ghcr.io/me/app:1.0.0# Signercosign sign ghcr.io/me/app:1.0.0
Performance
Section intitulée « Performance »Optimiser les builds Buildah améliore à la fois le temps de construction et la taille des images finales. Les multi-stage builds séparent l’environnement de compilation de l’environnement d’exécution, le cache de layers évite de reconstruire les étapes inchangées, et un bon .dockerignore réduit la taille du contexte envoyé au builder.
-
Utiliser des multi-stage builds
# Build stageFROM golang:1.23-alpine AS builderWORKDIR /appCOPY . .RUN go build -o myapp# Runtime stageFROM alpine:3.21COPY --from=builder /app/myapp /usr/local/bin/CMD ["myapp"] -
Activer le cache de layers
Fenêtre de terminal # Cache local (défaut)buildah bud -t myapp:1.0.0 .# Cache registry (CI/CD)buildah bud \--cache-to=type=registry,ref=ghcr.io/me/cache:myapp \--cache-from=type=registry,ref=ghcr.io/me/cache:myapp \-t myapp:1.0.0 . -
Optimiser le contexte de build
Créer un
.dockerignore:.gitnode_modules*.mdtests/
Maintenabilité
Section intitulée « Maintenabilité »Des builds reproductibles et traçables facilitent le debugging et la collaboration. Versionner explicitement les images de base évite les surprises lors des rebuilds, les labels OCI structurent les métadonnées (source, version, commit SHA), et centraliser le processus de build dans un script garantit la cohérence entre développeurs et CI/CD.
-
Versionner les images de base
# ❌ Mauvais (latest implicite)FROM alpine# ✅ Bon (version explicite)FROM alpine:3.21 -
Ajouter des labels OCI
LABEL org.opencontainers.image.source="https://github.com/me/app"LABEL org.opencontainers.image.version="1.0.0"LABEL org.opencontainers.image.revision="${GIT_SHA}"LABEL org.opencontainers.image.created="${BUILD_DATE}" -
Centraliser les builds dans un script
build.sh #!/bin/bashset -eVERSION=${1:-dev}IMAGE="ghcr.io/me/app"echo "🔨 Building $IMAGE:$VERSION..."buildah bud -t "$IMAGE:$VERSION" .echo "🔍 Scanning image..."trivy image --severity HIGH,CRITICAL "$IMAGE:$VERSION"if [ "$VERSION" != "dev" ]; thenecho "📤 Pushing to registry..."buildah push "$IMAGE:$VERSION" docker://"$IMAGE:$VERSION"fiecho "✅ Done!"
FAQ : Questions fréquentes
Section intitulée « FAQ : Questions fréquentes »Vulnérabilités corrigées dans v1.42
| CVE | CVSS | Description | Impact |
|---|---|---|---|
| CVE-2025-47913 | 7.5 (High) | Panique du client SSH via golang.org/x/crypto/ssh/agent |
Déni de service |
| CVE-2025-47914 | 5.3 (Medium) | Vulnérabilité complémentaire container-tools | Sécurité réduite |
Détails CVE-2025-47913
Composant affecté :golang.org/x/crypto/ssh/agentVecteur d'attaque : Un serveur SSH malveillant peut envoyer une réponse SSH_AGENT_SUCCESS inattendue, provoquant une panique du client SSH et un crash de l'application.Impact : Déni de service (DoS) lors d'opérations SSH (ex: clonage de dépôts Git privés pendant un build).Versions affectées
- Buildah < 1.42.0
- Podman < 5.5.0
- Skopeo < 1.20.0
- Autres outils utilisant
golang.org/x/cryptoavant v0.38.0
Mise à jour urgente
Fedora/RHEL
sudo dnf update buildah podman skopeo
buildah --version
# buildah version 1.42.0
Ubuntu/Debian
sudo apt update
sudo apt upgrade buildah
buildah --version
Arch Linux
sudo pacman -Syu buildah
Vérifier votre version
# Version installée
buildah --version
# Si < 1.42.0, mettre à jour immédiatement
Références officielles
- RHSA-2026:0753 - Red Hat Security Advisory
- SUSE-SU-2026:0014-1 - SUSE Security Update
- ELSA-2026-0436 - Oracle Linux Security Advisory
- CVE-2025-47913 - NVD
Bonnes pratiques post-mise à jour
- Vérifier la version :
buildah --version→ 1.42.0 ou supérieur - Mettre à jour les runners CI/CD : images Docker des pipelines
- Reconstruire les images de base : si elles utilisaient une version vulnérable
- Auditer les builds récents : vérifier l'absence d'incidents
Définition
Buildah est un outil en ligne de commande créé par Red Hat en 2017 pour construire des images de conteneurs OCI (Open Container Initiative) sans démon Docker.Avantages clés
| Avantage | Description |
|---|---|
| Sans démon | Aucun processus Docker root requis, CLI directe |
| Rootless natif | Fonctionne avec utilisateur non privilégié (user namespaces) |
| Sécurité renforcée | Surface d'attaque réduite vs démon Docker root |
| Scriptable | Commandes shell (from, run, commit) ou Dockerfiles (bud) |
| Compatible OCI | Images utilisables avec Docker, Podman, Kubernetes |
Cas d'usage recommandés
- CI/CD sécurisés : GitLab CI, GitHub Actions, Tekton (pas de socket Docker à monter)
- Builds rootless : conformité sécurité stricte (PCI-DSS, ISO 27001)
- Automatisation complexe : scripts Bash/Python pour orchestrer builds conditionnels
- Kubernetes : build in-cluster sans privilèges élevés
Exemple rapide
# Build from scratch (scriptable)
buildah from alpine:3.21
buildah run <ID> apk add nginx
buildah commit <ID> myapp:1.0.0
# Build avec Dockerfile
buildah bud -t myapp:1.0.0 .
Comparaison architecture
| Critère | Docker Build | Buildah |
|---|---|---|
| Démon requis | ✅ Oui (root) | ❌ Non |
| Rootless | ⚠️ Expérimental | ✅ Stable natif |
| Surface d'attaque | Élevée (démon + socket) | Faible (CLI directe) |
| Workflows | Dockerfile uniquement | from/run/commit + Dockerfile (bud) |
| CI/CD | Docker-in-Docker ou socket mount | Image Buildah standalone |
| Images OCI | ✅ Oui | ✅ Oui |
Docker build
# Requiert démon Docker root actif
docker build -t app:1.0.0 .
# → Envoie contexte au démon
# → Démon construit l'image
# → Privilèges root nécessaires
Buildah
# Aucun démon requis
buildah bud -t app:1.0.0 .
# → CLI directe, rootless possible
# → Même résultat (image OCI)
Quand choisir Buildah ?
- CI/CD sécurisé : pas de socket Docker à exposer
- Rootless obligatoire : conformité sécurité stricte
- Builds scriptés : besoin de conditions/boucles (from/run/commit)
- Kubernetes : build in-cluster sans privilèges
Quand rester sur Docker build ?
- Compatibilité legacy : scripts existants complexes
- Développement local : habitudes d'équipe établies
- Pas de contrainte rootless : environnement déjà sécurisé
Installation
Ubuntu/Debian
sudo apt update
sudo apt install buildah
Fedora/RHEL
sudo dnf install buildah
Arch Linux
sudo pacman -S buildah
Vérifier l'installation
buildah --version
# buildah version 1.42.0 (image-spec 1.2.0)
Configuration rootless
1. Vérifier les plages UID/GID
cat /etc/subuid
# bob:100000:65536
cat /etc/subgid
# bob:100000:65536
2. Ajouter l'utilisateur si absent
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
# Relancer session (logout/login ou newgrp)
newgrp -
3. Vérifier le mode rootless
buildah info | grep rootless
# rootless: true
4. Activer user namespaces (si désactivé)
# Vérifier
sysctl user.max_user_namespaces
# Doit être > 0 (typiquement 65536)
# Activer temporairement
sudo sysctl user.max_user_namespaces=65536
# Activer au démarrage
echo "user.max_user_namespaces=65536" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Configurer le stockage (optionnel)
mkdir -p ~/.config/containers
cat > ~/.config/containers/storage.conf <<EOF
[storage]
driver = "overlay"
graphroot = "/home/$USER/buildah-storage"
EOF
Test rapide
# Build simple
buildah from alpine:3.21
buildah images
Workflow from/run/commit (scriptable)
Principe : construire des images avec des commandes shell directes, sans Dockerfile.# 1. Créer conteneur temporaire
container=$(buildah from alpine:3.21)
# 2. Modifier le conteneur
buildah run $container apk add --no-cache nginx
buildah run $container mkdir -p /run/nginx
# 3. Configurer metadata
buildah config --cmd "nginx -g 'daemon off;'" $container
buildah config --port 80 $container
# 4. Créer l'image
buildah commit $container myapp:1.0.0
# 5. Nettoyer
buildah rm $container
Avantages :- Scriptable : boucles, conditions, variables shell natives
- Flexible : appels API, parsing JSON, logique complexe
- Langages multiples : Bash, Python, Ansible
Workflow bud (Dockerfile)
Principe : lire un Dockerfile classique (compatibilité Docker).# Dockerfile
cat > Dockerfile <<EOF
FROM alpine:3.21
RUN apk add --no-cache nginx
RUN mkdir -p /run/nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
EOF
# Build
buildah bud -t myapp:1.0.0 .
Avantages :- Compatibilité : drop-in replacement de
docker build - Familiarité : syntaxe Dockerfile standard
- Cache : réutilise layers comme Docker
Comparaison
| Critère | from/run/commit | bud (Dockerfile) |
|---|---|---|
| Syntaxe | Commandes shell | Instructions Dockerfile |
| Conditions | ✅ Natives (if/then) | ❌ Limitées (ARG) |
| Boucles | ✅ for/while | ❌ Non supportées |
| Cache layers | ⚠️ Manuel | ✅ Automatique |
| Compatibilité | Buildah uniquement | Docker + Buildah |
| Complexité | Scripts avancés | Builds standards |
Quand utiliser from/run/commit ?
- Build conditionnel (ex: archi CPU détectée dynamiquement)
- Appels API pendant build (récupérer config externe)
- Logique complexe (parsing, calculs, transformations)
Quand utiliser bud ?
- Dockerfiles existants à migrer
- Build standards (FROM, RUN, COPY)
- Équipe habituée à Dockerfile
Transports disponibles
| Transport | Syntaxe | Usage |
|---|---|---|
| docker:// | docker://ghcr.io/me/app:1.0.0 |
Registry OCI (GHCR, Docker Hub, Harbor) |
| docker-daemon: | docker-daemon:app:1.0.0 |
Charger dans démon Docker local |
| oci-archive: | oci-archive:/tmp/app.tar |
Fichier tar OCI |
| dir: | dir:/tmp/app-oci |
Dossier layout OCI |
| containers-storage: | containers-storage:app:1.0.0 |
Stockage Podman/Buildah local |
Push vers GitHub Container Registry
# 1. Authentification
echo $GITHUB_TOKEN | buildah login ghcr.io -u $GITHUB_USER --password-stdin
# 2. Build
buildah bud -t myapp:1.0.0 .
# 3. Push
buildah push myapp:1.0.0 docker://ghcr.io/$GITHUB_USER/myapp:1.0.0
# 4. Tag latest (optionnel)
buildah tag myapp:1.0.0 myapp:latest
buildah push myapp:latest docker://ghcr.io/$GITHUB_USER/myapp:latest
Push vers Docker Hub
# Authentification
echo $DOCKER_PASSWORD | buildah login docker.io -u $DOCKER_USER --password-stdin
# Push
buildah push myapp:1.0.0 docker://$DOCKER_USER/myapp:1.0.0
Charger dans démon Docker local
# Pour tests avec docker run
buildah push myapp:1.0.0 docker-daemon:myapp:1.0.0
# Vérifier
docker images | grep myapp
Export en archive OCI
# Export
buildah push myapp:1.0.0 oci-archive:/tmp/myapp-1.0.0.tar
# Import ailleurs
buildah pull oci-archive:/tmp/myapp-1.0.0.tar
Multi-platform push
# Build multi-arch
buildah bud --platform linux/amd64,linux/arm64 --manifest myapp:1.0.0 .
# Push manifest list
buildah manifest push --all myapp:1.0.0 docker://ghcr.io/me/myapp:1.0.0
GitLab CI (.gitlab-ci.yml)
variables:
IMAGE_NAME: $CI_REGISTRY_IMAGE
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
stages:
- build
- push
build-image:
stage: build
image: quay.io/buildah/stable:v1.42.0
script:
# Vérifier rootless
- buildah info | grep "rootless: true"
# Build
- buildah bud -f Dockerfile -t $IMAGE_NAME:$IMAGE_TAG .
# Vérifier image
- buildah images
push-image:
stage: push
image: quay.io/buildah/stable:v1.42.0
dependencies:
- build-image
script:
# Login
- echo $CI_REGISTRY_PASSWORD | buildah login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
# Push
- buildah push $IMAGE_NAME:$IMAGE_TAG docker://$IMAGE_NAME:$IMAGE_TAG
# Tag latest (si main)
- |
if [ "$CI_COMMIT_BRANCH" == "main" ]; then
buildah tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest
buildah push $IMAGE_NAME:latest docker://$IMAGE_NAME:latest
fi
only:
- main
- tags
GitHub Actions (.github/workflows/build.yml)
name: Build Image
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4.2.2
- name: Install Buildah
run: |
sudo apt update
sudo apt install -y buildah
- name: Verify rootless
run: buildah info | grep "rootless: true"
- name: Build image
run: |
buildah bud \
-t $REGISTRY/$IMAGE_NAME:${{ github.sha }} \
-t $REGISTRY/$IMAGE_NAME:latest \
.
- name: Login to GHCR
run: |
echo ${{ secrets.GITHUB_TOKEN }} | buildah login $REGISTRY -u ${{ github.actor }} --password-stdin
- name: Push image
run: |
buildah push $REGISTRY/$IMAGE_NAME:${{ github.sha }} docker://$REGISTRY/$IMAGE_NAME:${{ github.sha }}
buildah push $REGISTRY/$IMAGE_NAME:latest docker://$REGISTRY/$IMAGE_NAME:latest
Bonnes pratiques CI/CD
- Toujours vérifier rootless :
buildah info | grep rootless - Scanner les images : intégrer Trivy (
trivy image app:1.0.0) - Versionner avec git SHA : traçabilité complète
- Cache registry : accélérer builds avec
--cache-from/--cache-to - Multi-platform : builder amd64+arm64 en un seul job
Sécurité fondamentale
1. Mode rootless obligatoire
# Vérifier avant build
buildah info | grep "rootless: true"
# Si false, configurer /etc/subuid et /etc/subgid
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
2. Utilisateur non-root dans les images
FROM alpine:3.21
# Créer utilisateur dédié
RUN adduser -D -u 1000 appuser
# Installer paquets
RUN apk add --no-cache nginx
# Basculer vers utilisateur
USER appuser
CMD ["nginx"]
3. Scanner les vulnérabilités
# Build
buildah bud -t myapp:1.0.0 .
# Scanner avec Trivy
trivy image --severity HIGH,CRITICAL myapp:1.0.0
# Bloquer si CVE critique
trivy image --exit-code 1 --severity CRITICAL myapp:1.0.0
Build sécurisé
4. Multi-stage builds
# Build stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# Runtime stage (minimal)
FROM alpine:3.21
RUN adduser -D -u 1000 appuser
COPY --from=builder /app/myapp /usr/local/bin/
USER appuser
CMD ["myapp"]
5. Versionner les images de base
# ❌ Mauvais (latest implicite)
FROM alpine
# ✅ Bon (version explicite)
FROM alpine:3.21
6. Labels OCI pour traçabilité
LABEL org.opencontainers.image.source="https://github.com/me/app"
LABEL org.opencontainers.image.version="1.0.0"
LABEL org.opencontainers.image.revision="${GIT_SHA}"
LABEL org.opencontainers.image.created="${BUILD_DATE}"
Signature et provenance
7. Signer les images avec Cosign
# Build + push
buildah bud -t ghcr.io/me/app:1.0.0 .
buildah push ghcr.io/me/app:1.0.0 docker://ghcr.io/me/app:1.0.0
# Signer
cosign sign ghcr.io/me/app:1.0.0
# Vérifier
cosign verify --key cosign.pub ghcr.io/me/app:1.0.0
Checklist sécurité
- Rootless activé (
buildah info | grep rootless: true) - User namespaces configurés (
/etc/subuid,/etc/subgid) - USER non-root dans Dockerfile
- Images de base versionnées (pas
:latest) - Multi-stage builds pour réduire surface d'attaque
- Scan Trivy avant push (bloquer si CRITICAL)
- Signature Cosign en production
- Labels OCI pour traçabilité
Diagnostic rapide
# Vérifier le mode rootless
buildah info | grep rootless
# Doit afficher : rootless: true
Causes fréquentes
1. User namespaces désactivés
# Vérifier
sysctl user.max_user_namespaces
# Doit être > 0 (typiquement 65536)
# Si 0, activer temporairement
sudo sysctl user.max_user_namespaces=65536
# Activer au démarrage
echo "user.max_user_namespaces=65536" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
2. Plages UID/GID manquantes
# Vérifier /etc/subuid et /etc/subgid
cat /etc/subuid
cat /etc/subgid
# Doit contenir : bob:100000:65536
# Si vide, ajouter utilisateur
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
# IMPORTANT : relancer session
newgrp -
# Ou logout/login complet
3. Plages trop petites
# Vérifier la taille de plage (doit être >= 65536)
grep $USER /etc/subuid
# bob:100000:65536 → OK
# bob:100000:1000 → KO (trop petit)
# Corriger si nécessaire
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
4. Storage driver non supporté
# Vérifier le driver
buildah info | grep graphDriverName
# Recommandé : overlay ou fuse-overlayfs
# Si problème, installer fuse-overlayfs
sudo apt install fuse-overlayfs # Ubuntu/Debian
sudo dnf install fuse-overlayfs # Fedora/RHEL
Erreurs spécifiques
error creating build container
# Cause : UIDs insuffisants
grep $USER /etc/subuid
# Solution : augmenter plage à 65536
sudo usermod --add-subuids 100000-165535 $USER
ERRO[0000] cannot find UID/GID
# Cause : user namespaces mal configurés
# Solution : vérifier et relancer session
sudo sysctl user.max_user_namespaces=65536
newgrp -
permission denied: /var/lib/containers
# Cause : tentative d'utiliser storage root
# Solution : utiliser storage utilisateur
mkdir -p ~/.local/share/containers/storage
buildah info | grep graphRoot
# Doit pointer vers /home/$USER/.local/share/containers/storage
Test complet
# 1. Vérifier user namespaces
sysctl user.max_user_namespaces
# 2. Vérifier plages UID/GID
grep $USER /etc/subuid /etc/subgid
# 3. Vérifier rootless
buildah info | grep rootless
# 4. Test simple
buildah from alpine:3.21
buildah images
# Si tout OK, nettoyer
buildah rm --all
Checklist dépannage
-
user.max_user_namespaces >= 65536 -
/etc/subuidcontient$USER:100000:65536 -
/etc/subgidcontient$USER:100000:65536 - Session relancée (logout/login ou newgrp)
-
buildah info | grep rootlessaffichetrue - Storage driver =
overlayoufuse-overlayfs
Prérequis
Installer QEMU binfmt
# Ubuntu/Debian
sudo apt update
sudo apt install qemu-user-static
# Vérifier les architectures supportées
ls /proc/sys/fs/binfmt_misc/
# Doit contenir : qemu-aarch64, qemu-arm, etc.
Build multi-plateforme avec Dockerfile
Méthode 1 : Build direct (une seule plateforme)
# Build amd64 (natif sur machine amd64)
buildah bud --platform linux/amd64 -t myapp:1.0.0-amd64 .
# Build arm64 (émulé via QEMU)
buildah bud --platform linux/arm64 -t myapp:1.0.0-arm64 .
Méthode 2 : Manifest list (multi-arch)
# 1. Créer manifest list
buildah manifest create myapp:1.0.0
# 2. Build et ajouter amd64
buildah bud --platform linux/amd64 --manifest myapp:1.0.0 .
# 3. Build et ajouter arm64
buildah bud --platform linux/arm64 --manifest myapp:1.0.0 .
# 4. Inspecter le manifest
buildah manifest inspect myapp:1.0.0
# 5. Push toutes les architectures
buildah manifest push --all myapp:1.0.0 docker://ghcr.io/me/myapp:1.0.0
Build multi-plateforme en une commande
# Build amd64 + arm64 simultanément
buildah bud \
--platform linux/amd64,linux/arm64 \
--manifest myapp:1.0.0 \
.
# Push
buildah manifest push --all myapp:1.0.0 docker://ghcr.io/me/myapp:1.0.0
Workflow from/run/commit multi-arch
#!/bin/bash
set -e
IMAGE="myapp:1.0.0"
PLATFORMS=("linux/amd64" "linux/arm64")
# Créer manifest
buildah manifest create $IMAGE
for platform in "${PLATFORMS[@]}"; do
echo "Building for $platform..."
# Créer conteneur pour cette plateforme
container=$(buildah from --platform $platform alpine:3.21)
# Modifier
buildah run $container apk add --no-cache nginx
buildah config --cmd "nginx -g 'daemon off;'" $container
# Commit dans le manifest
buildah commit $container $IMAGE
buildah rm $container
done
# Push
buildah manifest push --all $IMAGE docker://ghcr.io/me/$IMAGE
Vérifier le manifest
# Inspecter localement
buildah manifest inspect myapp:1.0.0
# Vérifier dans le registry
regctl manifest get ghcr.io/me/myapp:1.0.0
CI/CD multi-plateforme
# GitLab CI
build-multiarch:
image: quay.io/buildah/stable:v1.42.0
before_script:
- apt update && apt install -y qemu-user-static
script:
- buildah bud --platform linux/amd64,linux/arm64 --manifest $IMAGE .
- buildah manifest push --all $IMAGE docker://$IMAGE
Performance
Émulation QEMU : builds arm64 sur amd64 sont 10-30x plus lents (émulation CPU). Pour production, préférer :- Builds natifs : runners amd64 + runners arm64 séparés
- GitHub Actions : runners
ubuntu-24.04(amd64) +ubuntu-24.04-arm(arm64) - GitLab CI : tags
amd64etarm64sur runners dédiés
Multi-stage builds
# Build stage (outils de compilation)
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp
# Runtime stage (minimal)
FROM alpine:3.21
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/myapp /usr/local/bin/
CMD ["myapp"]
Gain : image finale = 10-20 MB (vs 500+ MB avec golang:1.23)Images de base slim
# ❌ Taille : 77 MB
FROM ubuntu:22.04
# ✅ Taille : 7 MB
FROM alpine:3.21
# ✅ Taille : 27 MB (Debian minimal)
FROM debian:12-slim
# ✅ Taille : 2 MB (vide)
FROM scratch
COPY myapp /
CMD ["/myapp"]
Optimiser les layers
# ❌ Mauvais (3 layers)
RUN apk add nginx
RUN apk add curl
RUN apk add jq
# ✅ Bon (1 layer)
RUN apk add --no-cache nginx curl jq
Nettoyer les caches
# Python
RUN pip install --no-cache-dir -r requirements.txt
# APK (Alpine)
RUN apk add --no-cache nginx
# APT (Debian/Ubuntu)
RUN apt update && apt install -y nginx \
&& rm -rf /var/lib/apt/lists/*
# YUM (RHEL/CentOS)
RUN yum install -y nginx && yum clean all
Squash layers (optionnel)
# Fusionner tous les layers en un seul
buildah bud --squash -t myapp:1.0.0 .
Attention : perte du cache layers (rebuilds plus lents).Réordonner le Dockerfile
# ✅ Bon ordre (cache optimal)
FROM alpine:3.21
# 1. Instructions statiques (changent rarement)
RUN apk add --no-cache nginx
# 2. Dépendances (changent moyennement)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 3. Code applicatif (change souvent)
COPY . .
# ❌ Mauvais ordre (invalide cache à chaque modif code)
FROM alpine:3.21
COPY . . # Invalide tout le cache
RUN apk add --no-cache nginx
Exclure fichiers inutiles
# .dockerignore
.git
node_modules
*.md
tests/
.env
Comparer les tailles
# Avant optimisation
buildah bud -t myapp:before .
buildah images myapp:before
# myapp:before 450 MB
# Après multi-stage
buildah bud -f Dockerfile.optimized -t myapp:after .
buildah images myapp:after
# myapp:after 15 MB
# Gain : 96% de réduction
Checklist optimisation
- Multi-stage builds (séparer build et runtime)
- Image de base slim/alpine (pas ubuntu/centos)
- --no-cache pour gestionnaires de paquets
- Combiner RUN en une seule commande
- Nettoyer caches (/var/lib/apt/lists, /var/cache)
- .dockerignore pour exclure fichiers inutiles
- Instructions statiques en premier (cache)
- Vérifier taille finale (
buildah images)
À retenir
Section intitulée « À retenir »- Buildah = builds sans démon : aucun processus Docker root requis, CLI directe
- Rootless natif : mode par défaut, sécurité renforcée vs docker build
- Mettez à jour vers v1.42+ : correctifs CVE-2025-47913 et CVE-2025-47914
- Deux workflows :
from/run/commit(scriptable) oubud(Dockerfile) - Transports multiples :
docker://,docker-daemon:,oci-archive:, etc. - CI/CD idéal : GitLab CI, GitHub Actions, Tekton — aucun socket Docker à monter
- Compatible OCI : images utilisables avec Docker, Podman, Kubernetes
- Bonnes pratiques : rootless + multi-stage + scan Trivy + labels OCI
- Dépannage : vérifier
/etc/subuid,user.max_user_namespaces,buildah info