Pourquoi un registre Docker privé avec Harbor
Docker Hub et GHCR sont des services publics pratiques, mais ils posent trois problèmes concrets dès que votre organisation grandit ou doit répondre à une exigence NIS2 (art. 21 § 2 f – sécurité de la chaîne d'approvisionnement) ou GDPR (données d'infrastructure hébergées hors UE). Harbor répond point par point.
Bénéfices clés de Harbor
- Scan Trivy intégré — chaque push déclenche une analyse CVE ; les politiques d'admission bloquent le déploiement d'une image dont la criticité dépasse le seuil configuré.
- Signature Cosign / Notary — la chaîne de confiance va du build CI jusqu'au cluster Kubernetes ; seules les images signées par votre clé passent.
- RBAC multi-projet — isolation par équipe, par environnement (dev/staging/prod) ou par client, avec des rôles Guest, Developer, Maintainer et ProjectAdmin.
- Réplication inter-registres — synchronisez vers un second VPS, vers AWS ECR ou vers Docker Hub pour la distribution CDN.
- Audit logs immuables — chaque pull, push, suppression et connexion est tracé avec l'IP source ; conformité NIS2 article 21 § 2 d.
- Pas de rate limit — vos runners CI tirent autant d'images que nécessaire sans délai ni quota.
- OIDC natif — connectez Harbor à ZITADEL ou tout fournisseur OpenID Connect ; zéro compte local à gérer.
Prérequis chiffrés
Harbor tourne confortablement sur un VPS à partir de la configuration suivante :
- CPU : 4 vCPU (minimum 2 vCPU, mais Trivy consomme 1–2 cœurs au scan)
- RAM : 8 Go (4 Go minimum ; prévoir 2 Go pour la base PostgreSQL, 1 Go pour Redis, 2 Go pour le core Harbor, 1 Go de marge)
- Disque : 100 Go SSD (les layers Docker s'accumulent vite ; prévoir 40 Go pour le registre, 20 Go pour les logs Trivy, 20 Go pour le système)
- OS : Debian 12 ou Ubuntu 22.04 LTS
- Docker : ≥ 24.0, Docker Compose : ≥ 2.20
- DNS : un sous-domaine pointant vers votre VPS (registry.exemple.com), déjà résolu
- TLS : certificat valide — Let's Encrypt via Certbot ou Traefik ACME
Installation Harbor v2.15 avec Docker Compose
Télécharger le programme d'installation offline
Connectez-vous en SSH sur votre VPS. Récupérez le package offline Harbor v2.15.4 (le plus stable au 2026-10-13) depuis GitHub :
export HARBOR_VERSION=v2.15.4 wget https://github.com/goharbor/harbor/releases/download/${HARBOR_VERSION}/harbor-offline-installer-${HARBOR_VERSION}.tgz wget https://github.com/goharbor/harbor/releases/download/${HARBOR_VERSION}/harbor-offline-installer-${HARBOR_VERSION}.tgz.asc # Vérifier la signature GPG gpg --keyserver keyserver.ubuntu.com --recv-keys 7722D168DAEC457894C2988818B80B4B2D318C8A gpg --verify harbor-offline-installer-${HARBOR_VERSION}.tgz.asc tar xvf harbor-offline-installer-${HARBOR_VERSION}.tgz cd harborPréparer les certificats TLS
Harbor exige TLS. Si vous utilisez Certbot :
apt install -y certbot certbot certonly --standalone -d registry.exemple.com # Les certificats sont dans /etc/letsencrypt/live/registry.exemple.com/ mkdir -p /data/cert cp /etc/letsencrypt/live/registry.exemple.com/fullchain.pem /data/cert/registry.exemple.com.crt cp /etc/letsencrypt/live/registry.exemple.com/privkey.pem /data/cert/registry.exemple.com.keySi vous utilisez Traefik comme reverse proxy (voir l'article sur Traefik v3), déléguez la terminaison TLS à Traefik et configurez Harbor en HTTP interne sur le port 8080.
Configurer harbor.yml
Copiez le template et adaptez les valeurs essentielles :
cp harbor.yml.tmpl harbor.ymlÉditez
harbor.yml:hostname: registry.exemple.com https: port: 443 certificate: /data/cert/registry.exemple.com.crt private_key: /data/cert/registry.exemple.com.key harbor_admin_password: ChangezCeMotDePasse! database: password: ChangezAussiCeci! data_volume: /data trivy: ignore_unfixed: false skip_update: false offline_scan: false security_check: vuln insecure: falseLancer l'installateur
Harbor propose plusieurs composants optionnels. Activez Trivy (scanner) et Notary (signatures) :
./install.sh --with-trivy --with-notaryL'installateur génère un
docker-compose.ymlet démarre tous les conteneurs. Vérifiez qu'ils sont toushealthy:docker compose ps # STATUS attendu pour chaque service : healthyPremière connexion et changement du mot de passe
Ouvrez
https://registry.exemple.comdans votre navigateur. Connectez-vous avecadmin/ le mot de passe deharbor.yml. Rendez-vous dans Administration → Users → admin et changez immédiatement le mot de passe.Depuis votre poste de travail ou votre runner CI :
docker login registry.exemple.com # Entrez votre identifiant admin ou un compte de serviceCréer un projet et pousser une première image
Dans l'interface Harbor, créez un projet
mon-projet(privé). Puis depuis votre machine de build :docker pull nginx:alpine docker tag nginx:alpine registry.exemple.com/mon-projet/nginx:1.27-alpine docker push registry.exemple.com/mon-projet/nginx:1.27-alpine # Dans Harbor → mon-projet → Repositories : l'image apparaît avec son statut TrivyActiver le renouvellement TLS automatique
Créez un hook post-renouvellement Certbot pour recharger Harbor :
cat > /etc/letsencrypt/renewal-hooks/post/harbor-reload.sh << 'EOF' #!/bin/bash cp /etc/letsencrypt/live/registry.exemple.com/fullchain.pem /data/cert/registry.exemple.com.crt cp /etc/letsencrypt/live/registry.exemple.com/privkey.pem /data/cert/registry.exemple.com.key cd /opt/harbor && docker compose restart proxy EOF chmod +x /etc/letsencrypt/renewal-hooks/post/harbor-reload.sh
Configuration : projets, utilisateurs et OIDC (ZITADEL)
Projets et RBAC
Harbor organise les images par projets. Chaque projet peut être public ou privé, avec une politique de rétention indépendante. Créez un projet par équipe ou par environnement : frontend, backend, infra. Assignez des membres avec des rôles granulaires :
- Guest : pull uniquement
- Developer : push + pull
- Maintainer : gestion des tags, des webhooks et des politiques de rétention
- ProjectAdmin : gestion des membres
Intégration OIDC avec ZITADEL
Si vous avez déjà déployé ZITADEL (voir l'article dédié), connectez Harbor en quelques clics. Dans ZITADEL, créez une application harbor de type *Web*, notez le Client ID et le Client Secret. Dans Harbor → Administration → Configuration → Authentication :
- Auth Mode : OIDC
- OIDC Provider Name : ZITADEL
- OIDC Endpoint : https://sso.exemple.com
- OIDC Client ID / Secret : ceux de ZITADEL
- OIDC Scope : openid profile email groups
- Cochez Verify Certificate
Au premier login OIDC, Harbor crée automatiquement le compte utilisateur. Désactivez ensuite la création de comptes locaux (Administration → Configuration → Allow Self-Registration → Off).
Scanner les images avec Trivy
Harbor embarque Trivy directement dans le pipeline push. Dès qu'une image est poussée, Harbor déclenche un scan asynchrone qui interroge la base CVE de Trivy (mise à jour quotidienne). Le résultat apparaît dans l'interface : nombre de vulnérabilités par criticité (CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN).
Activer le scan automatique au push : dans Harbor → Administration → Configuration → Vulnerability → cochez *Automatically scan images on push*.
Consulter un rapport : ouvrez un dépôt, cliquez sur un tag → onglet *Vulnerabilities*. Chaque CVE liste le package affecté, la version fixée et un lien vers la NVD.
Version de Trivy dans Harbor v2.15 : Harbor v2.15.4 embarque trivy-adapter aligné sur Trivy v0.55.x, avec support des bases OS (Alpine, Debian, RHEL, Ubuntu), des gestionnaires de paquets applicatifs (npm, pip, cargo, go.mod) et des fichiers de lock.
Politiques de conformité par projet : dans Harbor → *Votre projet* → Policy → *Deployment Security* → choisissez le niveau de criticité qui bloque le pull. Par exemple, bloquer tout pull d'une image ayant au moins une CVE CRITICAL non corrigée.
Conseil sécurité : politiques d'admission Kubernetes
Combinez Harbor avec le contrôleur d'admission Cosign (via Kyverno ou Connaisseur) pour qu'aucune image non signée ou ayant une CVE CRITICAL ne puisse entrer dans votre cluster. Dans votre pipeline CI :
# Signer l'image avec Cosign après le build
cosign sign --key cosign.key registry.exemple.com/mon-projet/nginx:1.27-alpine
# Vérifier en CI avant le déploiement
cosign verify --key cosign.pub registry.exemple.com/mon-projet/nginx:1.27-alpineConfigurer une politique Kyverno qui exige la signature Harbor : chaque pod dont l'image n'est pas signée par votre clé est rejeté à l'admission. Cela ferme la brèche la plus exploitée dans les supply chain attacks post-CVE-2026-21589.
Dépannage : 5 erreurs réelles avec leurs causes
1. Error response from daemon: Get https://registry.exemple.com/v2/: x509: certificate signed by unknown authority
Cause : le certificat Let's Encrypt n'est pas encore propagé ou vous utilisez un certificat auto-signé.
Correction : sur chaque nœud Docker client, copiez fullchain.pem dans /etc/docker/certs.d/registry.exemple.com/ca.crt puis systemctl restart docker.
2. harbor-core redémarre en boucle avec Error 1045 (28000): Access denied for user 'root'@'localhost'
Cause : le volume PostgreSQL contient des données d'une installation précédente avec un autre mot de passe.
Correction : docker compose down -v (⚠️ supprime les données), modifiez le mot de passe dans harbor.yml, relancez ./prepare && docker compose up -d.
3. Scan Trivy bloqué sur Fetching vulnerability DB depuis 10 minutes
Cause : le VPS n'a pas accès à ghcr.io (pare-feu sortant restrictif) pour télécharger la base Trivy.
Correction : autorisez les sorties HTTPS vers ghcr.io et pkg.dev, ou activez le mode offline_scan: true après avoir pré-chargé la base avec trivy image --download-db-only.
4. unauthorized: unauthorized to access repository en CI malgré un docker login réussi
Cause : le compte de service n'a pas le rôle Developer sur le projet ciblé.
Correction : dans Harbor → *Votre projet* → Members → ajoutez le compte robot ou utilisateur avec le rôle Developer ou supérieur.
5. Interface Harbor inaccessible après renouvellement du certificat Let's Encrypt
Cause : le conteneur proxy (nginx) continue à servir l'ancien certificat chargé en mémoire.
Correction : docker compose restart proxy depuis le dossier d'installation Harbor. Idéalement, automatisez-le via le hook post-renouvellement décrit à l'étape 7.
Harbor vs Docker Hub vs GHCR vs Gitea Registry
Faites défiler le tableau
| Critère | Harbor self-hosted | Docker Hub | GHCR | Gitea Registry |
|---|---|---|---|---|
| Hébergement | Votre VPS (données UE) | Serveurs Docker Inc. (USA) | Serveurs GitHub/Microsoft (USA/UE opt.) | Votre VPS |
| Scan de vulnérabilités | Trivy intégré, auto au push | Docker Scout (payant) | Aucun natif | Aucun natif |
| Signature d'images | Cosign + Notary v2 | DCT (deprecated) | Cosign externe | Aucun natif |
| RBAC granulaire | 5 rôles par projet | Rôles d'organisation limités | Rôles GitHub (coarse) | Rôles Gitea basiques |
| Rate limit (pulls) | Aucun | 100/6h (gratuit), 5000/jour (Pro) | Lié aux limites GitHub | Aucun |
| OIDC/SSO natif | Oui (LDAP, OIDC, SAML) | Non (SSO entreprise payant) | SSO GitHub uniquement | OIDC partiel |
| Audit logs complets | Oui (IP, user, action, tag) | Limité | Partiel via GitHub audit | Limité |
| Conformité NIS2/GDPR | Totale (données on-premise) | Contractuelle seulement | DPA Microsoft disponible | Totale (on-premise) |
| Coût | VPS (~15-30 €/mois) | Gratuit / Pro ~5$/mois | Inclus GitHub Free/Pro | VPS partagé avec Gitea |