Aller au contenu

Integration

Vue d'ensemble des integrations

Le service CI/CD s'integre avec l'ensemble du catalogue de services pour former une chaîne logicielle complète.

graph TD
    Vault["Vault<br/>(secrets)"] --> CI
    Harbor["Harbor<br/>(registre)"] --> CI
    Sonar["SonarQube<br/>(qualite)"] --> CI
    Trivy["Trivy<br/>(securite)"] --> CI
    CI["Gitea Actions (CI)<br/>Vault inject → Build → Test → SonarQube → Trivy → Push"] --> CD
    CD["Flux v2 (CD)<br/>SOPS secrets | GitRepository → Kustomization → Cluster | Metrics → Grafana"]

Secrets depuis Vault

Gitea Actions : variables chiffrees + Vault

Les secrets peuvent etre injectes de deux manières dans les workflows Gitea Actions.

Méthode 1 — Variables CI (simple) :

Stocker les secrets dans Gitea (Settings > Secrets) et les referencer dans le workflow :

steps:
  - name: Login to Harbor
    run: |
      podman login harbor.example.com \
        -u ${{ secrets.HARBOR_USER }} \
        -p ${{ secrets.HARBOR_PASSWORD }}

Méthode 2 — Injection directe depuis Vault (recommande) :

steps:
  - name: Fetch secrets from Vault
    run: |
      export VAULT_ADDR="https://vault.example.com"
      export VAULT_TOKEN=$(vault write -field=token \
        auth/jwt/login role=ci-runner \
        jwt="${ACTIONS_ID_TOKEN_REQUEST_TOKEN}")

      # Recuperer les secrets
      HARBOR_USER=$(vault kv get -field=username secret/ci/harbor)
      HARBOR_PASS=$(vault kv get -field=password secret/ci/harbor)

      # Masquer dans les logs
      echo "::add-mask::${HARBOR_PASS}"

      podman login harbor.example.com \
        -u "${HARBOR_USER}" -p "${HARBOR_PASS}"

Flux v2 : secrets chiffres avec SOPS

Flux v2 supporte nativement le chiffrement des secrets dans Git via SOPS (Mozilla). Les secrets sont chiffres dans le dépôt et dechiffres par le kustomize-controller au moment de l'application.

# Configurer SOPS avec age (recommande)
# .sops.yaml dans le depot de manifestes
creation_rules:
  - path_regex: .*\.enc\.yaml$
    age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Chiffrer un secret
sops --encrypt --in-place overlays/production/secret.enc.yaml

# Le secret chiffre est commite dans Git en toute securite
git add overlays/production/secret.enc.yaml
git commit -m "chore: add encrypted database secret"
git push
# Kustomization avec decryption SOPS
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: myapp-production
  namespace: flux-system
spec:
  interval: 5m
  prune: true
  sourceRef:
    kind: GitRepository
    name: manifests
  path: ./overlays/production
  decryption:
    provider: sops
    secretRef:
      name: sops-age    # Secret contenant la cle age
# Creer le secret contenant la cle de dechiffrement
cat age-key.txt | kubectl create secret generic sops-age \
  --namespace=flux-system \
  --from-file=age.agekey=/dev/stdin

Alternative : External Secrets Operator

Pour les secrets stockes dans Vault et non dans Git, utiliser l'External Secrets Operator (ESO) :

# external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: myapp-secrets
  namespace: app-production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore

  target:
    name: myapp-secrets
    creationPolicy: Owner

  data:
    - secretKey: DATABASE_URL
      remoteRef:
        key: secret/data/myapp/production
        property: database_url

    - secretKey: API_KEY
      remoteRef:
        key: secret/data/myapp/production
        property: api_key
# cluster-secret-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "https://vault.example.com"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "external-secrets"
          serviceAccountRef:
            name: external-secrets
            namespace: external-secrets

SOPS vs External Secrets Operator

  • SOPS : secrets chiffres dans Git, dechiffres par Flux au deploy. Ideal pour les secrets applicatifs geres par l'equipe dev (tout est dans Git)
  • ESO : secrets restes dans Vault, synchronises vers des Kubernetes Secrets. Ideal pour les secrets d'infrastructure geres par l'equipe ops

Push d'images vers Harbor

Configuration du projet Harbor

# Creer un projet dans Harbor pour les images CI
curl -s -X POST \
  -H "Authorization: Basic $(echo -n 'admin:Harbor12345' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "project_name": "org",
    "public": false,
    "storage_limit": 53687091200,
    "metadata": {
      "auto_scan": "true",
      "severity": "high",
      "prevent_vul": "true"
    }
  }' \
  "https://harbor.example.com/api/v2.0/projects"

Robot account pour le CI

# Creer un robot account avec permissions push uniquement
curl -s -X POST \
  -H "Authorization: Basic $(echo -n 'admin:Harbor12345' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ci-pusher",
    "duration": -1,
    "level": "project",
    "permissions": [{
      "namespace": "org",
      "kind": "project",
      "access": [
        {"resource": "repository", "action": "push"},
        {"resource": "repository", "action": "pull"},
        {"resource": "tag", "action": "create"}
      ]
    }]
  }' \
  "https://harbor.example.com/api/v2.0/projects/org/robots"

Workflow avec push Harbor

- name: Push to Harbor
  run: |
    IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}"
    podman push "${IMAGE}"

    # Tag latest si branche main
    if [ "${GITHUB_REF}" = "refs/heads/main" ]; then
      podman tag "${IMAGE}" "${REGISTRY}/${IMAGE_NAME}:latest"
      podman push "${REGISTRY}/${IMAGE_NAME}:latest"
    fi

Quality gate avec SonarQube

Integration dans le workflow

- name: SonarQube Analysis
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
    SONAR_HOST_URL: https://sonar.example.com
  run: |
    sonar-scanner \
      -Dsonar.projectKey=myapp \
      -Dsonar.sources=. \
      -Dsonar.host.url=${SONAR_HOST_URL} \
      -Dsonar.token=${SONAR_TOKEN} \
      -Dsonar.qualitygate.wait=true \
      -Dsonar.qualitygate.timeout=300

Quality gate comme condition de merge

Configurer un webhook SonarQube vers Gitea pour mettre a jour le statut du commit :

# Proprietes du projet SonarQube (sonar-project.properties)
sonar.projectKey=myapp
sonar.projectName=My Application
sonar.sources=src
sonar.tests=tests
sonar.language=go
sonar.go.coverage.reportPaths=coverage.out
sonar.qualitygate.wait=true

Quality gate bloquante

Avec sonar.qualitygate.wait=true, le step échoue si le quality gate n'est pas passe. Combine avec la protection de branche Gitea, cela empeche le merge de code qui ne respecte pas les standards de qualité.


Scan de sécurité avec Trivy

Scan d'image dans le pipeline

- name: Scan image with Trivy
  run: |
    trivy image \
      --exit-code 1 \
      --severity HIGH,CRITICAL \
      --ignore-unfixed \
      --format table \
      --output trivy-report.txt \
      ${REGISTRY}/${IMAGE_NAME}:${TAG}

- name: Upload Trivy report
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: trivy-report
    path: trivy-report.txt

Scan du filesystem (IaC, dependencies)

- name: Scan filesystem
  run: |
    # Scan des dependances (go.sum, package-lock.json, etc.)
    trivy fs --exit-code 1 --severity HIGH,CRITICAL .

    # Scan des fichiers IaC (Dockerfile, Kubernetes manifests)
    trivy config --exit-code 1 --severity HIGH,CRITICAL .

Trivy integre dans Harbor

Harbor peut scanner automatiquement les images à chaque push (configure dans le projet Harbor avec auto_scan: true). Le pipeline peut alors vérifier le résultat :

- name: Check Harbor scan result
  run: |
    # Attendre le scan Harbor
    sleep 30

    VULN=$(curl -s \
      -H "Authorization: Basic ${HARBOR_AUTH}" \
      "https://harbor.example.com/api/v2.0/projects/org/repositories/myapp/artifacts/${TAG}?with_scan_overview=true" \
      | jq '.scan_overview."application/vnd.security.vulnerability.report; version=1.1".severity')

    if [ "${VULN}" = "\"Critical\"" ]; then
      echo "Critical vulnerabilities found — blocking deployment"
      exit 1
    fi

Notifications Flux v2

Flux dispose d'un notification-controller intégré qui gère les alertes et les webhooks entrants.

Provider Mattermost

# clusters/production/notifications/provider-mattermost.yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
  name: mattermost
  namespace: flux-system
spec:
  type: mattermost
  channel: cd-alerts
  address: https://mattermost.example.com/hooks/xxxx

Alertes sur echec de reconciliation

# clusters/production/notifications/alert-cd.yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
  name: cd-alerts
  namespace: flux-system
spec:
  providerRef:
    name: mattermost
  eventSeverity: error
  eventSources:
    - kind: Kustomization
      name: "*"
    - kind: HelmRelease
      name: "*"
    - kind: GitRepository
      name: "*"
  summary: "Flux reconciliation failure"

Alertes sur tous les événements (info + error)

# clusters/production/notifications/alert-all.yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
  name: cd-all-events
  namespace: flux-system
spec:
  providerRef:
    name: mattermost
  eventSeverity: info
  eventSources:
    - kind: Kustomization
      name: myapp-production
    - kind: HelmRelease
      name: myapp

Receiver pour webhooks entrants (Gitea)

Le Receiver permet à Gitea de notifier Flux d'un changement immédiatement, sans attendre le poll :

# clusters/production/notifications/receiver-gitea.yaml
apiVersion: notification.toolkit.fluxcd.io/v1
kind: Receiver
metadata:
  name: gitea-webhook
  namespace: flux-system
spec:
  type: generic
  secretRef:
    name: receiver-token
  resources:
    - kind: GitRepository
      name: manifests
# Creer le secret du receiver
kubectl -n flux-system create secret generic receiver-token \
  --from-literal=token=$(head -c 32 /dev/urandom | base64)

# Recuperer l'URL du webhook
kubectl -n flux-system get receiver gitea-webhook -o jsonpath='{.status.webhookPath}'
# Resultat : /hook/xxxxxxxxxxxxxxxxx

# Configurer ce path comme webhook dans Gitea :
# Repo > Settings > Webhooks > Add > POST https://flux-webhook.example.com/hook/xxx

Notifications CI sur echec

Mattermost sur echec

# Dans le workflow Gitea Actions
- name: Notify Mattermost on failure
  if: failure()
  run: |
    curl -s -X POST \
      -H "Content-Type: application/json" \
      -d '{
        "channel": "ci-alerts",
        "username": "Gitea CI",
        "icon_url": "https://gitea.io/images/gitea.png",
        "text": "### :red_circle: Pipeline echoue\n| Detail | Valeur |\n|---|---|\n| Repo | '"${{ github.repository }}"' |\n| Branche | '"${{ github.ref_name }}"' |\n| Commit | '"${{ github.sha }}"' |\n| Auteur | '"${{ github.actor }}"' |\n| [Voir le pipeline]('"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"') |"
      }' \
      "${{ secrets.MATTERMOST_WEBHOOK_URL }}"

Notification mail sur echec

- name: Send email on failure
  if: failure()
  run: |
    curl -s --url "smtps://smtp.example.com:465" \
      --ssl-reqd \
      --mail-from "ci@example.com" \
      --mail-rcpt "${{ github.event.pusher.email }}" \
      --user "ci@example.com:${{ secrets.SMTP_PASSWORD }}" \
      -T - <<EOF
    From: CI Pipeline <ci@example.com>
    To: ${{ github.event.pusher.email }}
    Subject: [CI] Pipeline echoue — ${{ github.repository }}

    Le pipeline CI a echoue sur le commit ${{ github.sha }}.
    Branche : ${{ github.ref_name }}
    Voir : ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
    EOF

Dashboards Grafana

Metriques a collecter

Metrique Source Indicateur
Duree du pipeline Gitea Actions Temps moyen build-to-deploy
Taux de succès Gitea Actions % de pipelines verts
Fréquence de déploiement Flux v2 Nombre de reconciliations par jour
MTTR (Mean Time To Repair) Flux v2 Temps entre echec et correction
Drift detection Flux v2 Nombre de corrections de drift par jour

Flux v2 expose des metriques Prometheus

Les controllers Flux exposent des metriques Prometheus nativement :

# ServiceMonitor pour Flux v2
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: flux-system
  namespace: flux-system
spec:
  selector:
    matchLabels:
      app.kubernetes.io/part-of: flux
  endpoints:
    - port: http-prom
      interval: 30s

Metriques Flux cles

Metrique Prometheus Description
gotk_reconcile_duration_seconds Duree de reconciliation par ressource
gotk_reconcile_condition Condition de chaque ressource (Ready, Stalled...)
controller_runtime_reconcile_total Nombre total de reconciliations
controller_runtime_reconcile_errors_total Nombre d'erreurs de reconciliation

Dashboard Grafana recommande

Importer le dashboard communautaire Flux : ID 16714 (Flux Cluster Stats).

Pour Gitea Actions, créer un dashboard custom base sur les webhooks Gitea ou l'API :

# Exemple : recuperer les statistiques de runs
curl -s -H "Authorization: token ${GITEA_TOKEN}" \
  "https://git.example.com/api/v1/repos/org/myapp/actions/runs?limit=50" \
  | jq '[.workflow_runs[] | {status, conclusion, run_started_at, updated_at}]'