Dynamic Secret Management On Helm Charts in ArgoCD App
Yeah, the thought process must have crossed your mind too, deploying Helm charts via ArgoCD apps. That feeling when you can finally breathe without another long hour of tofu apply or terraform apply for a minimal change to your Helm chart values.
But wait... what about secrets? ๐ค
The Problemโ
This GitOps freedom comes with extra concerns for the security minded. secrets, secrets, secrets and the risk of exposing sensitive information in your Git repos.
In traditional IaC, we've unlocked many ways of managing secrets:
- HashiCorp Vault
- AWS Secrets Manager
- GCP Secret Manager
- Azure Key Vault
So how do we bring this to ArgoCD?
Your Optionsโ
You have a few paths here:
- External Secrets Operator - If you already have it installed, you're golden. Just use it.
- ArgoCD Vault Plugin (AVP) - Perfect for Vault fans like me
If you're thinking "I'll just use the normal Vault sidecar injection..." sorry, that won't work with Helm charts in ArgoCD. But don't stress, the ArgoCD Vault Plugin has you covered.
Prerequisitesโ
Before we dive in, make sure you have:
- โ HashiCorp Vault deployed and accessible
- โ ArgoCD installed in your cluster
- โ Kubernetes service account with Vault permissions
- โ Vault role and policy configured for your secrets
Step 1: Create the AVP Credentials Secretโ
First, create a secret containing your Vault connection details:
resource "kubectl_manifest" "avp_plugin_secret" {
yaml_body = <<-EOF
apiVersion: v1
kind: Secret
metadata:
name: argocd-vault-plugin-credentials
namespace: argocd
type: Opaque
stringData:
AVP_TYPE: vault
AVP_AUTH_TYPE: k8s
AVP_K8S_ROLE: helm-pod-role
VAULT_ADDR: https://vault.vault.svc.cluster.local:8200
VAULT_SKIP_VERIFY: "true"
EOF
}
Step 2: Configure the CMP Pluginโ
Next, create a ConfigMap that defines how ArgoCD should process Helm charts with Vault secrets:
resource "kubectl_manifest" "cmp_plugin_configmap" {
yaml_body = <<-EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: cmp-plugin
namespace: ${kubernetes_namespace.argocd[0].id}
data:
avp-helm.yaml: |
apiVersion: argoproj.io/v1alpha1
kind: ConfigManagementPlugin
metadata:
name: argocd-vault-plugin-helm
spec:
allowConcurrency: true
discover:
find:
command:
- sh
- "-c"
- "find . -name 'Chart.yaml' && find . -name 'values.yaml'"
generate:
command:
- bash
- "-c"
- |
set -e
helm template $ARGOCD_APP_NAME -n $ARGOCD_APP_NAMESPACE -f <(echo "$ARGOCD_ENV_HELM_VALUES") . --include-crds | \
argocd-vault-plugin generate -s argocd:argocd-vault-plugin-credentials -
lockRepo: false
EOF
}
๐ก What's happening here? The plugin runs
helm templateto render your chart, then pipes the output throughargocd-vault-pluginwhich replaces any<path:...>placeholders with actual secrets from Vault.
Step 3: Update ArgoCD Helm Valuesโ
Now update your ArgoCD Helm chart values to include the plugin sidecar:
repoServer:
replicas: 1
securityContext:
runAsUser: 999
runAsGroup: 999
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
serviceAccount:
create: true
name: argocd-repo-server
automountServiceAccountToken: true
volumes:
- name: cmp-plugin
configMap:
name: cmp-plugin
- name: cmp-tmp
emptyDir: {}
- name: var-files
emptyDir: {}
- name: plugins
emptyDir: {}
- name: custom-tools
emptyDir: {}
initContainers:
- name: download-tools
image: registry.access.redhat.com/ubi8
env:
- name: AVP_VERSION
value: "1.16.1"
- name: HELM_VERSION
value: "3.13.3"
command: [sh, -c]
args:
- >-
curl -L https://github.com/argoproj-labs/argocd-vault-plugin/releases/download/v$${AVP_VERSION}/argocd-vault-plugin_$${AVP_VERSION}_linux_arm64 -o argocd-vault-plugin &&
chmod +x argocd-vault-plugin &&
mv argocd-vault-plugin /custom-tools/ &&
curl -L https://get.helm.sh/helm-v$${HELM_VERSION}-linux-arm64.tar.gz | tar xz &&
mv linux-arm64/helm /custom-tools/helm &&
chmod +x /custom-tools/helm
volumeMounts:
- mountPath: /custom-tools
name: custom-tools
- name: avp-helm
command: [/var/run/argocd/argocd-cmp-server]
image: registry.access.redhat.com/ubi8
securityContext:
runAsNonRoot: true
runAsUser: 999
env:
- name: HELM_CACHE_HOME
value: /tmp/helm/cache
- name: HELM_CONFIG_HOME
value: /tmp/helm/config
- name: HELM_DATA_HOME
value: /tmp/helm/data
volumeMounts:
- mountPath: /var/run/argocd
name: var-files
- mountPath: /home/argocd/cmp-server/plugins
name: plugins
- mountPath: /tmp
name: cmp-tmp
- mountPath: /home/argocd/cmp-server/config/plugin.yaml
subPath: avp-helm.yaml
name: cmp-plugin
- name: custom-tools
subPath: argocd-vault-plugin
mountPath: /usr/local/bin/argocd-vault-plugin
- name: custom-tools
subPath: helm
mountPath: /usr/local/bin/helm
โ ๏ธ Don't forget: Your service account must be bound to the Vault role with appropriate secret read permissions.
Step 4: Deploy Your App with Secretsโ
Once your ArgoCD repo server is up and running, you can deploy apps that reference Vault secrets. Here's a Redis example:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: redis
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
chart: redis
repoURL: registry-1.docker.io/bitnamicharts
targetRevision: 22.0.7
plugin:
name: argocd-vault-plugin-helm
env:
- name: HELM_VALUES
value: |
fullnameOverride: redis
image:
registry: docker.io
repository: bitnamilegacy/redis
tag: 8.2.1-debian-12-r0
auth:
enabled: true
sentinel: true
password: <path:secret/data/redis-pod#password>
commonConfiguration: |-
appendonly no
save 900 1
save 300 10
save 60 10000
master:
persistence:
enabled: true
replica:
replicaCount: 3
sentinel:
enabled: true
masterSet: redismain
quorum: 2
auth:
enabled: true
password: <path:secret/data/redis-pod#password>
image:
registry: docker.io
repository: bitnamilegacy/redis-sentinel
tag: 8.2.1-debian-12-r0
serviceAccount:
create: true
name: redis-sa
automountServiceAccountToken: true
destination:
server: https://kubernetes.default.svc
namespace: microcore
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- Replace=true
The Magic: Secret Placeholdersโ
Notice the <path:secret/data/redis-pod#password> syntax? That's the AVP placeholder format:
<path:SECRET_PATH#KEY>
The plugin replaces these with actual values from Vault at deploy time. Your secrets never touch Git! ๐
Important: The
redis-saservice account must also be bound to your Vault role with read access to the secrets.
Key Takeawaysโ
- ArgoCD Vault Plugin bridges GitOps and secret management
- Works seamlessly with Helm charts via CMP sidecar
- Secrets are injected at deploy time, not stored in Git
- Use
<path:...>placeholders in your values
Referencesโ
Till next time, Peace be on you โ๐ฝ
