Aller au contenu

Plugin Vault pour ArgoCD

Dans la logique GitOps, l'état de l'infrastructure et des applications est constamment défini dans les repository git qui est la source unique de vérité.

Néanmoins, un problème apparait quand il y a un besoin de passer des données sensibles (token, password, adresses, etc).

En terme de bonne pratique, stocker ces éléments dans le repository git, même si ce dernier est privé, n'est pas recommandé. Généralement, les secrets vont être stockés dans un coffre-fort (Vault en anglais).

ArgoCD a pour vocation à rester neutre vis-à-vis des solutions de passage de secrets dans les processus GitOps.

Néanmoins, ArgoCD reste ouvert aux extentions, ainsi qu'à l'utilisation de ressources spécialisées dans le passage de secrets.

La solution retenue dans notre cas est le plugin argocd-vault-plugin developpé par IBM.

Ce plugin permet de configurer dans des templates YAML, Helm ou Kustomize avec des <placeholder> (les chevrons < et > délimitent les "secrets") qui seront instanciés par des valeurs venant d'un Vault Hashicorp.

Exemple de fonctionnement

Avant de décrire l'installation et la configuration du plugin, étudions un exemple d'usage du plugin Vault/ArgoCD, l'utilisation se fait en 3 étapes :

  1. Création du secret sur Vault ;
  2. Création d'une application sur ArgoCD ;
  3. Passage d'un manifeste contenant un <placeholder>.

Cette sous-partie de la documentation part du principe que le plugin et le vault sont déjà configurés.

Création du secret dans Vault

Il d'abord créer un secret sur le vault :

$ curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{ "data": {"token": "Nqd2SFJN1zef2"} }'  http://127.0.0.1:8200/v1/secret/data/gitops/gitlab-monitoring/gitlab-token
Un secret token:Nqd2SFJN1zef2 est versé sur le chemin /secret/data/gitops/gitlab-token du vault.

Création de l'application Vault

Ensuite, il faut créer une application ArgoCD via son CRD en y précisant l'emploi du plugin argocd-vault-plugin :

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: vault-plugin-test-app
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  destination:
    namespace: default
    server: https://kubernetes.default.svc
  project: default
  source:
    path: .
    repoURL: $ADDRESSE_REPOSITORY
    targetRevision: HEAD
    plugin:
      name: argocd-vault-plugin
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

Passage d'un manifeste avec secret

Pour finir, il faut ajouter dans le repository défini dans l'application à l'étape précédente, un manifeste YAML contenant les deux éléments nécessaires pour l'exécution du manifest :

  • Une annotation avp.kubernetes.io/path avec en valeur le chemin du secret, dans notre cas secret/data/gitops/gitlab-token ;
  • Le <placeholder> où la valeur de ce dernier doit correspondre à la clef du secret passé au Vault, dans notre cas <token> !

Ensuite, il faut créer une application ArgoCD via son CRD.

Voici un exemple de fichier YAML fonctionnant avec le plugin :

kind: Secret
apiVersion: v1
metadata:
  name: example-secret
  annotations:
    avp.kubernetes.io/path: "secret/data/gitops/gitlab-token"
type: Opaque
stringData:
  password: <token>

Configuration du Vault

Voici les étapes du configuration du vault pour fonctionner avec le plugin Vault/ArgoCD, ce n'est pas une configuration spécifique, le plugin s'intègre au Vault comme le ferait une application lambda, en utilisant un AppRole.

Les étapes présentées couvrent la configuration globale du Vault (autorisation des AppRole et activation du KeyValue store).

Pour la configuration du Vault, il est nécessaire d'avoir accès à ce dernier , ainsi que d'avoir les droits d'admin sur user/token/AppRole avec les droits administrateur ou, a minima, les droits suivants si le Vault est déjà existant et que vous ne l'administrez pas :

  • Création d'AppRole ;
  • Création de Police de Sécurité ;
  • Création de Secret KV ou KV2.

Résumé des Étapes

  • Autorisation des AppRoles sur Vault ;
  • Activation du secret backend KV2 sur l'endpoint /secret ;
  • Application de la politique de sécurité pour le rôle GitOps ;
  • Création de l'AppRole GitOps ;
  • Récupération ID/Access Key pour l'AppRole GitOps ;
  • Création des secrets.

Avant de suivre la procédure, il faut exporter les variables d'environnement suivantes: VAULT_ADDR qui contient l'adresse du Vault ainsi que VAULT_TOKEN qui doit contenir un jeton avec des droits suffisant sur le Vault (Cf. supra pour les droits).

Il est aussi possible d'effectuer certaines des commandes ci-dessous en ligne de commandes du vault !

Autoriser AppRoles sur Vault

$ curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{"type": "approle"}'$VAULT_ADDR/v1/sys/auth/approle

Autoriser le secret backend KV sur l'endpoint souhaité

Cette commande s'effectue avec la ligne de commande :

$ vault secrets enable -version=2 -path=secret kv

Trouver équivalent en appels API

Création de la politique GitOps

Il faut créer une règle pour permettre au plugin de lire et lister les secrets (aucun droit supplémentaire n'est nécessaire pour ce faire). Dans la politique ci-dessous, les droit de lecture et de listage sont alloués (["read","list"]), sur tous les secrets du chemin /setcret/data/gitops/ sous le nom gitops-policy.

Tous les secrets liés au GitOps seront sous le chemin /secret/data/gitops/.

Le "sous-dossier" /data est précisé car il est obligatoire dans l'API du storage KV2.

Configuration du fichier de la politique gitops-policy.hcl :

path "/secret/data/gitops/*" 
{
  capabilities = ["read","list"]
}

Le fichier doit être convertit en JSON avant le versement, on utilise le script jsonify.sh d'Erwan broquaire ou des solution de conversion sur le net comme celle-ci .

Il faut ensuite verser cette politique d'accès au Vault :

curl --header "X-Vault-Token: $VAULT_TOKEN" --request PUT --data "@gitops-policy.json" "$VAULT_ADDR/v1/sys/policies/acl/gitops-policy"

Il est ensuite possible d'interroger cette politique pour vérifier qu'elle a bien été appliquée :

curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{"policies": "gitops-policy"}' $VAULT_ADDR/v1/auth/approle/role/gitops-role

Création de l'AppRole Gitops

Il faut ensuite créer l'AppRole qui sera utilisé par le plugin pour interroger les secrets dans le Vault. Dans l'exemple ci-dessous, le rôle créé porte le nom gitops-role :

curl --header "X-Vault-Token: $VAULT_TOKEN" http://127.0.0.1:8200/v1/auth/approle/role/gitops-role

Il faut ensuite interroger le role-id et le secret-id qui seront utilisés pour obtenir un jeton pour interagir avec le vault :

curl --header "X-Vault-Token: $VAULT_TOKEN" http://127.0.0.1:8200/v1/auth/approle/role/gitops-role/role-id
curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST http://127.0.0.1:8200/v1/auth/approle/role/gitops-role/secret-id

Il faut garder ces éléments, il seront utilisés pour insertion dans le fichier de configuration.

Création du secret test

Un secret test est créé pour ce tutoriel, sous le chemin /secret/data/gitops/gitlab-monitoring/gitlab-token, avec pour clef token et en valeur Nqd2SFJN1zef2 (ce sont des variables test) :

curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{ "data": {"token": "Nqd2SFJN1zef2"} }'  $VAULT_ADDR/v1/secret/data/gitops/gitlab-monitoring/gitlab-token

Configuration du plugin Vault ArgoCD

Nous suivons la procédure définie par ArgoCD.

Il est soit possible de passer l'exécutable du plugin par un initContainer et un EmptyDir via la solution de déploiement utilisée (Helm ou Kustomize et manifeste YAML), soit de construire sa propre image ArgoCD et l'utiliser dans le déploiement.

La première approche a été utilisée, car plus simple dans le déploiement et dans la gestion des mises-à-jour.

Voici la marche à suivre pour rendre un plugin disponible à ArgoCD (applicable à tout plugin qui se présente sous la forme d'un exécutable) :

  • Création d'un InitContainer dans le déploiement argocd-repo-server, qui télécharge la version souhaitée du plugin, et qui le passe dans un EmptyDir ;
  • L'exécutable du plugin est passé au container argocd-repo-server, via un volumeMount ;
  • Le plugin est déclaré dans la configMap argocd-cm ;
  • Les argument nécessaires au fonctionnement du plugin sont passés en variable d'environnement au pod argocd-repo-server ; dans le cas du plugin vault, un secret avec les variables est créé.

Le déploiement du plugin via Helm et Kustomize seront décrite plus bas. Mais il faut d'abord créer dans le cluster un secret qui va contenir les information de connexion au Vault qui va pouvoir être passé au pod argocd-repo-server.

Copier le manifeste ci-dessous, configurer les variables et l'appliquer, dans l'espace de nommage qui va accueillir l'ArgoCD :

kind: Secret
apiVersion: v1
metadata:
  name: argocd-vault-plugin-credentials
  namespace: argocd
type: Opaque
stringData:
  VAULT_ADDR: <Addresse du Vault>
  AVP_TYPE: vault
  AVP_AUTH_TYPE: approle
  AVP_ROLE_ID: <Id du role>
  AVP_SECRET_ID: <secret ID du Role>

Déploiement d'ArgoCD et du plugin vault via Helm

Il existe un repository Helm d'ArgoCD, maintenu par la communauté. Il est possible de déployer le plugin entièrement via une configuration du fichier values.yaml du chart Helm. Voici ce fichier configuré :

#Argocd Server Config
server:
  config:
    url: https://argocd.example.com
    application.instanceLabelKey: argocd.argoproj.io/instance
    configManagementPlugins: |- #Theses lines will append the argocd-vault-plugin in the configmap
      - name: argocd-vault-plugin
        generate:
          command: ["argocd-vault-plugin"]
          args: ["generate", "./"]
      - name: argocd-vault-plugin-helm
        generate:
          command: ["sh", "-c"]
          args: ["helm template . > all.yaml && argocd-vault-plugin generate all.yaml"]
      - name: argocd-vault-plugin-kustomize
        generate:
          command: ["sh", "-c"]
          args: ["kustomize build . > all.yaml && argocd-vault-plugin generate all.yaml"]
repoServer:
  envFrom: #Thise line will mount the values of the secret in the repo server
   - secretRef:
       name: argocd-vault-plugin-credentials
  volumeMounts: #Mounting the binary download by the initContainer in the main pod
    - name: custom-tools
      mountPath: /usr/local/bin/argocd-vault-plugin
      subPath: argocd-vault-plugin
  volumes: #Creation of an EmptyDir sha red by the initContainer and the repo-server container 
    - name: custom-tools
      emptyDir: {}
  initContainers: #Creation of an init container that download the  binary
    - name: download-tools
      image: busybox
      command: [sh, -c]
      args:
        - wget -O argocd-vault-plugin
          https://github.com/IBM/argocd-vault-plugin/releases/download/v1.5.0/argocd-vault-plugin_1.5.0_linux_amd64
          chmod +x argocd-vault-plugin && mv argocd-vault-plugin /custom-tools/
      volumeMounts:
        - mountPath: /custom-tools
          name: custom-tools

Il est néanmoins nécessaire de modifier quelques points avant d'appliquer :

  • repoServer/envFrom : bien vérifier que le nom du secret correspond à celui créé à l'étape précédente ;
  • repoServer/initContainer[0].args: vérifier la version du binaire souhaitée, dans l'exemple ci-dessus, la version téléchargée est la v1.5.0.

Il est possible de rajouter des configurations au fichier values.yaml ci-dessus.

Déploiement d'ArgoCD et du plugin Vault via Kustomize

Il est aussi possible de déployer le plugin Vault via l'utilisation de l'outil Kustomize, afin d'ajouter au manifeste d'ArgoCD les modifications à apporter.

Voici le lien du manifeste officiel d'installation maintenu par ArgoCD ; ce dernier déploie ArgoCD sans la haute disponibilité (ce qui n'est pas un point critique car ArgoCD ne possède aucune donnée), mais un manifeste compatible avec la haute disponibilité est ici .

Pour déployer via Kustomize, il faut définir les ressources à modifier, ainsi qu'un manifeste kustomization.yml :

  • argocd-cm-kustomize.yaml :
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
data:
  configManagementPlugins: |-
    - name: argocd-vault-plugin
      generate:
        command: ["argocd-vault-plugin"]
        args: ["generate", "./"]
    - name: argocd-vault-plugin-helm
      generate:
        command: ["sh", "-c"]
        args: ["helm template . > all.yaml && argocd-vault-plugin generate all.yaml"]
    - name: argocd-vault-plugin-kustomize
      generate:
        command: ["sh", "-c"]
        args: ["kustomize build . > all.yaml && argocd-vault-plugin generate all.yaml"]
  • argcd-repo-vault-kustomize.yaml :
apiVersion: apps/v1
kind: Deployment
metadata:
  name: argocd-repo-server
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-repo-server
  template:
    spec:
      containers:
      - name: argocd-repo-server
        volumeMounts:
        - name: custom-tools
          mountPath: /usr/local/bin/argocd-vault-plugin
          subPath: argocd-vault-plugin
        envFrom:
        - secretRef:
            name: argocd-vault-plugin-credentials
      volumes:
      - name: custom-tools
        emptyDir: {}
      initContainers:
      - name: download-tools
        image: alpine:3.8
        command: [sh, -c]
        args:
          - >-
            wget -O argocd-vault-plugin
            https://github.com/IBM/argocd-vault-plugin/releases/download/v1.5.0/argocd-vault-plugin_1.5.0_linux_amd64 &&
            chmod +x argocd-vault-plugin &&
            mv argocd-vault-plugin /custom-tools/
        volumeMounts:
          - mountPath: /custom-tools
            name: custom-tools
      automountServiceAccountToken: true

Il faut penser à vérifier et à configurer la version du plugin souhaité dans spec.template.spec.initContainer[0].args ainsi que le nom du secret dans spec.template.spec.container[0].volumeMount.

  • kustomization.yml :
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

patchesStrategicMerge:
- argocd-repo-vault-plugin-kustomize.yaml
- argocd-cm-kustomize.yaml

Afin de vérifier le bon fonctionnement du kustomize, il est possible de créer des modèles pour les manifestes avec :

$ kubectl kustomize ./

Et ensuite de les appliquer avec :

$ kubectl apply -k ./

Les deux commandes précédentes sont appliquées dans le dossier où les trois manifestes décrits précédemment sont présents.

Liens