Passer au contenu principal

Buncker : synchronisation delta de layers Docker pour l'air-gap

Romain Grosos

Les environnements air-gapped qui adoptent la culture DevOps se heurtent rapidement à une contrainte fondamentale : comment construire des images Docker sans accès aux registries externes ? La question semble simple. En pratique, elle ne l'est pas.

Un secteur comme la défense, l'industrie critique ou le datacenter totalement isolé a besoin d'outillage. Ces équipes montent en charge sur Docker, les pipelines CI/CD, les pratiques GitOps... mais sur un réseau qui ne touche pas l'internet. Le premier verrou : les images de base.

La réalité des environnements isolés

Un environnement 100% air-gapped ne fait pas de compromis. Pas de proxy sortant, pas d'outillage allégé avec une allowlist DNS, pas de miroir synchronisé automatiquement. Les postes et serveurs opèrent sur un LAN fermé, avec comme seul canal physique vers l'extérieur : la clé USB.

Monter en puissance sur Docker implique de disposer localement de toutes les images nécessaires aux builds : images de base (debian:12, python:3.11-slim, node:20-alpine), images intermédiaires dans les builds multi-stage, images spécifiques aux outils (linters, scanners, runtimes). À chaque docker build, il faut que tout soit présent. Sinon, le build échoue.

Hauler : la référence et ses limites

L'outil le plus connu dans cet espace est Hauler (SUSE/Rancher). L'approche : déclarer une liste d'images, exporter un snapshot complet, le transporter hors ligne, l'importer.

Ça fonctionne. Mais le modèle bulk a des limites dans un contexte d'usage intensif :

  • Aucune résolution de Dockerfile : il faut identifier manuellement quelles images sont nécessaires pour chaque build
  • Pas de delta : chaque synchronisation exporte les images en entier, même si 90 % des layers sont déjà présents dans le store local
  • Pas de chiffrement du canal de transport : la clé USB voyage sans protection cryptographique
  • Pas de piste d'audit : aucun log structuré des opérations de synchronisation

Pour un usage ponctuel, ces limites sont gérables. Pour une équipe qui build des dizaines d'images avec des cycles de mise à jour réguliers, le modèle devient pesant.

Buncker : synchronisation chirurgicale

Buncker aborde le problème différemment. Plutôt que d'exporter des snapshots complets, l'outil analyse statiquement les Dockerfiles, identifie les layers manquants dans le store local, et ne transfère que le delta. L'architecture est volontairement minimale : deux composants indépendants, zéro communication réseau entre eux.

Résolution statique de Dockerfile

Le composant offline (buncker daemon) intègre un résolveur de Dockerfile : il parse les instructions FROM, résout les ARG de pré-build (valeurs par défaut et overrides via --build-arg), gère le multi-stage et le multi-arch, et normalise les références Docker Hub (nginx devient docker.io/library/nginx).

En sortie : la liste exacte des blobs manquants dans le store, avec leur taille et leur digest SHA256. Avant de générer quoi que ce soit, l'opérateur sait précisément ce qui va être transféré.

Transfer chiffré via USB

La demande de synchronisation est chiffrée en AES-256-GCM et signée HMAC-SHA256 avant d'être écrite sur la clé USB (request.json.enc). Le secret partagé est dérivé d'une phrase mnémonique BIP-39 (12 mots), communiquée une seule fois via un canal humain. Aucune PKI à gérer, aucun certificat à renouveler.

Du côté connecté, buncker-fetch déchiffre la demande, vérifie l'intégrité HMAC, télécharge uniquement les blobs listés depuis les registries publics (Docker Hub, ghcr.io, quay.io...), vérifie chaque digest SHA256, et produit une réponse chiffrée (response.tar.enc). La réponse revient sur clé USB, puis est importée et vérifiée blob par blob côté offline.

Aucun blob non demandé, aucun blob corrompu : le store reste propre ou l'import échoue avec un message explicite.

Registry OCI permanent sur le LAN isolé

Une fois importés, les blobs sont servis via un daemon HTTP permanent (systemd) qui implémente le sous-ensemble pull de l'OCI Distribution API. Les clients Docker du LAN n'ont besoin que d'une ligne dans hosts.toml : aucun changement de configuration côté build.

Un docker build sur un poste du LAN isolé se comporte exactement comme s'il tirait les images depuis docker.io. Le registre local répond aux requêtes manifest et blob avec les headers OCI requis.

Piste d'audit complète

Toutes les opérations sont journalisées en JSON Lines (append-only) : analyse de Dockerfile, génération de manifest, import, pull de blob, garbage collection, rotation de clés. Les logs ne contiennent jamais de secrets (mnémonique, clés dérivées, tokens d'authentification).

Le GC est manuel uniquement : rapport de candidats inactifs, confirmation de l'opérateur, suppression. Aucune suppression automatique.

Philosophie technique

Le choix technique reflète les contraintes du terrain. Python >=3.11 (natif sur Debian 12/Ubuntu 22.04), zéro pip en production, zéro virtualenv. La seule dépendance externe est python3-cryptography, installée via apt. Les deux composants sont packagés en .deb, avec service systemd et hardening (NoNewPrivileges, ProtectSystem=strict, PrivateTmp).

Toutes les écritures dans le store passent par une séquence atomique (fichier temporaire + vérification SHA256 + rename). Un crash ne corrompt pas le store.

Buncker est open source sur GitHub sous licence Apache 2.0.

Sources

GitHub - Rwx-G/Buncker: Offline OCI registry for air-gapped environments - encrypted USB transfers with delta sync
Offline OCI registry for air-gapped environments - encrypted USB transfers with delta sync - Rwx-G/Buncker
GitHub - hauler-dev/hauler: Airgap Swiss Army Knife
Airgap Swiss Army Knife. Contribute to hauler-dev/hauler development by creating an account on GitHub.
GitHub - opencontainers/distribution-spec: OCI Distribution Specification
OCI Distribution Specification. Contribute to opencontainers/distribution-spec development by creating an account on GitHub.
bips/bip-0039.mediawiki at master · bitcoin/bips
Bitcoin Improvement Proposals. Contribute to bitcoin/bips development by creating an account on GitHub.
Buncker/docs/architecture.md at main · Rwx-G/Buncker
Offline OCI registry for air-gapped environments - encrypted USB transfers with delta sync - Rwx-G/Buncker