Domain 4: Application Environment, Configuration and Security (25%)¶
Overview¶
This is the highest-weighted domain on the CKAD exam. It covers ConfigMaps, Secrets, SecurityContexts, ServiceAccounts, resource management (requests, limits, quotas), admission controllers, and extending Kubernetes with Custom Resources and Operators.
ConfigMaps¶
π ConfigMaps - ConfigMap concepts π Configure a Pod to Use a ConfigMap - Consuming ConfigMaps in Pods
Creating ConfigMaps¶
Imperative:
# From literals
kubectl create configmap app-config \
--from-literal=DB_HOST=localhost \
--from-literal=DB_PORT=5432 \
--from-literal=LOG_LEVEL=info
# From a file
kubectl create configmap app-config --from-file=config.properties
# From an env file
kubectl create configmap app-config --from-env-file=config.env
# From a directory (each file becomes a key)
kubectl create configmap app-config --from-file=./config-dir/
# Generate YAML
kubectl create configmap app-config --from-literal=key=value --dry-run=client -o yaml
Declarative:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DB_HOST: "localhost"
DB_PORT: "5432"
LOG_LEVEL: "info"
config.properties: |
server.port=8080
server.host=0.0.0.0
logging.level=INFO
Consuming ConfigMaps¶
Method 1: Individual environment variables
spec:
containers:
- name: app
image: myapp:1.0
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DATABASE_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
Method 2: All keys as environment variables
spec:
containers:
- name: app
image: myapp:1.0
envFrom:
- configMapRef:
name: app-config
prefix: APP_ # Optional: prefix all keys
Method 3: Volume mount (keys become files)
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config
Method 4: Mount specific keys
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: config.properties
path: app.properties # Mounted as /etc/config/app.properties
ConfigMap Updates¶
- Volume-mounted ConfigMaps are eventually updated (can take up to the kubelet sync period)
- Environment variables from ConfigMaps are NOT updated after Pod creation
- To pick up env var changes, the Pod must be restarted
Secrets¶
π Secrets - Secret concepts and types π Managing Secrets using kubectl - Creating Secrets π Distribute Credentials Securely - Using Secrets securely
Secret Types¶
| Type | Usage |
|---|---|
Opaque | Generic key-value pairs (default) |
kubernetes.io/dockerconfigjson | Docker registry credentials |
kubernetes.io/tls | TLS certificate and private key |
kubernetes.io/basic-auth | Basic authentication (username/password) |
kubernetes.io/ssh-auth | SSH authentication (private key) |
kubernetes.io/service-account-token | ServiceAccount token |
Creating Secrets¶
# Generic (Opaque) Secret
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password='s3cur3P@ss'
# From files
kubectl create secret generic tls-data \
--from-file=cert.pem \
--from-file=key.pem
# Docker registry Secret
kubectl create secret docker-registry my-registry \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=password
# TLS Secret
kubectl create secret tls my-tls \
--cert=path/to/cert.pem \
--key=path/to/key.pem
Declarative (values must be base64-encoded):
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4= # echo -n "admin" | base64
password: czNjdXIzUEBzcw== # echo -n "s3cur3P@ss" | base64
Using stringData (plain text, automatically encoded):
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
username: admin
password: s3cur3P@ss
Consuming Secrets¶
Secrets are consumed the same way as ConfigMaps:
As environment variables:
spec:
containers:
- name: app
image: myapp:1.0
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-credentials
key: password
envFrom:
- secretRef:
name: db-credentials
As volume mounts:
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
defaultMode: 0400 # Read-only for owner
Secret Security Notes¶
- Secrets are base64-encoded, NOT encrypted by default
- Enable encryption at rest for production clusters
- Use RBAC to restrict access to Secrets
- Consider using external secret management (Vault, AWS Secrets Manager)
- Set
readOnly: truewhen mounting Secrets as volumes
SecurityContexts¶
π Configure a Security Context for a Pod or Container - SecurityContext configuration π Pod Security Standards - Privileged, Baseline, Restricted π Pod Security Admission - Enforcing standards
Pod-Level SecurityContext¶
Applies to all containers in the Pod:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsUser: 1000 # UID for all containers
runAsGroup: 3000 # Primary GID for all containers
fsGroup: 2000 # Group for volume mounts
runAsNonRoot: true # Reject containers that run as root
supplementalGroups:
- 4000
containers:
- name: app
image: myapp:1.0
Container-Level SecurityContext¶
Applies to a specific container (overrides Pod-level where applicable):
spec:
containers:
- name: app
image: myapp:1.0
securityContext:
runAsUser: 1000
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
privileged: false
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
Key SecurityContext Fields¶
Pod-level only: - fsGroup: Group applied to all mounted volumes - supplementalGroups: Additional groups for all containers
Pod or container level: - runAsUser: UID to run the container process - runAsGroup: GID for the container process - runAsNonRoot: Fail if container tries to run as root (UID 0)
Container-level only: - readOnlyRootFilesystem: Mount root filesystem as read-only - allowPrivilegeEscalation: Allow child processes to gain more privileges - privileged: Run container in privileged mode (avoid in production) - capabilities: Add or drop Linux capabilities
Common Capability Management¶
securityContext:
capabilities:
drop:
- ALL # Drop all capabilities
add:
- NET_BIND_SERVICE # Bind to ports below 1024
- SYS_TIME # Set system clock
Pod Security Standards¶
| Level | Description |
|---|---|
| Privileged | No restrictions - for system-level workloads |
| Baseline | Prevents known privilege escalations - reasonable default |
| Restricted | Heavily restricted - follows hardening best practices |
ServiceAccounts¶
π Service Accounts - ServiceAccount concepts π Configure Service Accounts for Pods - Using ServiceAccounts in Pods π Managing Service Accounts - Administration
ServiceAccount Basics¶
- Every namespace has a
defaultServiceAccount - Pods use the
defaultServiceAccount unless you specify otherwise - ServiceAccount tokens are automatically mounted at
/var/run/secrets/kubernetes.io/serviceaccount/ - Use custom ServiceAccounts for fine-grained RBAC
Creating and Using ServiceAccounts¶
# Create a ServiceAccount
kubectl create serviceaccount my-sa
kubectl create sa my-sa -n my-namespace
Assign to a Pod:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: my-sa
automountServiceAccountToken: true # Default is true
containers:
- name: app
image: myapp:1.0
Disable automatic token mounting:
spec:
serviceAccountName: my-sa
automountServiceAccountToken: false
containers:
- name: app
image: myapp:1.0
RBAC with ServiceAccounts¶
π Using RBAC Authorization - RBAC documentation
# Create a Role
kubectl create role pod-reader \
--verb=get,list,watch \
--resource=pods \
-n my-namespace
# Bind the Role to a ServiceAccount
kubectl create rolebinding pod-reader-binding \
--role=pod-reader \
--serviceaccount=my-namespace:my-sa \
-n my-namespace
# Create a ClusterRole
kubectl create clusterrole secret-reader \
--verb=get,list \
--resource=secrets
# Bind with ClusterRoleBinding
kubectl create clusterrolebinding secret-reader-binding \
--clusterrole=secret-reader \
--serviceaccount=my-namespace:my-sa
Resource Management¶
π Managing Resources for Containers - Resource requests and limits π Resource Quotas - Namespace-level quotas π Limit Ranges - Default and max/min limits
Requests and Limits¶
spec:
containers:
- name: app
image: myapp:1.0
resources:
requests:
cpu: 100m # 0.1 CPU core
memory: 128Mi # 128 mebibytes
limits:
cpu: 500m # 0.5 CPU core
memory: 256Mi # 256 mebibytes
Behavior: - Requests: Used by the scheduler to decide which node to place the Pod on. The node must have this capacity available. - Limits: Maximum the container can use. CPU is throttled when exceeded. Memory causes OOMKill when exceeded.
Resource Units: | Resource | Unit | Examples | |----------|------|---------| | CPU | Millicores | 100m = 0.1 CPU, 1 = 1 CPU, 1500m = 1.5 CPU | | Memory | Bytes | 128Mi = 128 MiB, 1Gi = 1 GiB, 256M = 256 MB |
ResourceQuotas¶
Limit total resource consumption in a namespace:
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: my-namespace
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20"
services: "10"
persistentvolumeclaims: "5"
configmaps: "10"
secrets: "10"
# Create imperatively
kubectl create quota compute-quota -n my-namespace \
--hard=requests.cpu=4,requests.memory=8Gi,limits.cpu=8,limits.memory=16Gi,pods=20
# View quota usage
kubectl describe quota compute-quota -n my-namespace
Important: When a ResourceQuota exists for CPU/memory, every Pod in the namespace MUST specify requests and limits. Otherwise, the Pod will be rejected.
LimitRanges¶
Set default, min, and max resource constraints per container/Pod:
apiVersion: v1
kind: LimitRange
metadata:
name: resource-limits
namespace: my-namespace
spec:
limits:
- type: Container
default: # Default limits
cpu: 200m
memory: 256Mi
defaultRequest: # Default requests
cpu: 100m
memory: 128Mi
min: # Minimum allowed
cpu: 50m
memory: 64Mi
max: # Maximum allowed
cpu: "1"
memory: 1Gi
- type: Pod
max:
cpu: "2"
memory: 2Gi
Key Points: - LimitRange applies to individual containers or Pods - ResourceQuota applies to the total namespace - LimitRange provides defaults, so Pods without resource specs get automatic values - If a container exceeds LimitRange max, it will be rejected
Admission Controllers¶
π Admission Controllers Reference - Built-in admission controllers π Dynamic Admission Control - Webhooks
What Are Admission Controllers?¶
Admission controllers intercept requests to the API server after authentication and authorization but before the object is persisted. They can validate or mutate requests.
Types¶
- Validating: Accept or reject a request (e.g., enforce policies)
- Mutating: Modify the request object (e.g., inject defaults)
Important Admission Controllers for CKAD¶
| Controller | Purpose |
|---|---|
NamespaceLifecycle | Rejects requests in non-existent or terminating namespaces |
LimitRanger | Applies default resource requests/limits from LimitRange |
ResourceQuota | Enforces ResourceQuota constraints |
ServiceAccount | Auto-assigns ServiceAccount and mounts token |
PodSecurity | Enforces Pod Security Standards (replaced PodSecurityPolicy) |
DefaultStorageClass | Assigns default StorageClass to PVCs without one |
Dynamic Admission Webhooks¶
ValidatingWebhookConfiguration: External validation of resourcesMutatingWebhookConfiguration: External mutation of resources- Used by tools like OPA Gatekeeper and Kyverno for policy enforcement
Custom Resources and Operators¶
π Custom Resources - Extending the API π Custom Resource Definitions - Creating CRDs π Operator Pattern - Operators
Custom Resource Definitions (CRDs)¶
CRDs let you add new resource types to Kubernetes. Once a CRD is created, you can use kubectl to manage instances of the custom resource.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: backups.myapp.example.com
spec:
group: myapp.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
schedule:
type: string
database:
type: string
scope: Namespaced
names:
plural: backups
singular: backup
kind: Backup
shortNames:
- bk
Operators¶
- Operators combine CRDs with custom controllers
- They automate the management of complex applications
- The controller watches for changes to custom resources and takes action
- Example: A database operator that handles provisioning, scaling, backups, and failover
Working with Custom Resources¶
# List CRDs
kubectl get crd
# Get instances of a custom resource
kubectl get backups
kubectl get bk # Using short name
# Describe a custom resource
kubectl describe backup my-backup