- Kubernetes logs: Pod logs, container logs, Docker logs, kubelet logs, and master node logs - Mon, Sep 25 2023
- Kubernetes DaemonSets - Wed, Sep 6 2023
- Static Pods in Kubernetes - Fri, Sep 1 2023
ConfigMap vs. Secret
The concept of a Kubernetes Secret is similar to that of a ConfigMap, but there are some noticeable differences, as shown below:
ConfigMaps | Secrets |
ConfigMaps are designed to store nonsensitive information. | Secrets are designed to store sensitive information. |
ConfigMaps are suitable for keeping the configuration data outside of containerized applications. | Secrets help you avoid storing sensitive information such as passwords, tokens, and API keys outside of the containerized applications. |
ConfigMap stores the information in plain text key–value pairs. | Secrets store the information in base-64–encoded format to add a layer of obscurity but don't offer any encryption. |
Types of Secrets
Kubernetes supports various Secret types:
- Opaque or generic Secret—This is a general-purpose Secret that can be used to store any type of sensitive data in key–value pairs. This is the most common Secret type and is used to store items such as passwords, authentication tokens, or TLS certificates. To create this Secret type, you need to set the type field to Opaque.
- Docker config Secret—This is used to store credentials for private Docker registries. It allows Pods to pull images from private registries that require authentication. To create this Secret type, you need to set the type field to kubernetes.io/dockerconfigjson.
- TLS Secret—This type of Secret is used to store TLS certificates and private keys to enable secure communication within applications. To create this Secret type, you need to set the type field to kubernetes.io/tls.
- SSH Secret—This Secret type is used to store SSH private keys securely, which can be used to authenticate with services that support SSH authentication. To create this Secret type, you need to set the type field to kubernetes.io/ssh-auth.
- Service account token—Service account tokens are used to authenticate with the Kube API server. When you create a Kubernetes Pod, a service account token Secret is automatically created and associated with the Pod, allowing it to communicate securely with the Kube API server. To create this Secret type, you need to set the type field to kubernetes.io/service-account-token.
These are the built-in Secret types, but you can easily define your own Secret type by using a nonempty string as the type value. If you leave the Secret type empty or use generic as the type value, an Opaque Secret is created by default.
Create a Kubernetes Secret
Like a ConfigMap, you can create a Kubernetes Secret with either an imperative command or a declarative approach.
Imperative approach
To create a Kubernetes Secret with an imperative approach, use the following command:
kubectl create secret <secret-type> <secret-name> <data-source>
You can see the command is similar to that of a ConfigMap with a new <secret-type> option, where you need to specify the type of secret you want to create. In the above screenshot, I created a generic (or Opaque) secret with a db-secret name. The <data-source> can be a file, directory, or literal string. For more information, see my previous post on ConfigMaps.
To view the created secret, use the following command:
kubectl get secret db-secret -o yaml
The -o (or --output) option lets you view the Secret object in a specified format (YAML, in this case). If you take a look at the data section, there are two keys (username and password) with their values encoded in base-64 format. You can easily decode the values using the base64 utility, as shown below:
echo -n ‘encoded-value’ | base64 --decode
Declarative approach
Now, let's see how to create a Secret with declarative configuration. You can use the --dry-run=client option to create a definition file in YAML format, as shown below.
kubectl create secret generic app-secret \ --from-literal password=P@ssw0rd \ --dry-run=client \ --output yaml > app-secret.yaml
Here, I only added one key–value pair (password). Once the definition file is created, open it in a text editor to make further changes.
Here, I added one more key–value pair (username) under a new stringData section. The data section requires base-64–encoded values, but you can specify the values in plain text under the stringData section. In short, the stringData section is there for your convenience, so you don't have to manually encode the values.
When a Secret is created, the key–value pairs specified under the stringData section are automatically merged into the data section, and the corresponding plain-text values are base-64–encoded. You could specify your key–value pairs under either or both, depending on your requirements, but the key names should not overlap. If overlapping keys are specified, the stringData section takes precedence.
Let's create a Secret from this file using the kubectl create command.
kubectl create -f app-secret.yaml kubectl get secret app-secret -o yaml
You can see that the username that I mentioned under the stringData section is automatically merged into the data section, and the value is encoded.
Consume Secrets in a Pod
Secrets and ConfigMaps are consumed by Pods or other Kubernetes in the same way as discussed in the previous post.
Environment variables
To use Secrets as environment variables in a Pod, create a db-secret.yaml file.
apiVersion: v1 data: password: UEBzc3cwcmQ= username: Ymx1ZWFkbWlu kind: Secret metadata: name: db-secret
Now, create a Secret using this file.
kubectl apply -f db-secret.yaml
Next, we will create a webapp-pod.yaml Pod configuration file.
apiVersion: v1 kind: Pod metadata: name: webapp-pod spec: containers: - name: webapp-container image: nginx env: - name: USERNAME valueFrom: secretKeyRef: name: db-secret key: username - name: PASSWORD valueFrom: secretKeyRef: name: db-secret key: password
Here, I did not use the entire Secret directly as an environment variable. Instead, I defined two variables (USERNAME and PASSWORD) and used the secretKeyRef field to reference the key from the Secret object with each variable name. Ultimately, the USERNAME variable is mapped to the username key, and the PASSWORD variable is mapped to the Secret password key.
Let's now create the Pod and see if we can find the Secret values in the Pod's environment variables.
kubectl apply -f webapp-pod.yaml kubectl exec webapp-pod -it -- printenv
The above screenshot shows that both variables now have access to the values stored in the Secret object itself. The container can now use these values to authenticate with the target database server.
Volume mounts
Another way of consuming a Secret within a Pod is to mount it as a volume. To do so, make sure you have the app-secret.yaml file created in the previous section.
apiVersion: v1 data: password: UEBzc3cwcmQ= stringData: username: redadmin kind: Secret metadata: name: app-secret
Create the secret using this file.
kubectl apply -f app-secret.yaml
Now, create a new Pod configuration file named webapp-pod-2.yaml.
apiVersion: v1 kind: Pod metadata: name: webapp-pod-2 spec: containers: - name: webapp-container image: nginx volumeMounts: - name: app-secret-volume mountPath: /etc/secrets/app-secret volumes: - name: app-secret-volume secret: secretName: app-secret
Here, I defined a Pod with a container named webapp-pod-2, and a volume that is sourced from our Secret named app-secret. Then, I used the mountPath keyword to mount the volume at /etc/secrets/app-secret inside the container. Ultimately, the app-secret is mounted as a volume named app-secret-volume. Let's create the Pod.
kubectl apply -f webapp-pod-2.yaml kubectl describe pod webapp-pod-2
You can see that the Secret is successfully mounted as a volume under the /etc/secrets/app-secret directory. If you connect to the Pod, you will see two symlinks that correspond to each key of the Secret, as shown in the screenshot below.
That's it. Your application can now read the username and password from the mounted volume and use it.
Immutability
Kubernetes Secrets (and ConfigMaps) can be set as immutable by specifying an immutable: true field in the configuration file.
Once a Secret (or ConfigMap) is marked immutable, it cannot be modified. This is particularly useful when you want to prevent junior admins or developers from accidentally modifying a Secret or ConfigMap, which could cause potential downtime in business-critical applications. The only way to modify an immutable secret is to delete and recreate it, as shown in the screenshot below.
kubectl delete secret app-secret kubectl apply -f app-secret.yaml
Conclusion
I hope this post helps you get started with Kubernetes Secrets. Remember that a Kubernetes Secret does not encrypt sensitive information. The information is base-64–encoded, which means that anyone with sufficient access to the Kube API server or etcd database can easily decode and read the information in clear text. The etcd is a central key–value store where all Kubernetes cluster data is stored in an unencrypted format by default. However, you can manually enable encryption at rest, so the Secret values are not stored in clear text in etcd.
Subscribe to 4sysops newsletter!
That being said, you will see it mentioned in a lot of blogs that Kubernetes Secret is a secure way of storing sensitive information. In my opinion, Kubernetes Secrets are not secure by design because encoding merely gives a little obscurity from accidental disclosure, but your sensitive information is still vulnerable due to a lack of encryption. Certain best practices (e.g., role-based access control and encryption at rest) make Kubernetes Secrets secure when implemented properly. Therefore, I would strongly recommend not pushing your secret configuration files into source code repositories like GitHub.