Installation et configuration de Harbor¶
Pre-requis¶
| Composant | Version minimale | Notes |
|---|---|---|
| Kubernetes | 1.28+ | Pour déploiement Helm |
| Helm | 3.12+ | Chart Harbor officiel |
| Podman (ou Docker) | 4.0+ | Pour déploiement standalone |
| PostgreSQL | 15+ | Instance externe, TLS active |
| MinIO / S3 | RELEASE.2024+ | Stockage des blobs |
| Certificat TLS | x509, SAN | Wildcard ou spécifique a Harbor |
Option A : Déploiement Helm (Kubernetes)¶
Preparer l'infrastructure¶
PostgreSQL externe¶
# Creer la base de donnees Harbor
psql -h postgres.example.com -U admin -c "
CREATE USER harbor WITH PASSWORD 'CHANGEME';
CREATE DATABASE harbor OWNER harbor;
CREATE DATABASE harbor_notary_server OWNER harbor;
CREATE DATABASE harbor_notary_signer OWNER harbor;
GRANT ALL PRIVILEGES ON DATABASE harbor TO harbor;
"
Bucket S3 (MinIO)¶
# Creer le bucket pour Harbor
mc alias set minio https://minio.example.com admin CHANGEME
mc mb minio/harbor-registry
mc policy set private minio/harbor-registry
Secret TLS¶
# Creer le secret TLS dans le namespace harbor
kubectl create namespace harbor
kubectl create secret tls harbor-tls \
--cert=harbor.example.com.crt \
--key=harbor.example.com.key \
-n harbor
Installer le chart Helm¶
Créer le fichier de valeurs :
# harbor-values.yaml
expose:
type: ingress
tls:
enabled: true
certSource: secret
secret:
secretName: harbor-tls
ingress:
hosts:
core: harbor.example.com
className: traefik
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
externalURL: https://harbor.example.com
persistence:
enabled: true
resourcePolicy: "keep"
imageChartStorage:
type: s3
s3:
region: us-east-1
bucket: harbor-registry
accesskey: MINIO_ACCESS_KEY
secretkey: MINIO_SECRET_KEY
regionendpoint: https://minio.example.com
secure: true
database:
type: external
external:
host: postgres.example.com
port: "5432"
username: harbor
password: CHANGEME
coreDatabase: harbor
sslmode: require
redis:
type: external
external:
addr: redis.example.com:6379
password: CHANGEME
# Composants Harbor
core:
replicas: 2
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
registry:
replicas: 2
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
jobservice:
replicas: 1
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
trivy:
enabled: true
replicas: 2
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
# Metriques Prometheus
metrics:
enabled: true
serviceMonitor:
enabled: true
# Garbage collection planifie
gc:
enabled: true
schedule: "0 2 * * 0" # Dimanche 2h du matin
deleteUntagged: true
# Deployer Harbor
helm install harbor harbor/harbor \
-f harbor-values.yaml \
-n harbor \
--wait --timeout 10m
# Verifier le deploiement
kubectl get pods -n harbor
Sortie attendue :
NAME READY STATUS RESTARTS AGE
harbor-core-6f8b9c7d4-abc12 1/1 Running 0 2m
harbor-core-6f8b9c7d4-def34 1/1 Running 0 2m
harbor-jobservice-5d4c3b2a1-ghi56 1/1 Running 0 2m
harbor-registry-7e6d5c4b3-jkl78 1/1 Running 0 2m
harbor-registry-7e6d5c4b3-mno90 1/1 Running 0 2m
harbor-trivy-8f7e6d5c4-pqr12 1/1 Running 0 2m
harbor-trivy-8f7e6d5c4-stu34 1/1 Running 0 2m
Option B : Déploiement Podman (standalone)¶
Pour les environnements sans Kubernetes, Harbor fournit un installeur offline.
Telecharger l'installeur¶
# Telecharger la derniere version
HARBOR_VERSION=2.11.0
curl -LO "https://github.com/goharbor/harbor/releases/download/v${HARBOR_VERSION}/harbor-offline-installer-v${HARBOR_VERSION}.tgz"
# Extraire
tar xzf "harbor-offline-installer-v${HARBOR_VERSION}.tgz"
cd harbor
Configurer harbor.yml¶
# harbor.yml
hostname: harbor.example.com
http:
port: 80
https:
port: 443
certificate: /etc/harbor/certs/harbor.example.com.crt
private_key: /etc/harbor/certs/harbor.example.com.key
harbor_admin_password: CHANGEME
database:
host: postgres.example.com
port: 5432
db_name: harbor
username: harbor
password: CHANGEME
max_idle_conns: 50
max_open_conns: 1000
ssl_mode: require
data_volume: /data/harbor
storage_service:
s3:
accesskey: MINIO_ACCESS_KEY
secretkey: MINIO_SECRET_KEY
region: us-east-1
regionendpoint: https://minio.example.com
bucket: harbor-registry
secure: true
trivy:
ignore_unfixed: false
security_check: vuln
insecure: false
jobservice:
max_job_workers: 10
log:
level: info
local:
rotate_count: 50
rotate_size: 200M
location: /var/log/harbor
metric:
enabled: true
port: 9090
path: /metrics
Installer et démarrer¶
# Preparer la configuration
./prepare
# Installer (avec Trivy)
./install.sh --with-trivy
# Verifier les conteneurs
podman ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
Configuration post-installation¶
Changer le mot de passe admin¶
# Se connecter a l'interface web
# https://harbor.example.com
# Login : admin / Harbor12345 (defaut)
# Ou via API
curl -X PUT https://harbor.example.com/api/v2.0/users/1/password \
-H "Content-Type: application/json" \
-u admin:Harbor12345 \
-d '{
"old_password": "Harbor12345",
"new_password": "NEW_SECURE_PASSWORD"
}'
Changer le mot de passe admin immédiatement
Le mot de passe par defaut est connu de tous. Le changer des la première connexion et stocker le nouveau mot de passe dans Vault.
Configurer l'authentification OIDC¶
# Configurer Keycloak comme provider OIDC
curl -X PUT https://harbor.example.com/api/v2.0/configurations \
-H "Content-Type: application/json" \
-u admin:NEW_SECURE_PASSWORD \
-d '{
"auth_mode": "oidc_auth",
"oidc_name": "Keycloak",
"oidc_endpoint": "https://keycloak.example.com/realms/organization",
"oidc_client_id": "harbor",
"oidc_client_secret": "CLIENT_SECRET",
"oidc_scope": "openid,profile,email,groups",
"oidc_groups_claim": "groups",
"oidc_admin_group": "harbor-admins",
"oidc_verify_cert": true,
"oidc_auto_onboard": true
}'
Créer les projets¶
# Projet pour les images de base
curl -X POST https://harbor.example.com/api/v2.0/projects \
-H "Content-Type: application/json" \
-u admin:NEW_SECURE_PASSWORD \
-d '{
"project_name": "library",
"public": true,
"metadata": {
"auto_scan": "true",
"severity": "critical",
"enable_content_trust": "true",
"prevent_vul": "true"
},
"storage_limit": 53687091200
}'
# Projet applicatif prive
curl -X POST https://harbor.example.com/api/v2.0/projects \
-H "Content-Type: application/json" \
-u admin:NEW_SECURE_PASSWORD \
-d '{
"project_name": "app-backend",
"public": false,
"metadata": {
"auto_scan": "true",
"severity": "high",
"enable_content_trust": "true",
"prevent_vul": "true"
},
"storage_limit": 21474836480
}'
Configurer le scanner¶
# Verifier que Trivy est enregistre
curl -s https://harbor.example.com/api/v2.0/scanners \
-u admin:NEW_SECURE_PASSWORD | jq '.[].name'
# → "Trivy"
# Definir Trivy comme scanner par defaut
SCANNER_UUID=$(curl -s https://harbor.example.com/api/v2.0/scanners \
-u admin:NEW_SECURE_PASSWORD | jq -r '.[0].uuid')
curl -X PATCH "https://harbor.example.com/api/v2.0/scanners/${SCANNER_UUID}" \
-H "Content-Type: application/json" \
-u admin:NEW_SECURE_PASSWORD \
-d '{"is_default": true}'
Activer le tag immutability¶
# Regle : les tags matchant v*.*.* sont immutables
curl -X POST https://harbor.example.com/api/v2.0/projects/app-backend/immutabletagrules \
-H "Content-Type: application/json" \
-u admin:NEW_SECURE_PASSWORD \
-d '{
"tag_selectors": [
{
"kind": "doublestar",
"decoration": "matches",
"pattern": "v*.*.*"
}
],
"scope_selectors": {
"repository": [
{
"kind": "doublestar",
"decoration": "repoMatches",
"pattern": "**"
}
]
}
}'
Configurer un endpoint de réplication¶
# Ajouter un endpoint de replication (registre DR)
curl -X POST https://harbor.example.com/api/v2.0/registries \
-H "Content-Type: application/json" \
-u admin:NEW_SECURE_PASSWORD \
-d '{
"name": "harbor-dr",
"type": "harbor",
"url": "https://harbor-dr.example.com",
"credential": {
"type": "basic",
"access_key": "robot-replicator",
"access_secret": "ROBOT_TOKEN"
},
"insecure": false
}'
# Creer une regle de replication push
curl -X POST https://harbor.example.com/api/v2.0/replication/policies \
-H "Content-Type: application/json" \
-u admin:NEW_SECURE_PASSWORD \
-d '{
"name": "dr-replication",
"src_registry": null,
"dest_registry": {"id": 1},
"dest_namespace_replace_count": 0,
"trigger": {
"type": "event_based"
},
"filters": [
{"type": "name", "value": "app-*/**"},
{"type": "tag", "value": "v*"}
],
"enabled": true,
"override": true
}'
Vérification du déploiement¶
# Tester le login
podman login harbor.example.com -u admin -p NEW_SECURE_PASSWORD
# Pousser une image de test
podman pull docker.io/library/alpine:3.20
podman tag docker.io/library/alpine:3.20 harbor.example.com/library/alpine:3.20
podman push harbor.example.com/library/alpine:3.20
# Verifier le scan
curl -s "https://harbor.example.com/api/v2.0/projects/library/repositories/alpine/artifacts/3.20?with_scan_overview=true" \
-u admin:NEW_SECURE_PASSWORD | jq '.scan_overview'
# Tester le pull
podman rmi harbor.example.com/library/alpine:3.20
podman pull harbor.example.com/library/alpine:3.20
Checklist post-installation
- Mot de passe admin change et stocke dans Vault
- Authentification OIDC configuree (Keycloak)
- Projets crees avec scan automatique et tag immutability
- Scanner Trivy verifie et base CVE a jour
- Robot accounts crees pour CI/CD et Kubernetes
- Réplication configuree vers le site DR
- Metriques Prometheus accessibles
- TLS verifie (pas de certificat auto-signe en production)