How to mount Secrets in our Deployments

Frequently we would need to use passwords and sensitive data in our pods for them to work, like for example passwords to the databases or keys for our APIs. However, what is the best way to implement those? Adding the values is never a good idea, since it will allow other people to see that sensitive data that maybe they shouldn’t see.

For this reason, Kubernetes introduces Secrets which are defined as the following:

A Secret is an object that contains a small amount of sensitive data such as a password, a token, or a key. Such information might otherwise be put in a Pod specification or in a container image. Using a Secret means that you don’t need to include confidential data in your application code.

Because Secrets can be created independently of the Pods that use them, there is less risk of the Secret (and its data) being exposed during the workflow of creating, viewing, and editing Pods. Kubernetes, and applications that run in your cluster, can also take additional precautions with Secrets, such as avoiding writing sensitive data to nonvolatile storage.

Secrets are similar to ConfigMaps but are specifically intended to hold confidential data.

https://kubernetes.io/docs/concepts/configuration/secret/

Now, in order to learn how to create them and mount them, let’s image the next case scenario:

Currently, the webapp-deployment is running with sensitive database environment variables directly embedded in the deployment YAML. To enhance security and protect the sensitive data, perform the following steps:

  • Create a Kubernetes Secret named db-secret with the below sensitive database environment variable values:
    • Key: DB_Host , Value: mysql-host
    • Key: DB_User , Value: root
    • Key: DB_Password , Value: dbpassword
  • Update the webapp-deployment to load the sensitive database environment variables from the newly created db-secret Secret.

Creating the Secret with it’s values

First of all, let’s start by creating the Secret. A secret is basically an object with a name that contains one or multiple keys with values. So we can have for this example a secret for the database data with three keys for the needed details with its values. We can create it using the kubectl create secret command. This allows to create secrets from string passed as arguments or even passed as files.

controlplane $ kubectl create secret generic db-secret / 
--from-literal=DB_Host=mysql-host / 
--from-literal=DB_User=root / 
--from-literal=DB_Password=dbpassword
secret/db-secret created

Once we have created it we can check the content.

controlplane $ k get secret
NAME        TYPE     DATA   AGE
db-secret   Opaque   3      10s
controlplane $ k describe secret db-secret
Name:         db-secret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
DB_Host:      10 bytes
DB_Password:  10 bytes
DB_User:      4 bytes

We can see now the secret, but it doesn’t display the content. As it’s stored in base64 we would need to retrieve first the values and then decode to make sure everything is right.

controlplane $ kubectl get secret db-secret -o jsonpath='{.data}'
{"DB_Host":"bXlzcWwtaG9zdA==","DB_Password":"ZGJwYXNzd29yZA==","DB_User":"cm9vdA=="}

controlplane $ echo 'ZGJwYXNzd29yZA==' | base64 --decode
dbpassword

After decoding it, we can see that the value for DB_Password matches with what we specified.

Now we can see it because we have permissions to get Secrets, but other entities using Service Accounts which doesn’t have permissions to get the Secrets won’t be able to see the content.

How to mount Secrets in our Deployments

As mentioned in the case scenario, we have already a working deployment but this one has the data hardcoded in the yaml file. For security reasons, it’s much a better choice to mount and use the Secret we just created instead of writing the values there since someone who hasn’t have permissions to see Secrets will be able to see our sensitive data.

controlplane $ k get deploy
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
webapp-deployment   1/1     1            1           14m
controlplane $ k describe deploy
Name:                   webapp-deployment
Namespace:              default
CreationTimestamp:      Mon, 24 Jun 2024 09:03:46 +0000
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=webapp
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=webapp
  Containers:
   webapp-container:
    Image:      nginx:latest
    Port:       80/TCP
    Host Port:  0/TCP
    Environment:
      DB_Host:      mysql-host
      DB_User:      root
      DB_Password:  dbpassword
    Mounts:         <none>
  Volumes:          <none>
  Node-Selectors:   <none>
  Tolerations:      <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   webapp-deployment-b4ddb5755 (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  15m   deployment-controller  Scaled up replica set webapp-deployment-b4ddb5755 to 1

Inside of the YAML we would see:

controlplane $ k edit deploy webapp-deployment
....
containers:
      - env:
        - name: DB_Host
          value: mysql-host
        - name: DB_User
          value: root
        - name: DB_Password
          value: dbpassword

To change it, instead of value we would need to use valueFrom: and secretKeyRef as following

containers:
      - env:
        - name: DB_Host
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: DB_Host
        - name: DB_User
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: DB_User
        - name: DB_Password
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: DB_Password

Now if we apply these changes we will see how our deployment starts using the new references instead of the hardcoded values, making our deployment much cleaner and safer.

controlplane $ k describe deploy
Name:                   webapp-deployment
Namespace:              default
CreationTimestamp:      Mon, 24 Jun 2024 09:03:46 +0000
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 2
Selector:               app=webapp
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=webapp
  Containers:
   webapp-container:
    Image:      nginx:latest
    Port:       80/TCP
    Host Port:  0/TCP
    Environment:
      DB_Host:      <set to the key 'DB_Host' in secret 'db-secret'>      Optional: false
      DB_User:      <set to the key 'DB_User' in secret 'db-secret'>      Optional: false
      DB_Password:  <set to the key 'DB_Password' in secret 'db-secret'>  Optional: false
    Mounts:         <none>
  Volumes:          <none>
  Node-Selectors:   <none>
  Tolerations:      <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  webapp-deployment-b4ddb5755 (0/0 replicas created)
NewReplicaSet:   webapp-deployment-6ff644ffb8 (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  23m   deployment-controller  Scaled up replica set webapp-deployment-b4ddb5755 to 1
  Normal  ScalingReplicaSet  89s   deployment-controller  Scaled up replica set webapp-deployment-6ff644ffb8 to 1
  Normal  ScalingReplicaSet  87s   deployment-controller  Scaled down replica set webapp-deployment-b4ddb5755 to 0 from 1

If you want to know more about Kubernetes, check all our posts in Kubernetes

Leave a Comment

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

Scroll to Top