Secure secrets for apps that run in your Kubernetes cluster
In this tutorial, you learn how to use IBM Cloud® Secrets Manager to manage secrets for applications that run your IBM Cloud Kubernetes Service cluster by using the External Secrets Operator open-source tool.
Alternatively, you can use the Kubernetes Service CLI plug-in to manage TLS and non-TLS secrets. To learn more about this approach, see Setting up Kubernetes Ingress.
You're a developer in an organization, and your team is using Kubernetes Service to deploy containerized apps and services on IBM Cloud. In your current flow, you use Kubernetes Secrets to store the sensitive data, such as passwords and API keys, that are used by the apps and services that run in your cluster. To have more control over your application secrets, you want the ability to store your cluster secrets in an external secrets management service, where you can encrypt them at rest, monitor their activity, and easily manage them.
With Secrets Manager, 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 Secrets Manager at run time. When it's time to rotate the secret, you can do so from Secrets Manager. For example, consider the following scenario:
- As a developer, you use Secrets Manager to store a secret for an application that you want to deploy in a Kubernetes cluster.
- Secrets Manager provides an ID for the secret. You include the ID in the
ExternalSecrets
configuration file for your app and you apply the configuration to the cluster. - The External Secrets controller fetches the
ExternalSecrets
objects in the configuration file that you defined by using the Kubernetes API. - At application run time, the controller retrieves the secret data from Secrets Manager, and converts the
ExternalSecrets
objects 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:
-
jq
helps you slice and filter JSON data. You usejq
in this tutorial to grab and use stored environment variables.
Set up your environment
To work with Secrets Manager and Kubernetes Service, you need to create a cluster and a Secrets Manager instance in your IBM Cloud account. You also need to configure permissions so that you can run operations against both services.
In this step, you set up an access environment by creating a service ID and an IBM Cloud API key. At the end of the tutorial, you can easily remove your resources if you no longer need them. Alternatively, you can use a trusted profile to authorize the External Secrets operator.
Create a service ID and API key
Start by creating the account credentials that you need to be able to run operations against Secrets Manager and Kubernetes Service.
-
From the command line, log in to IBM Cloud through the IBM Cloud CLI.
ibmcloud login
If the login fails, run the
ibmcloud login --sso
command to try again. The--sso
parameter 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. -
Create a service ID and set it as an environment variable.
export SERVICE_ID=`ibmcloud iam service-id-create kubernetes-secrets-tutorial --description "A service ID for testing ESO integration" --output json | jq -r ".id"`; echo $SERVICE_ID
-
Assign the service ID permissions to read secrets from Secrets Manager.
ibmcloud iam service-policy-create $SERVICE_ID --roles "SecretsReader" --service-name secrets-manager
By assigning SecretsReader service access, the External Secrets controller has the correct level of access to read secrets from Secrets Manager and populate them in a Kubernetes cluster.
-
Create an IBM Cloud API key for your service ID.
export IBM_CLOUD_API_KEY=`ibmcloud iam service-api-key-create kubernetes-secrets-tutorial $SERVICE_ID --description "An API key for testing ESO integration." --output json | jq -r ".apikey"`
You use this API key later to configure Secrets Manager for your cluster deployment.
Create a Kubernetes cluster and Secrets Manager instance
Create a Kubernetes cluster and an instance of Secrets Manager in your IBM Cloud account.
You can create one free Kubernetes cluster and Secrets Manager service instance per IBM Cloud account. If you already have both resources in your account, you can use your existing free cluster and Secrets Manager instance to complete the tutorial.
-
From the command line, select the account, region, and resource group where you want to create a Secrets Manager service instance.
In this tutorial, you interact with the Dallas region. If you're logged in to a different region, be sure to set Dallas as your target region by running the following command.
ibmcloud target -r us-south -g default
-
Create a Kubernetes cluster.
ibmcloud ks cluster create classic --zone dal10 --flavor free --name my-test-cluster
-
Create a Secrets Manager instance.
ibmcloud resource service-instance-create my-secrets-manager secrets-manager trial us-south
Provisioning for both Secrets Manager and your Kubernetes cluster takes 5 - 15 minutes to complete.
-
Before you continue to the next step, verify that your cluster and Secrets Manager instance are provisioned successfully.
-
Verify that the deployment of your worker node is complete.
ibmcloud ks worker ls --cluster my-test-cluster
When your worker node is finished provisioning, the status changes to Ready.
ID Public IP Private IP Flavor State Status Zone Version kube-c39pf4ld0m87o3fv1utg-mytestclust-default-000000dd 169.xx.xx.xxx 10.xxx.xx.xxx free normal Ready mex01 1.20.7_1543
-
Next, verify that your Secrets Manager instance provisioned successfully.
ibmcloud resource service-instance my-secrets-manager
When the instance is finished provisioning, the state changes to Active.
Name: my-secrets-manager ID: crn:v1:bluemix:public:secrets-manager:us-south:a/f047b55a3362ac06afad8a3f2f5586ea:fe06948b-0c6b-4183-8d4b-e6c1d38ff65f:: GUID: fe06948b-0c6b-4183-8d4b-e6c1d38ff65f Location: us-south Service Name: secrets-manager Service Plan Name: trial Resource Group Name: default State: active Type: service_instance Sub Type: Created at: 2021-01-06T17:11:32Z Created by: zara@example.com Updated at: 2021-03-31T02:33:26Z
-
-
Set the context for your Kubernetes cluster in the CLI.
ibmcloud ks cluster config --cluster my-test-cluster
-
Verify that
kubectl
commands run properly and that the Kubernetes context is set to your cluster.kubectl config current-context
Example output:
my-test-cluster/<your_cluster_ID>
Create a trusted profile
A trusted profile enables the External Secrets operator to read from Secrets Manager, without having to create a service ID or manage an API key.
-
Get the CRNs for your Secrets Manager instance and Kubernetes cluster.
CLUSTER_CRN=$(ibmcloud ks cluster get --cluster my-test-cluster --output json | jq -r '.crn') SECRETS_MANAGER_CRN=$(ibmcloud resource service-instance my-secrets-manager --output JSON | jq -r '.[0].crn')
-
Create the profile.
ibmcloud iam trusted-profile-create 'External Secrets'
-
Authorize the Kubernetes cluster to use the trusted profile.
ibmcloud iam trusted-profile-rule-create 'External Secrets' --name kubernetes --type Profile-CR --conditions claim:namespace,operator:EQUALS,value:external-secrets --conditions claim:name,operator:EQUALS,value:external-secrets --conditions claim:crn,operator:EQUALS,value:$CLUSTER_CRN --cr-type IKS_SA
-
Create an access policy that allows the trusted profile to read secrets from your Secrets Manager instance.
ibmcloud iam trusted-profile-policy-create 'External Secrets' --roles SecretsReader --service-instance $SECRETS_MANAGER_CRN --service-name secrets-manager
Prepare your Secrets Manager instance
Finally, configure your Secrets Manager instance to start working with secrets.
-
From the command line, verify that you can access the Secrets Manager CLI plug-in.
ibmcloud secrets-manager --help
Don't have the plug-in yet? To install the Secrets Manager CLI plug-in, run
ibmcloud plugin install secrets-manager
. -
Export an environment variable with your unique Secrets Manager API endpoint URL.
export SECRETS_MANAGER_URL=`ibmcloud resource service-instance my-secrets-manager --output json | jq -r '.[].dashboard_url | .[0:-3]'`; echo $SECRETS_MANAGER_URL
-
Create a secret group for your instance.
Secret groups are a way to organize and control who on your team has access to specific secrets in your instance. To create a secret group from the IBM Cloud CLI, you use the
ibmcloud secrets-manager secret-group-create
command. Run the following command to create a secret group and store its ID as an environment variable.export SECRET_GROUP_ID=`ibmcloud secrets-manager secret-group-create --name my-test-secret-group --description "Read and write to my test app" --output json --service-url $SECRETS_MANAGER_URL | jq -r '.id'`; echo $SECRET_GROUP_ID
Using a Windows™ command prompt (
cmd.exe
) or PowerShell? If you encounter errors with passing JSON content on the command line, you might need to adjust the strings for quotation-escaping requirements that are specific to your operating system. For more information, see Using quotation marks with strings in the IBM Cloud CLI.Success! Now you can store the secret in Secrets Manager that you want to populate in your Kubernetes cluster. Continue to the next step.
Create a secret in Secrets Manager
Secrets are application-specific and can vary based on the individual app or service that requires them. A secret might consist of a username, password, API key, or any other type of credential.
Secrets Manager supports various types of secrets that you can create and manage in the service. For example, if you need to manage an API key for an app that is protected by IBM Cloud IAM authentication, you can create an IAM credential. Or, if you need to manage a secret that can hold any type of structured or unstructured data, you can create an arbitrary secret.
In this tutorial, you create a username and password as an example. To create a secret from the IBM Cloud CLI, you use the ibmcloud secrets-manager secret-create
command. Run the following command to create the secret and store its ID as an environment variable.
export SECRET_ID=`ibmcloud secrets-manager secret-create --secret-type=username_password --secret-name example_username_password --username-password-username user123 --username-password-password cloudy-rainy-coffee-book --secret-labels "my-test-cluster, tutorial" --secret-group-id $SECRET_GROUP_ID --output json $SECRETS_MANAGER_URL | jq -r '.id'`; echo $SECRET_ID
Be sure to update instance_id
and region
to yours.
The output shows the ID of your newly created secret. For example:
e0246cea-d668-aba7-eef2-58ca11ad3707
Set up External Secrets Operator
Now that you have a secret for your application, you can set up the External Secrets Operator tool for your cluster. This package configures the connection between
Secrets Manager and your cluster by creating ExternalSecrets
objects that are converted to Kubernetes secrets for your application.
External Secrets Operator is an open source tool that is not maintained by IBM. For more information about this tool or to troubleshoot any issues, refer to the project documentation.
Configure External Secrets Operator for your cluster
Kubernetes
First, add external-secrets
resources to your cluster by installing the official Helm chart. For more installation options, check out the getting started guide.
-
Run the following command to install External Secrets Operator helm repository:
helm repo add external-secrets https://charts.external-secrets.io
-
Configure authentication between External Secrets Operator and Secrets Manager.
If you're using a service ID to authenticate:
kubectl -n default create secret generic secret-api-key --from-literal=apikey=$IBM_CLOUD_API_KEY helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace --set installCRDs=true
If you're using a trusted profile to authenticate:
echo ' installCRDs: true extraVolumes: - name: sa-token projected: defaultMode: 420 sources: - serviceAccountToken: path: sa-token expirationSeconds: 3600 audience: iam extraVolumeMounts: - mountPath: /var/run/secrets/tokens name: sa-token webhook: extraVolumes: - name: sa-token projected: defaultMode: 420 sources: - serviceAccountToken: path: sa-token expirationSeconds: 3600 audience: iam extraVolumeMounts: - mountPath: /var/run/secrets/tokens name: sa-token' >values.yml helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace -f values.yml
OpenShift
-
Install the External Secrets Operator by creating the following resources:
echo ' apiVersion: v1 kind: Namespace metadata: name: external-secrets-operator --- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: name: external-secrets-operator namespace: external-secrets-operator spec: targetNamespaces: - external-secrets-operator --- apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: external-secrets-operator namespace: external-secrets-operator spec: channel: stable installPlanApproval: Automatic name: external-secrets-operator source: community-operators sourceNamespace: openshift-marketplace ' | oc create -f-
-
Configure authentication between External Secrets Operator and Secrets Manager.
If you're using a service ID to authenticate:
echo " apiVersion: operator.external-secrets.io/v1alpha1 kind: OperatorConfig metadata: name: cluster namespace: external-secrets-operator spec: {} --- apiVersion: v1 kind: Secret metadata: name: secret-api-key namespace: default type: Opaque stringData: apikey: $IBM_CLOUD_API_KEY " | oc create -f-
If you're using a trusted profile to authenticate:
echo ' apiVersion: operator.external-secrets.io/v1alpha1 kind: OperatorConfig metadata: name: cluster namespace: external-secrets-operator spec: extraVolumeMounts: - mountPath: /var/run/secrets/tokens name: sa-token extraVolumes: - name: sa-token projected: defaultMode: 420 sources: - serviceAccountToken: audience: iam expirationSeconds: 3600 path: sa-token webhook: extraVolumeMounts: - mountPath: /var/run/secrets/tokens name: sa-token extraVolumes: - name: sa-token projected: defaultMode: 420 sources: - serviceAccountToken: audience: iam expirationSeconds: 3600 path: sa-token ' | oc create -f-
Update your app configuration
After you install External Secrets Operator in your cluster, you can define Secrets Manager as the secrets backend for your application. Start by creating a configuration file that targets the secret in Secrets Manager that you want to use.
-
In the root directory of your application, create an
external-secrets-example.yml
file.touch external-secrets-example.yml
-
Modify the file to include information about the secret that you want to fetch from your Secrets Manager instance.
apiVersion: external-secrets.io/v1beta1 kind: SecretStore metadata: name: ibmcloud-secrets-manager-example spec: provider: ibm: serviceUrl: <endpoint_url> auth: secretRef: secretApiKeySecretRef: name: secret-api-key key: apikey --- apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: ibmcloud-secrets-manager-example spec: secretStoreRef: name: ibmcloud-secrets-manager-example kind: SecretStore target: name: ibmcloud-secrets-manager-example data: - secretKey: username remoteRef: property: username key: username_password/<SECRET_ID> - secretKey: password remoteRef: property: password key: username_password/<SECRET_ID>
There are two modes you choose to work by - Secret ID or secret Name. If choosing secret name the format changes from
secret_type/secret_id
tosecret_group/secret_type/secret_name
.Replace
<endpoint_url>
with the Secrets Manager endpoint URL that you retrieved earlier. Replace<SECRET_ID>
with the unique ID of the secret that you created in the previous step.If you're using a trusted profile to authenticate, replace the
auth
block with the following snippet.auth: containerAuth: profile: "External Secrets" iamEndpoint: https://iam.cloud.ibm.com tokenLocation: /var/run/secrets/tokens/sa-token
-
Apply the configuration to your cluster.
kubectl apply -f external-secrets-example.yml
-
Verify that the External Secrets Operator is able to fetch the secret that is stored in your Secrets Manager instance.
kubectl get secret ibmcloud-secrets-manager-example -o json | jq '.data | map_values(@base64d)'
Example output:
{ "password": "cloudy-rainy-coffee-book", "username": "user123" }
Success! You're now able to fetch the secret data that is stored in your Secrets Manager instance. Continue to the next step.
Deploy an app to the cluster
Finally, you can deploy an application in your cluster that uses the Secrets Manager secret that you defined in the external-secret-example.yml
file. At application run time, the secret data that is fetched from Secrets Manager
is converted to a Kubernetes secret that can be used by your cluster.
Looking for examples on how to deploy an app? Check out Deploying Kubernetes-native apps in clusters to find out more about deploying a single instance of an app.
(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 my-test-cluster
-
Delete your test Secrets Manager instance.
ibmcloud resource service-instance-delete my-secrets-manager
-
Delete your authorization.
If you're using a service ID.
ibmcloud iam service-id-delete $SERVICE_ID
If you're working with a trusted profile.
ibmcloud iam trusted-profile-delete 'External Secrets'
Notes of interest
As you construct your YAML document, keep in mind that each key in the data section is polled periodically by using REST from the Secrets Manager instance. Be aware that:
- By default, the polling interval is set to 1 hour and is the preferred value to set. You can change this value by using
spec.refreshInterval
in the External Secrets template. The interval can be expressed in units ofs
,m
, orh
. - If you set the YAML to fetch a Secrets Manager secret by name rather than ID (
keyByName: true
), an additional call is made by ESO to fetch the relevant secret ID. For more information, see the External Secrets documentation.
Next steps
Great job! In this tutorial, you learned how to set up Secrets Manager to securely populate application secrets to your cluster. Check out more resources to help you get started with Secrets Manager.
- Review the secret types in Secrets Manager that are supported by External Secrets Operator.
- Learn more about the Secrets Manager API.