kubernetes
Environments (ConfigMap) and Secrets

Environments (ConfigMap) and Secrets

We have 3 ways to set environment variables and secrets;

  • key-value pair format
  • ConfigMap
  • Secrets

key-value pair format

pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: my-ubuntu
spec:
  container:
    - name: my-ubuntu
      image: ubuntu
      env:
        - name: URL
          value: https://karchunt.com
        - name: APP_NAME
          value: karchunt-ubuntu

ConfigMap

The concept of ConfigMap is actually the same as key-value pair format, just it stores the configuration data in a Kubernetes API object.

There are two ways of creating ConfigMap;

  • Imperative
  • Declarative
# get configmap
kubectl get configmap

Ways of creating ConfigMap

Imperative

# create configmap from key-valuye pairs
kubectl create configmap <config-name> --from-literal=<key>=<value>
kubectl create configmap api-config --from-literal=port=8080
 
# create configmap from file path
kubectl create configmap <config-name> --from-file=<path-to-file>
kubectl create configmap api-config --from-file=.env

When you create configmap from file path, make sure each line adhere to the format like this <name>=<value>

.env
port=8080
env=prod

Declarative

api-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  port: 8080
  env: prod

Apply entire ConfigMap to Pod

Make sure the configMapRef name is equal to ConfigMap file metadata name.

api-party.yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-party
spec:
  containers:
    - name: api-party
      image: api-party:0.1.0
      ports:
        - containerPort: 8080
      envFrom:
        - configMapRef:
            name: api-config # ConfigMap file metadata name

Apply only 1 value from ConfigMap to Pod

We can also apply only 1 value from ConfigMap to Pod.

api-party.yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-party
spec:
  containers:
    - name: api-party
      image: api-party:0.1.0
      ports:
        - containerPort: 8080
      env:
        - name: port
          valueFrom:
            configMapKeyRef:
              name: api-config # ConfigMap file metadata name
              key: port # ConfigMap file under data section

Apply ConfigMap to volume

ℹ️

A container using a ConfigMap as a subPath volume will not receive ConfigMap updates.

In some cases, you might want to create files with the contents during runtime, but you can actually do that using ConfigMap.

special-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: model-config
data:
  config.py: |
    config = {
      'url': 'http://localhost:8080'
    }
  special_file.txt: specialData
api-party.yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-party
spec:
  containers:
    - name: api-party
      image: api-party:0.1.0
      ports:
        - containerPort: 8080
      volumeMounts:
        - name: config-volume
          mountPath: /app/src
          # subPath: src
  volumes:
    - name: config-volume
      configMap:
        name: model-config

In this case, two files called will be created in /app/src, so these files will present at this path:

  • /app/src/config.py
  • /app/src/special_file.txt

Secrets

ℹ️
Remember, the way of mapping secrets and how it works are very similar to ConfigMap. Refer to link to learn understand more about Kubernetes secret (opens in a new tab). Addtional info:
  • Secrets only encoded and not encrypted in etcd. Therefore, we should enable encryption at kube-apiserver (REST)
  • Consider external secrets providers like HashiCorp Vault and AWS Secrets Manager
  • Don't check in secret objects with data to SCM like GitHub
  • Remember to setup RBAC (least-privileged access) to secrets as anyone can access those Kubernetes objects within the same namespace

Secrets are used to store confidential information like a password, key, or token.

Additional information on how Kubernetes manage secrets:

  • A secret will be only sent to a node if a pod requires it
  • When we mount secrets into pods, the kubelet will store a copy of the secret into a tmpfs (aka a disk in RAM) so that the confidential data is not written to disk storage.
  • The kubelet will delete its local copy of the secret when the pod depends on the secret is deleted.
kubectl get secrets
Built-in TypeUsage
Opaquearbitrary user-defined data
kubernetes.io/service-account-tokenServiceAccount token
kubernetes.io/dockerconfigjsonserialized ~/.docker/config.json file
kubernetes.io/basic-authcredentials for basic authentication
kubernetes.io/ssh-authcredentials for SSH authentication
kubernetes.io/tlsdata for a TLS client or server
bootstrap.kubernetes.io/tokenbootstrap token data

Ways of creating Secrets

Imperative

When you use imperative to create secret, you no need to encode your data, as the command will automatically help you to encode.

# create secret generic (opaque) from key-value pairs
kubectl create secret generic <secret-name> --from-literal=<key>=<value>
kubectl create secret generic api-secret --from-literal=token=yourtoken
 
# create secret generic from file path
kubectl create secret generic <secret-name> --from-file=<path-to-file>
kubectl create secret generic api-secret --from-file=.env
 
# create container registry secret
kubectl create secret docker-registry secret-harbor \
  --docker-email=karchun@gmail.com \
  --docker-username=karchun \
  --docker-password=mypassword \
  --docker-server=harbor.registry.com
 
# retrieve and decode the data for registry secret
kubectl get secret secret-harbor -o jsonpath='{.data.*}' | base64 -d
 
# Create TLS secret
# the certificate for --cert must be .PEM encoded
kubectl create secret tls <secret-name> --cert=<path-to-cert-file> --key=<path-to-key-file>
kubectl create secret tls tls-domain --cert="path.crt" --key="key.key"

When you create secret from file path, make sure each line adhere to the format like this <name>=<value>

.env
token=yourtoken

Declarative

Make sure your secrets store with base64 format

# encode
echo -n '<data>' | base64
echo -n 'password' | base64
 
# decode
echo -n 'bXlzcWw=' | base64 --decode
Opaque
api-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: api-secret
data:
  token: bXlzcWw=
Docker config
docker-config.yaml
apiVersion: v1
kind: Secret
metadata:
  name: secret-dockercfg
type: kubernetes.io/dockercfg
data:
  .dockerconfigjson: |
    eqJhdXRocyd6eyJodHRwczoaL2V4YW1wbGUvdjEvcjP7ImF1dGgiOiJvcGVuc2VzYW1lIn19fQo=    
Basic authentication

The data or stringData (clear text content) field of the Secret must contain one of the following two keys:

  • username
  • password
basic-authentication.yaml
apiVersion: v1
kind: Secret
metadata:
  name: secret-basic-auth
type: kubernetes.io/basic-auth
data:
  username: bXlzcWw=
  password: bXlzcWw=
SSH authentication

Mandatory field: ssh-privatekey

ssh-authentication.yaml
apiVersion: v1
kind: Secret
metadata:
  name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
  ssh-privatekey: |
    abcoAI812hcnaik
TLS

Mandatory fields: tls.crt and tls.key

tls.yaml
apiVersion: v1
kind: Secret
metadata:
  name: secret-tls
type: kubernetes.io/tls
data:
  tls.crt: |
    abcoAI812hcnaik
    UUVCQlFVQU1Jada
    bkkxWHgKRHanca1
  tls.key: |
    abcoAI812hcnaikabcoAI812hcnaikabcoAI812hcnaik

Apply entire Secret to Pod

Make sure the secretRef name is equal to Secret file metadata name.

api-party.yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-party
spec:
  containers:
    - name: api-party
      image: api-party:0.1.0
      ports:
        - containerPort: 8080
      envFrom:
        - secretRef:
            name: api-secret # Secret file metadata name

Apply only 1 value from Secret to Pod

We can also apply only 1 value from Secret to Pod.

api-party.yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-party
spec:
  containers:
    - name: api-party
      image: api-party:0.1.0
      ports:
        - containerPort: 8080
      env:
        - name: token
          valueFrom:
            secretKeyRef:
              name: api-secret # Secret file metadata name
              key: token # Secret file under data section

Apply Secret to volume

ℹ️

A container using a Secret as a subPath volume will not receive Secret updates.

In some cases, you might want to create files with the contents during runtime, but you can actually do that using Secret.

special-cm.yaml
apiVersion: v1
kind: Secret
metadata:
  name: sample-secret
data:
  .secret-file: bXlzcWw=
api-party.yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-party
spec:
  containers:
    - name: api-party
      image: api-party:0.1.0
      ports:
        - containerPort: 8080
      volumeMounts:
        - name: secret-volume
          mountPath: /app/src
          readOnly: true
          # subPath: src
  volumes:
    - name: secret-volume
      configMap:
        name: sample-secret

In this case, a file called .secret-file will be created in /app/src, so this file will present at this path /app/src/.secret-file.