Using Secrets within Kubernetes

Secrets are used in a similar way to ConfigMaps but have the advantage of not being stored in clear text. For a reminder of how to use ConfigMaps click here

We'll start with some existing secrets that were created in a previous exercise where a secret called mysecret was created.

We'll use this object again, which has a username and password defined. This can be confirmed by running kubectl get secrets mysecret

salterje@k8s-master:~$ kubectl get secrets mysecret
NAME       TYPE     DATA   AGE
mysecret   Opaque   2      21h

Confirmation of presence of Secret Object

The actual YAML of the secret can be found by using kubectl get secrets mysecret -o yaml


salterje@k8s-master:~$ kubectl get secrets mysecret -o yaml
apiVersion: v1
data:
  password.txt: U3VwZXJTZWNyZXRQYXNzd29yZDEyMwo=
  username.txt: YWRtaW4K
kind: Secret
metadata:
  creationTimestamp: "2020-07-12T15:13:46Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:password.txt: {}
        f:username.txt: {}
      f:type: {}
    manager: kubectl
    operation: Update
    time: "2020-07-12T15:13:46Z"
  name: mysecret
  namespace: default
  resourceVersion: "288441"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: b7496c8b-3d58-490c-8156-6c124c0d6178
type: Opaque


Confirmation of YAML Contents of Secret

It can be seen that the actual values of the data within the secret are encoded as Base64. We shall see what happens to these values when they are used within a container.

Importing Secret as an Environmental Value

The first deployment we will build is an Ubuntu container that will have two environment variables USERNAME and PASSWORD populated from the secret object called mysecret.

This is done by populating the env section of the manifest file to take it's values from those contained within the secret.

The container within the POD will log the values of USERNAME and PASSWORD every 30 seconds which will prevent it being killed off when it has completed the import.

kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: mysecret-env-deployment
  name: mysecret-env-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysecret-env-deployment
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: mysecret-env-deployment
    spec:
      containers:
      - image: ubuntu:latest
        name: mysecret-ubuntu
        env:  # Setting the env values within the mysecret-ubuntu container
        - name: USERNAME   # This will be the first env variable
          valueFrom:
            secretKeyRef:  # This defines it will be taken from a secret
              name: mysecret  # The name of the secret object
              key: username.txt  # The name of the string contained in the secret
        - name: PASSWORD  # This will be the 2nd env variable
          valueFrom:
            secretKeyRef:  # Again the value is taken from a secret object
              name: mysecret  # The name of the secret object
              key: password.txt  # The name of the string contained in the secret
        command: ["/bin/bash"]
        args: ["-c", "while true; do echo Username:$USERNAME   Password:$PASSWORD; sleep 30; done"]
      restartPolicy: Always




Adding Environmental Import to Container Spec

The POD is created by running the deployment which will create a single POD instance.

salterje@k8s-master:~/YAML/secrets$ kubectl apply -f mysecret-env-deployment.yaml 
deployment.apps/mysecret-env-deployment created

salterje@k8s-master:~/YAML/secrets$ kubectl get deployments.apps mysecret-env-deployment 

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
mysecret-env-deployment   1/1     1            1           10s

salterje@k8s-master:~/YAML/secrets$ kubectl get pods

NAME                                       READY   STATUS    RESTARTS   AGE
mysecret-env-deployment-7c7b66bb47-vtljk   1/1     Running   0          89s

Creation of Deployment

The confirmation of the succesful import of the secrets can be confirmed by checking the logs of the container and by connecting to the container with an interactive terminal session.

salterje@k8s-master:~/YAML/secrets$ kubectl logs mysecret-env-deployment-7c7b66bb47-vtljk
Username:admin Password:SuperSecretPassword123


salterje@k8s-master:~/YAML/secrets$ kubectl exec -it mysecret-env-deployment-7c7b66bb47-vtljk -- bash

root@mysecret-env-deployment-7c7b66bb47-vtljk:/# env | grep USERNAME
USERNAME=admin

root@mysecret-env-deployment-7c7b66bb47-vtljk:/# env | grep PASSWORD
PASSWORD=SuperSecretPassword123

root@mysecret-env-deployment-7c7b66bb47-vtljk:/#

Confirmation of Environmental Variable Import

It can be seen that while the variables are stored in Base64 format they are actually taken back to their true values when imported into the containers.


Mounting a Secret within a Container

Another use for secrets is to import and mount them as files within the container.

In this example we will take the same secret and mount it at /etc/mysecret-vol. This will create two files called username.txt and password.txt

The first step involved is to create the necessary volumeMount within the container spec and link it to a volume, which is defined as a secret.

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: mysecret-mount-deployment
  name: mysecret-mount-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysecret-mount-deployment
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: mysecret-mount-deployment
    spec:
      containers:
      - image: ubuntu:latest
        name: mysecret-ubuntu
        volumeMounts:
        - name: mysecret-vol  # Name of the volume to be mounted
          mountPath: "/etc/mysecret-vol"  # Location to be mounted within container, this will be overwrite anything in the container at same location
        command: ["/bin/bash"]
        args: ["-c", "while true; do echo hello; sleep 30; done"]
      restartPolicy: Always
      volumes:
      - name: mysecret-vol  # Name of volume, must match name in volumeMount
        secret:                 # Defines volume as a secret
          secretName: mysecret  # Name of secret object



Creation of Manifest File to Mount Secret

The confirmation of the success can be carried out by describing the created POD and again to log onto the running container to verfiy the internal file structure.

salterje@k8s-master:~/YAML/secrets$ kubectl get pods
NAME                                        READY   STATUS    RESTARTS   AGE
mysecret-env-deployment-7c7b66bb47-vtljk    1/1     Running   0          52m
mysecret-mount-deployment-ff9f8668d-m6wrk   1/1     Running   0          11m

Verify POD has been created

The confirmation that a secret has been mounted can be done by describing the deployment

kubectl describe deployment mysecret-mount-deployment

Name:                   mysecret-mount-deployment
Namespace:              default
CreationTimestamp:      Mon, 13 Jul 2020 16:32:07 +0000
Labels:                 app=mysecret-mount-deployment
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=mysecret-mount-deployment
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=mysecret-mount-deployment
  Containers:
   mysecret-ubuntu:
    Image:      ubuntu:latest
    Port:       <none>
    Host Port:  <none>
    Command:
      /bin/bash
    Args:
      -c
      while true; do echo hello; sleep 30; done
    Environment:  <none>
    Mounts:
      /etc/mysecret-vol from mysecret-vol (rw)
  Volumes:
   mysecret-vol:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  mysecret
    Optional:    false
    Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   mysecret-mount-deployment-ff9f8668d (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  14m   deployment-controller  Scaled up replica set mysecret-mount-deployment-ff9f8668d to 1


Describing Deployment to Confirm Secret has been Mounted

To confirm the secrets have been mounted an interactive terminal can be used onto the container. Before this can be done the actual POD needs to be found using kubectl get pods

kubectl get pods
NAME                                        READY   STATUS    RESTARTS   AGE
mysecret-env-deployment-7c7b66bb47-vtljk    1/1     Running   1          24h
mysecret-mount-deployment-ff9f8668d-m6wrk   1/1     Running   1          23h



Finding the Correct POD

The interactive terminal can be used to verify the secret has been mounted correctly and to look at the actual contents.

salterje@k8s-master:~/YAML/configmaps$ kubectl exec -it mysecret-mount-deployment-ff9f8668d-m6wrk -- ls -lh /etc/mysecret-vol
total 0

lrwxrwxrwx 1 root root 19 Jul 14 15:09 password.txt -> ..data/password.txt
lrwxrwxrwx 1 root root 19 Jul 14 15:09 username.txt -> ..data/username.txt

salterje@k8s-master:~/YAML/configmaps$ kubectl exec -it mysecret-mount-deployment-ff9f8668d-m6wrk -- cat /etc/mysecret-vol/password.txt
SuperSecretPassword123

salterje@k8s-master:~/YAML/configmaps$ kubectl exec -it mysecret-mount-deployment-ff9f8668d-m6wrk -- cat /etc/mysecret-vol/username.txt
admin



Confirming the files have been mounted in POD


Conclusions

This post shows two possible uses of injecting values from a secret object into containers. The mechanism is very similar to using a ConfigMap but offers more security.

Care must still be taken on ensuring any manifest files that are used when working with secrets are suitably protected by suitable RBAC policies. It is relatively easy for the data to be decoded when secrets are able to be viewed.