Introduction
The previous tutorial’s introduction discussed the benefits of managing secrets with GitOps via Argo CD and the Argo CD Vault Plugin. This guide explores using the alternative secrets management tool, External Secrets Operator1 (ESO). Although the implementation of these tools differ, both essential solve the same problem. How to manage secrets via GitOps.
How does External Secrets Operator differ from Argo CD Vault Plugin?
External Secrets Operator | Vault Plugin | |
---|---|---|
Supported Secret Providers |
|
|
Requires Argo CD? | ❌ | ✔️ |
Requires Custom Resources and controllers? | ✔️ | ❌ |
Auto sync changes to external API secret? | ✔️ | ❌ |
Connect to multiple secret managers simultaneously? | ✔️ | ❌ |
Prerequisites
For this guide you’ll need the following:
- A secret management system such as HashiCorp Vault (used in this guide), AWS Secrets Manager or Google Cloud Secret Manager (ESO supports many more systems)
- Beginner to intermediate knowledge of Argo CD, Kubernetes & Git/GitHub
- Terraform ≥ v0.15
How External Secrets Operator (ESO) Works
Here’s a high level overview of what this guide aims to achieve; ESO working with Argo CD to automate the creation and updating of secrets in a Kubernetes cluster via GitOps:
- Argo CD syncs the ExternalSecret manifest from a git repo creating the ExternalSecret resource inside the Kubernetes cluster.
- The ESO Controller inspects the configuration of the synced ExternalSecret to determine which SecretStore to use and the path the secret is located on in external API (Secrets Manager e.g. Vault).
- The Controller then queries the SecretStore for the credentials required to access the external API.
- ESO fetches the secret(s) from the secrets manager at the path named in the ExternalSecret.
- Finally, ESO creates/syncs an actual secret resource inside the cluster.
Simply put, the External Secrets Operator uses an ExternalSecret
resource as a template to define the creation and maintenance of Kubernetes Secrets.
Install External Secrets Operator on a Kubernetes Cluster
Install the External Secrets Operator either via Helm (using the helm
command) or via Argo CD as an App:
Helm
Add the External Secretes repo:
helm repo add external-secrets https://charts.external-secrets.io
Install the External Secrets Operator:
helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace \
--set installCRDs=true
Argo CD (my preference)
If you haven’t already, download and install the Argo CD CLI2 then login to your Argo CD installation using the argocd
command:
argocd login argocd.local
Username: admin
Password:
'admin:login' logged in successfully
Context 'argocd.local' updated
Create the external-secrets
app using the following argocd
command:
argocd app create external-secrets \
--repo https://charts.external-secrets.io \
--helm-chart external-secrets \
--revision 0.5.9 \
--dest-namespace external-secrets \
--dest-server https://kubernetes.default.svc
And then sync it to complete the installation on the cluster:
argocd app sync external-secrets
Create a secret containing your Secret Manager’s credentials
Before creating a SecretStore
, you’ll need to create a secret containing the credentials the SecretStore
will use to authenticate with your Secrets Manager. HashiCorp’s Vault is the secrets manager used in this guide. With token-based authentication.
Run the following command to store your Vault token in a file named vault_token.txt
:
echo -n 'VAULT_TOKEN' > ./vault_token.txt
Then use kubectl
to create a secret named vault-token
containing the credentials stored in the vault_token.txt
file:
kubectl -n external-secrets create secret generic vault-token \
--from-file=token=./vault_token.txt
Create a ClusterSecretStore
ESO offers two types of resources for defining how secret managers are accessed, SecretStore
and ClusterSecretStore
. The SecretStore
resource is namespaced and can only be referenced by ExternalSecrets
that reside in the the same namespace it does.
The ClusterSecretStore
however is a cluster scoped SecretStore
that can be referenced by all ExternalSecrets
from all namespaces.
For this guide we’ll create a CluserSecretStore
:
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.local"
path: "secret"
version: "v2"
auth:
# points to a secret that contains a vault token
# https://www.vaultproject.io/docs/auth/token
tokenSecretRef:
name: "vault-token"
key: "token"
namespace: external-secrets
The above ClusterSeceretStore
resource defines the url of the Vault server (line 8). It also references the name (vault-token
, line 15) and namespace (external-secrets
, line 17) of the secret containing the static token required to authenticate against Vault.
Create a Secret in Vault
Create an example secret in Vault using the following command:
vault kv put secret/demo-secrets tokenA=too_many_secrets tokenB=not_enough_secrets
Key Value
--- -----
created_time 2022-08-18T23:48:59.493381453Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
Create an ExternalSecret
Create a git repository containing the following example ExternalSecret
manifest (here’s my example repo):
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: demo-secret
namespace: default
spec:
refreshInterval: "15s"
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: demo-secret
data:
- secretKey: token
remoteRef:
key: secret/demo-secrets
property: tokenA
The ESO documentation example provides a detailed description of each parameter in an ExternalSecret
resource but the important lines from above are:
- 5. Namespace to create the secret resource in (
default
). - 9. Name of the `ClusterSecretStore` created earlier (
vault-backend
). - 12. Name of created secret.
- 14. Key for the secret value.
- 16. Path of the secret in the Vault server.
- 17. Value of the secret in the Vault server.
Sync this ExternalSecret
from the git repository to the cluster by creating an app
named demo-secret
in Argo CD:
argocd app create demo-secret --repo https://github.com/colinwilson/argocd-micro-example-apps.git --path external-secrets/ExternalSecret --dest-namespace default --dest-server https://kubernetes.default.svc --sync-policy automated
A breakdown of the flags specified in the above command:
argocd app create demo-secret
creates an Argo CD application named ‘demo-secret’--repo https://github.com/colinwilson/argocd-micro-example-apps.git
specifies the URL of your git repository--path external-secrets/ExternalSecret
specifies the path within the git repo to use as the app directory (my example)--dest-namespace default
the namespace to deploy the app to (default
)--dest-server https://kubernetes.default.svc
deploy the app internally i.e. the same Kubernetes cluster Argo CD is installed on--sync-policy automated
set the sync policy toautomated
(optional)
If successful you can view the demo-secret
app in the Argo CD UI:
Click on app to see further details:
You can see from the UI that the secret has been successfully created. You can confirm the value of the generated secret matches value in Vault by running the kubectl
command below:
kubectl -n default get secret demo-secret -o jsonpath="{.data.token}" | base64 -d; echo
too_many_secrets
Automatic Updates
Argo CD
Now the demo-secret
app has been setup and configured with an automated
sync policy, any changes to the ExternalSecret
in your git repository will be picked up by Argo CD’s polling and reflected in the cluster. Secrets can be automatically created in the cluster simply by pushing additional ExternalSecret
manifests to the repository directory.
External Secrets Operator
The ESO controller can monitor your Secret Manager(s) at regular intervals for changes in secret values and update the cluster secrets accordingly. This interval is set by spec.refreshInterval
in the ExternalSecret
. In the case of this guide the interval for demo-secret
is set to 15 seconds.
Summary
Once Argo CD has been configured with ESO and an external secrets manager, the workflow for managing secrets becomes quite painless.
- Pushing an
ExternalSecret
manifest to your repository will result in a secret being created on the cluster - Updates to secrets in your secret manager are automatically reflected in the secrets on the cluster
- Updating an
ExternalSecret
in your git repository results in a synchronisation of those changes on the cluster
This is GitOps! Syncing configurations in your Git repository with your infrastructure. All without any secrets being stored in the repository itself.
This guide covered the basic feature of ESO. Check out the documentation for more info on other features.
References
Footnotes
-
This guide is based on External Secrets Operator v0.5.9 ↩︎