A Kubernetes Secret is a resource that is used to store sensitive information, such as passwords, authentication tokens, API keys, and TLS certificates, within a cluster. Storing such information in plain text is problematic, which is why a Secret can be used instead of Kubernetes ConfigMaps.

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:

ConfigMapsSecrets
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>
Creating a generic Secret in Kubernetes with an imperative command

Creating a generic Secret in Kubernetes with an imperative command

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
Viewing a Kubernetes Secret in YAML output format

Viewing a Kubernetes Secret in YAML output format

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
Viewing the Secret values in clear text using the base64 utility

Viewing the Secret values in clear text using the base64 utility

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
Creating a YAML configuration file with the dry runclient option in Kubernetes

Creating a YAML configuration file with the dry runclient option in Kubernetes

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.

Modifying the Secret config file in a text editor

Modifying the Secret config file in a text editor

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
Creating a Kubernetes Secret with declarative configuration

Creating a Kubernetes Secret with declarative configuration

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
Create a Pod config file to use Kubernetes Secrets as environment variables

Create a Pod config file to use Kubernetes Secrets as environment variables

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
Create a Kubernetes Pod and check environment variables to view the Secrets

Create a Kubernetes Pod and check environment variables to view the Secrets

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
Create a Pod config file to use a Kubernetes Secret as a volume mount

Create a Pod config file to use a Kubernetes Secret as a volume mount

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
Create and describe a Pod to check the Kubernetes Secret mounted as a volume

Create and describe a Pod to check the Kubernetes Secret mounted as a volume

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.

Connecting the Pod to view the Secret mounted as a volume

Connecting the Pod to view the Secret mounted as a volume

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.

Marking a Kubernetes Secret as immutable

Marking a Kubernetes Secret as immutable

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
Deleting and recreating an immutable Secret in Kubernetes

Deleting and recreating an immutable Secret in Kubernetes

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.

0 Comments

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account