Secure secrets for apps with Vault Dedicated and External Secrets Operator
In this tutorial, you learn how to use IBM Cloud Vault Enterprise to manage secrets for applications that run in your IBM Cloud Kubernetes Service cluster by using the External Secrets Operator open-source tool.
You're a developer in an organization, and your team is using Kubernetes Service to deploy containerized apps and services on IBM Cloud. You want to store your application secrets in Vault Dedicated, IBM Cloud's managed HashiCorp Vault service, where you can encrypt them at rest, manage their lifecycle, and easily rotate them.
With Vault Dedicated and External Secrets Operator, you can centralize and secure the secrets that are used by the apps that run in your Kubernetes clusters. Rather than injecting your secrets at deployment time, you can configure your apps to securely retrieve secrets from Vault Dedicated at run time. For example, consider the following scenario:
- As a developer, you use Vault Dedicated to store a secret for an application that you want to deploy in a Kubernetes cluster.
- You configure the External Secrets Operator to connect to your Vault Dedicated instance using the HashiCorp Vault provider.
- The External Secrets controller fetches the
ExternalSecretsobjects in the configuration file that you defined by using the Kubernetes API. - At application run time, the controller retrieves the secret data from Vault Dedicated, and converts the
ExternalSecretsobjects to Kubernetes secrets for your cluster.
This scenario features a third-party tool that can impact the compliance readiness of workloads that run in your Kubernetes cluster. If you add a community or third-party tool, keep in mind that you are responsible for maintaining the compliance of your apps, and working with the appropriate provider to troubleshoot any issues. For more information, see Your responsibilities with using IBM Cloud Kubernetes Service.
Before you begin
Before you get started, be sure that you have Administrator platform access so that you can create account credentials and provision resources. You also need the following prerequisites:
-
jqhelps you slice and filter JSON data. You usejqin this tutorial to grab and use stored environment variables. -
A Vault Dedicated Instance provisioned in your IBM Cloud account. For more information, see Setting up your Vault Dedicated instance.
Set up your environment
To work with Vault Dedicated and Kubernetes Service, you need to create a cluster in your IBM Cloud account and configure access to your Vault Dedicated instance.
Create a Kubernetes cluster
Create a Kubernetes cluster in your IBM Cloud account.
-
From the command line, log in to IBM Cloud through the IBM Cloud CLI.
ibmcloud loginIf the login fails, run the
ibmcloud login --ssocommand to try again. The--ssoparameter is required when you log in with a federated ID. If this option is used, go to the link listed in the CLI output to generate a one-time passcode. -
Select the account, region, and resource group where you want to create your cluster.
ibmcloud target -r REGION -g RESOURCE_GROUPReplace
REGIONwith your target region (for example,au-syd) andRESOURCE_GROUPwith your resource group name. -
Create a Kubernetes cluster.
ibmcloud ks cluster create vpc-gen2 --zone ZONE --flavor FLAVOR --workers 1 --name eso-test-cluster --vpc-id VPC_ID --subnet-id SUBNET_IDReplace
ZONE,FLAVOR,VPC_ID, andSUBNET_IDwith your values. Provisioning your Kubernetes cluster takes 5 - 15 minutes to complete. -
Before you continue to the next step, verify that your cluster is provisioned successfully.
ibmcloud ks worker ls --cluster eso-test-clusterWhen your worker node is finished provisioning, the status changes to Ready.
-
Set the context for your Kubernetes cluster in the CLI.
ibmcloud ks cluster config --cluster eso-test-cluster -
Verify that
kubectlcommands run properly and that the Kubernetes context is set to your cluster.kubectl config current-context
Prepare your Vault Dedicated instance
Configure your Vault Dedicated instance to start working with secrets and set up authentication for External Secrets Operator.
-
Export environment variables with your Vault Dedicated instance details.
export VAULT_DEDICATED_ADDR="https://<your-vault_dedicated-instance-id>.vault.<region>.appdomain.cloud" export VAULT_DEDICATED_NAMESPACE="admin"Replace
<your-vault_dedicated-instance-id>with your Vault Dedicated instance ID and<region>with your Vault Dedicated region (for example,au-syd). -
Get a Vault token from your Vault Dedicated instance.
You can generate a token from the Vault Dedicated UI or by using the Vault CLI. For development and testing, you can use a root token. For production, create a token with appropriate policies.
export VAULT_TOKEN="<your-vault-token>" -
Verify the KV secrets engine mount point in Vault Dedicated.
Vault Dedicated instances have the KV v2 secrets engine mounted at
kv/by default. You can verify this in the Vault Dedicated UI or by listing mounts.curl -k -X GET \ -H "X-Vault-Token: $VAULT_TOKEN" \ -H "X-Vault-Namespace: $VAULT_DEDICATED_NAMESPACE" \ $VAULT_DEDICATED_ADDR/v1/sys/mounts | jq -
Create a test secret in Vault Dedicated.
curl -k -X POST \ -H "X-Vault-Token: $VAULT_TOKEN" \ -H "X-Vault-Namespace: $VAULT_DEDICATED_NAMESPACE" \ -d '{"data":{"username":"user123","password":"cloudy-rainy-coffee-book"}}' \ $VAULT_DEDICATED_ADDR/v1/kv/data/example_username_passwordNote that Vault Dedicated uses
kv/as the mount path for the KV secrets engine. -
Verify the secret was created.
curl -k -X GET \ -H "X-Vault-Token: $VAULT_TOKEN" \ -H "X-Vault-Namespace: $VAULT_DEDICATED_NAMESPACE" \ $VAULT_DEDICATED_ADDR/v1/kv/data/example_username_password | jq
Install External Secrets Operator
Install External Secrets Operator using Helm.
-
Add the External Secrets Helm repository.
helm repo add external-secrets https://charts.external-secrets.io helm repo update -
Install External Secrets Operator.
helm install external-secrets \ external-secrets/external-secrets \ --namespace external-secrets \ --create-namespace \ --set installCRDs=true -
Verify the installation.
kubectl get pods -n external-secretsWait until all pods are in Running state.
-
Verify the Custom Resource Definitions (CRDs) are installed.
kubectl get crd | grep external-secretsYou should see CRDs like
secretstores,clustersecretstores, andexternalsecrets.
Configure SecretStore for Vault Dedicated
Create a SecretStore resource that defines how External Secrets Operator connects to your Vault Dedicated instance.
-
Create a Kubernetes secret with your Vault token.
kubectl create secret generic vault-token \ --namespace external-secrets \ --from-literal=token="$VAULT_TOKEN" -
Create a
secretstore.yamlfile.touch secretstore.yaml -
Add the following configuration to the file.
apiVersion: external-secrets.io/v1beta1 kind: SecretStore metadata: name: vault-dedicated-secretstore namespace: default spec: provider: vault: server: "<VAULT_DEDICATED_ADDR>" path: "kv" version: "v2" namespace: "admin" auth: tokenSecretRef: name: "vault-token" key: "token" namespace: "external-secrets"Replace
<VAULT_DEDICATED_ADDR>with your Vault Dedicated instance address. Note that thepathis set tokv, which is the default mount point for the KV secrets engine in Vault Dedicated. -
Apply the SecretStore configuration.
kubectl apply -f secretstore.yaml -
Verify the SecretStore is valid.
kubectl get secretstore vault-dedicated-secretstore -n default kubectl describe secretstore vault-dedicated-secretstore -n defaultThe status should show Valid if the connection to Vault Dedicated is successful.
Create an ExternalSecret
Create an ExternalSecret resource that defines which secrets to fetch from Vault Dedicated.
-
Create an
externalsecret.yamlfile.touch externalsecret.yaml -
Add the following configuration.
apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: vault-dedicated-app-secret namespace: default spec: refreshInterval: 1h secretStoreRef: name: vault-dedicated-secretstore kind: SecretStore target: name: my-k8s-secret creationPolicy: Owner data: - secretKey: username remoteRef: key: example_username_password property: username - secretKey: password remoteRef: key: example_username_password property: passwordThe
refreshIntervaldetermines how often External Secrets Operator polls Vault Dedicated for updates. The default and recommended value is 1 hour. -
Apply the ExternalSecret configuration.
kubectl apply -f externalsecret.yaml -
Verify that the External Secrets Operator fetched the secret from Vault Dedicated.
kubectl get secret my-k8s-secret -o json | jq '.data | map_values(@base64d)'Example output:
{ "password": "cloudy-rainy-coffee-book", "username": "user123" }Success! You're now able to fetch secret data from your Vault Dedicated instance and use it in your Kubernetes cluster.
Deploy an app to the cluster
Finally, you can deploy an application in your cluster that uses the Vault Dedicated secret. At application run time, the secret data that is fetched from Vault Dedicated is converted to a Kubernetes secret that can be used by your cluster.
-
Create a simple test deployment that uses the secret.
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: test-app namespace: default spec: containers: - name: app image: busybox command: ['sh', '-c', 'echo "Username: \$USERNAME"; echo "Password: \$PASSWORD"; sleep 3600'] env: - name: USERNAME valueFrom: secretKeyRef: name: my-k8s-secret key: username - name: PASSWORD valueFrom: secretKeyRef: name: my-k8s-secret key: password EOF -
Check the pod logs to verify the secret was injected.
kubectl logs test-app -n defaultExpected output:
Username: user123 Password: cloudy-rainy-coffee-book
Looking for more examples on how to deploy an app? Check out Deploying Kubernetes-native apps in clusters to find out more about deploying applications.
(Optional) Clean up resources
If you no longer need the resources that you created in this tutorial, you can complete the following steps to remove them from your account.
-
Delete your test Kubernetes cluster.
ibmcloud ks cluster rm --cluster eso-test-cluster -
Clean up test secrets in Vault dedicated.
curl -k -X DELETE \ -H "X-Vault-Token: $VAULT_TOKEN" \ -H "X-Vault-Namespace: $VAULT_DEDICATED_NAMESPACE" \ $VAULT_DEDICATED_ADDR/v1/kv/metadata/example_username_password
Notes of interest
As you construct your YAML documents, keep in mind the following considerations:
-
Polling interval: By default, the polling interval is set to 1 hour (
refreshInterval: 1h) and is the preferred value. You can change this value in the ExternalSecret template. The interval can be expressed in units ofs,m, orh. -
Vault Dedicated mount path: Vault Dedicated uses
kv/as the default mount path for the KV secrets engine, notsecret/. Ensure you specify the correct path in your SecretStore configuration. -
Vault Dedicated namespaces: Vault Dedicated uses Vault Enterprise namespaces. The default namespace is
admin. Ensure you specify the correct namespace in your SecretStore configuration. -
Authentication methods: This tutorial uses token authentication for simplicity. For production environments, consider using AppRole or Kubernetes authentication methods for better security.
-
TLS considerations: Vault Dedicated requires TLS connections. In production environments, ensure proper certificate validation is configured instead of using
skipTLSVerify.
Next steps
Great job! In this tutorial, you learned how to set up Vault Dedicated to securely populate application secrets to your Kubernetes cluster using External Secrets Operator. Check out more resources to help you get started with Vault Dedicated.
- Learn about Vault Secrets Operator, HashiCorp's official operator for Kubernetes integration.
- Review the External Secrets Operator Vault provider documentation.
- Explore Vault Dedicated documentation for more advanced features and configurations.
- Learn more about HashiCorp Vault.