Mastering the CKS Kubernetes Exam — 5/6 — Cluster Hardening

Rafael Medeiros
6 min readSep 10, 2024

--

As you prepare for the Certified Kubernetes Security Specialist (CKS) exam, it’s crucial to focus on securing your Kubernetes cluster effectively.

This is part 5 of the 6-post series, showing all the exam’s domains and competencies.

Part 1 is here

part 2 is here

part 3 is here

part 4 is here

Today’s competency is Supply Chain Security, which constitutes 20% of the exam:

Table of Contents

1. Minimize Base Image Footprint

2. Secure Your Supply Chain: Whitelist Allowed Registries, Sign and Validate Images

3. Use Static Analysis of User Workloads

4. Scan Images for Known Vulnerabilities

1. Minimize Base Image Footprint

Recommended Lab:

Minimizing the footprint of your base images is one of the most effective ways to reduce the attack surface of your containers. Smaller images have fewer components and, therefore, fewer vulnerabilities.

Example: Using Minimal Base Images

Instead of using a full-featured base image like ubuntu, consider using a minimal base image such as alpine. Here’s a sample Dockerfile that uses an Alpine base image:

# Use a minimal base image
FROM alpine:latest
# Install only necessary packages
RUN apk add --no-cache curl
# Copy application files
COPY my-app /usr/local/bin/my-app
USER myuser
# Set the entrypoint for the container
ENTRYPOINT ["/usr/local/bin/my-app"]

Exam tips:

Remove root users whenever possible;

Reduce image size and attack surface by using alpine images;

Run the update and install commands in the same RUN instructions (e.g. apt update && apt install curl);

Layers that copy secrets are not secure, even if the next instruction is to delete them, because layers will persist the data;

2. Secure Your Supply Chain: Whitelist Allowed Registries, Sign and Validate Images

Recommended Lab:

Securing your supply chain involves controlling which registries are allowed and ensuring that the images you use are trusted. This can be achieved through image signing and validation.

OPA Gatekeeper is an open-source policy enforcement tool that integrates with Kubernetes to help manage and enforce policies on your Kubernetes cluster. One critical policy is to control which container image registries are allowed, ensuring that only trusted sources are used for container deployments.

Example Policy: Image Registry Whitelist

Here’s an example of a Gatekeeper policy that restricts the use of container images to those from specific registries. This helps in mitigating risks associated with using images from untrusted or potentially compromised sources.

To use this policy, you need to create a ConstraintTemplate and Constraint in OPA Gatekeeper that references this Rego policy. Here’s how you can do it:

  1. Create a ConstraintTemplate:

It defines the policy logic and is applied as a CRD in Kubernetes. This CRD defines the schema and logic for the constraints that will use it:

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8simagewhitelist
spec:
crd:
spec:
names:
kind: K8sImageRegistry
listKind: K8sImageRegistryList
singular: k8simageregistry
plural: k8simageregistries
validation:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
match:
type: object
properties:
kinds:
type: array
items:
type: object
properties:
apiGroups:
type: array
items:
type: string
kinds:
type: array
items:
type: string
parameters:
type: object
properties:
allowedRegistries:
type: array
items:
type: string
rego: |
package k8simagewhitelist

allowed_registries = [
"trusted-registry.example.com",
"another-trusted-registry.example.com"
]

deny[msg] {
container_image := input.review.object.spec.containers[_].image
image_registry := split(container_image, "/")[0]
not allowed_registries[image_registry]
msg := sprintf("Image from registry '%s' is not allowed.", [image_registry])
}

Create the Constraint CRD

Once the ConstraintTemplate is in place, you create a Constraint that uses this template. The Constraint specifies which resources the policy applies to and any parameters for the policy.

apiVersion: templates.gatekeeper.sh/v1beta1
kind: K8sImageRegistry
metadata:
name: image-registry-whitelist
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
allowedRegistries:
- "trusted-registry.example.com"
- "another-trusted-registry.example.com"

Apply these YAML manifests to your Kubernetes cluster to enforce the registry whitelist policy. The Gatekeeper controller will then evaluate all Pod creations and updates against this policy, denying any Pods that use images from unauthorized registries.

You can read more about OPA constraint templates here:

3. Use Static Analysis of User Workloads

Static analysis of your Kubernetes resources and Dockerfiles helps to identify potential security issues before deployment.

Recommended labs:

Static Analysis of Dockerfiles

Common Issues to Look For:

  • Use of Untrusted Base Images: Ensure that you use official and well-maintained base images. Avoid using latest tags.
  • Unnecessary Privileges: Check for USER instructions and avoid running containers as root. Use the USER directive to specify a non-root user.
  • Package Management: Ensure packages are updated to their latest versions to avoid known vulnerabilities.
  • Hardcoded Secrets: Avoid hardcoding sensitive data like passwords or API keys in Dockerfiles.

Misconfigured Dockerfile Example

Issue: Running as root and not using any tag in the image:

# Dockerfile

FROM ubuntu:latest

# Install packages without updating first
RUN apt-get install -y curl

# Expose port
EXPOSE 80

# Run as root user
CMD ["curl", "http://example.com"]

Fix it with the following:

# Dockerfile

# Use the Alpine base image
FROM alpine:latest

# Install curl securely
RUN apk update && apk add --no-cache curl

# Create a non-root user
RUN adduser -D myuser

# Switch to the non-root user
USER myuser

# Expose port
EXPOSE 80

# Run as the non-root user
CMD ["curl", "http://example.com"]

Base Image:

  • FROM alpine:latest: Changed the base image to Alpine Linux.

Security Context:

  • USER myuser: Switches to the non-root user.

This Dockerfile is now optimized for Alpine Linux, a lightweight base image, which helps in creating smaller and more secure container images.

Static Analysis of Kubernetes Resources

Common Issues to Look For:

  • Pod Security Policies: Ensure that pods are not running with excessive privileges. Look for privileged containers, like allowPrivilegeEscalation being true, host network usage, and root user privileges.
  • ConfigMaps and Secrets: Ensure that sensitive data is properly managed using Kubernetes Secrets rather than ConfigMaps.
  • Sensitive Data: Ensure sensitive data is not stored in insecure volumes; use Kubernetes Secrets or ConfigMaps appropriately.

Misconfigured Deployment Example

Issues:

  • Running as Root: The container runs as the root user.
  • Privileged Escalation: No restrictions on privilege escalation.
  • Secrets as Environment Variables: Secrets are hardcoded directly in the deployment manifest.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-container
image: my-registry/my-app:latest
ports:
- containerPort: 80
# Hardcoded secrets (not secure)
env:
- name: MY_SECRET
value: "supersecretvalue"
# No resource limits specified
# Running as root (default behavior)
securityContext:
allowPrivilegeEscalation: true

Fixed Example

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-container
image: my-registry/my-app:latest
ports:
- containerPort: 80
# Use environment variable from Kubernetes Secret
env:
- name: MY_SECRET
valueFrom:
secretKeyRef:
name: my-secret
key: secret-key
# Set resource requests and limits
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
# Run as non-root user and restrict privilege escalation
securityContext:
runAsUser: 1000
allowPrivilegeEscalation: false
---
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
secret-key: c3VwZXJzZWNyZXQ= # base64 encoded value of "supersecretvalue"

4. Scan Images for Known Vulnerabilities

Regularly scanning container images for known vulnerabilities is crucial to maintaining security.

Example: Static Analysis Tools

Tools like trivy can scan Dockerfiles for vulnerabilities. Here’s how you might use it to scan your Dockerfile:

# Scan Dockerfile with Trivy
trivy image --severity HIGH,CRITICAL nginx

This way you filter only by HIGH and CRITICAL vulnerabilities, which is most likely what they will ask in the exam. Then you have the following result:

Exam tips:

You may be tasked to find the less vulnerable image among the options and write to a file, or delete the pod with more vulnerabilities found, be prepared for any kind of task that involves finding the vulnerabilities and quickly scan them.

Conclusion

Good luck with your CKS exam prep!

Stay tuned for Part 6/6, which will be out soon.

If you have any questions or need a hand with anything, just let me know. Let’s tackle this certification journey together!

Follow me on: Linkedin

--

--

Rafael Medeiros
Rafael Medeiros

Written by Rafael Medeiros

DevOps Engineer | CNCF Kubestronaut | 3x Azure | Terraform Fanatic | Another IT Professional willing to help the community

No responses yet