Environments (ConfigMap) and Secrets
We have 3 ways to set environment variables and secrets;
- key-value pair format
- ConfigMap
- Secrets
key-value pair format
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>
port=8080
env=prod
Declarative
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.
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.
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.
apiVersion: v1
kind: ConfigMap
metadata:
name: model-config
data:
config.py: |
config = {
'url': 'http://localhost:8080'
}
special_file.txt: specialData
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
- 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 Type | Usage |
---|---|
Opaque | arbitrary user-defined data |
kubernetes.io/service-account-token | ServiceAccount token |
kubernetes.io/dockerconfigjson | serialized ~/.docker/config.json file |
kubernetes.io/basic-auth | credentials for basic authentication |
kubernetes.io/ssh-auth | credentials for SSH authentication |
kubernetes.io/tls | data for a TLS client or server |
bootstrap.kubernetes.io/token | bootstrap 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>
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
apiVersion: v1
kind: Secret
metadata:
name: api-secret
data:
token: bXlzcWw=
Docker config
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
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
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
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.
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.
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.
apiVersion: v1
kind: Secret
metadata:
name: sample-secret
data:
.secret-file: bXlzcWw=
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
.