CI/CD avec GitHub et signatures avec cosign
Objectifs
- Construire une image de conteneur automatiquement avec les GitHub Workflows / Actions
- Signer une image de conteneur avec cosign
- Configurer podman pour n'accepter que des images de conteneur signées
CI/CD avec les GitHub Workflows / Actions
Créer un compte GitHub si vous n'en avez pas déjà un
Ajouter un clé SSH à votre compte
Créer un nouveau dépot public sur GitHub
Cloner le dépôt en local sur votre machine
Ajouter un
Containerfilepour créer une image de conteneur :conteneur/Containerfile:FROM registry.fedoraproject.org/fedora:39 RUN touch /insa-cvlAjouter le workflow GitHub suivant :
.github/workflows/conteneur.yml:name: "Build container image" env: NAME: "conteneur" # Si votre identifiant de compte GitHub comporte des majuscules, remplacez # '${{ github.repository_owner }}' par votre identifiant en miniscule uniquement REGISTRY: ghcr.io/${{ github.repository_owner }} # Ce workflow ne sera exécuté que lors d'un 'push' sur la branche 'main' on: push: branches: - main # Authorise : # - Accès en lecture seule au contenu du dépôt Git # - Accès en lecture/écriture au "packages" pour avoir l'accès au registre de # conteneurs de GitHub permissions: contents: read packages: write jobs: build-push-image: runs-on: ubuntu-latest steps: # Récupération du dépôt Git - name: Checkout repo uses: actions/checkout@v4 # Construction de l'image de conteneur avec buildah - name: Build container image uses: redhat-actions/buildah-build@v2 with: context: ${{ env.NAME }} image: ${{ env.NAME }} tags: latest containerfiles: ${{ env.NAME }}/Containerfile layers: false oci: true # Publication de l'image de conteneur dans le regsitre de GitHub avec podman - name: Push to Container Registry uses: redhat-actions/push-to-registry@v2 id: push with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} image: ${{ env.NAME }} tags: latestVous devez obtenir une organisation comme suit :
$ tree -a . ├── conteneur │ └── Containerfile ├── .git │ └── ... └── .github └── workflows └── conteneur.ymlCommiter et poussez vos changements dans votre dépôt distant.
Observer la construction de votre image dans l'onglet Actions.
Vérifier que votre image est bien construite et disponible dans le registre de conteneur de GitHub à l'aide de l'interface Web.
Tester votre image :
$ podman run --rm -ti ghcr.io/<utilisateur GitHub>/conteneur # ls / ... insa-cvl ...
Sigstore et Cosign
Installer cosign. Suivre la documentation en ligne: https://docs.sigstore.dev/system_config/installation/
Générer une clé pivée et publique pour signer les conteneurs avec cosign
$ cosign generate-key-pairConnectez vous au registre de conteneurs GitHub de votre dépôt :
$ podman login ghcr.io/<utilisateur GitHub>Si vous utilisez l'authentification multi-facteurs, suivez la documentation GitHub pour créer un access token à utiliser comme mot de passe dans la commande précédente.
Si nécessaire, copiez les token d'authentification de podman à l'emplacement de ceux de Docker :
$ cp ${XDG_RUNTIME_DIR}/containers/auth.json ~/.docker/config.jsonSigner votre image avec cosign :
$ cosign sign --key cosign.key ghcr.io/<utilisateur GitHub>/conteneurVérifier la signature ajoutée à l'image avec cosign :
$ cosign verify --key cosign.pub ghcr.io/<utilisateur GitHub>/conteneur
Signatures avec cosign dans le workflow GitHub
Nous allons ensuite automatiser cette procédure à l'aide du workflow GitHub :
Ajouter la clé privée
cosign.keycomme secret dans GitHub :- Aller dans les préférences du dépôt, puis "Secrets et variables", puis "Actions"
- Ajouter la clé privée dans les secrets associés au dépôt
- Nommer le secret
COSIGN_PRIVATE_KEY
Ajouter la signature du conteneur à l'aide de cosign dans le workflow GitHub, à la suite du contenu existant :
... # Authentification au registre de conteneur de GitHub - name: Login to Container Registry uses: redhat-actions/podman-login@v1 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # Installation de cosign - uses: sigstore/cosign-installer@v3.3.0 # Signature de l'image de conteneur - name: Sign container image run: | cosign sign -y --key env://COSIGN_PRIVATE_KEY ${{ env.REGISTRY }}/${{ env.NAME }}@${{ steps.push.outputs.digest }} env: COSIGN_EXPERIMENTAL: false COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}Attendre qu'une nouvelle version de l'image soit publiée dans le registre de conteneur
Vérifier la signature de la nouvelle image à l'aide de cosign :
$ cosign verify --key cosign.pub ghcr.io/<utilisateur GitHub>/conteneur
Configurer Podman pour vérifier les signatures
Nous allons configurer podman sur notre système pour qu'il vérifie la signature des images de conteneurs et qu'il refuse les images non signées.
Installer la clé publique :
$ sudo mkdir -p /etc/pki/containers $ sudo cp cosign.pub /etc/pki/containers/ $ sudo restorecon -RFv /etc/pki/containersAjouter la configuration du registre pour récupérer les signatures :
$ cat /etc/containers/registries.d/ghcr.io-<utilisateur GitHub>.yamldocker: ghcr.io/<utilisateur GitHub>: use-sigstore-attachments: true$ sudo restorecon -RFv /etc/containers/registries.d/ghcr.io-<utilisateur GitHub>.yamlConfigurer ensuite la politique utilisée par podman. Modifier le fichier
/etc/containers/policy.jsonpour obtenir la configuration suivante. Les...signifient de laisser les valeurs qui se trouvent déjà dans le fichier à ces emplacements :{ "default": [ { "type": "reject" } ], "transports": { "docker": { "ghcr.io/<utilisateur GitHub>": [ { "type": "sigstoreSigned", "keyPath": "/etc/pki/containers/ghcr.io-<utilisateur GitHub>.pub", "signedIdentity": { "type": "matchRepository" } } ], ... "": [ { "type": "insecureAcceptAnything" } ] }, ... } }La page de man qui explique les options de configuration utilisées : containers-policy.json(5)
Vérifier que seul les conteneurs signés sont acceptés :
$ podman rmi --force ghcr.io/<utilisateur GitHub>/conteneur $ podman pull ghcr.io/<utilisateur GitHub>/conteneur $ podman run --rm -ti ghcr.io/<utilisateur GitHub>/conteneurAjouter une copie du workflow initial pour créer un autre conteneur non signé dans le même repo mais avec un autre tag et vérifier qu'il n'est pas accepté par podman
Références
- Exemple complet avec support multi-architectures: