Restrict a Web Server to TLS 1.3 Only
The secure-web Deployment in namespace secure-space uses a ConfigMap tls-config that currently supports both TLS 1.2 and TLS 1.3. Task: Modify the configuration so that only TLS 1.3 is supported.
Live lab access
This task description is public. Its live lab requires full access; six other CKA labs are available in Free Practice with AI Lab Coach.
Create an account to continue →
Explore the six free labs with AI Lab Coach →
Read the brief on any device. Use a desktop or laptop for the terminal lab. Nothing starts until you choose Start.
The task
The secure-web Deployment in namespace secure-space uses a ConfigMap tls-config that currently supports both TLS 1.2 and TLS 1.3.
Task: Modify the configuration so that only TLS 1.3 is supported.
Note: ConfigMaps are immutable - you must delete and recreate it, then restart the Deployment.
Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster. You also need openssl on the machine where you run kubectl.
Changing TLS versions in nginx when the ConfigMap is immutable
Which TLS versions a web server accepts is plain server configuration: in nginx it is the ssl_protocols directive (default TLSv1.2 TLSv1.3 in current releases). In Kubernetes that file usually arrives through a ConfigMap volume, and three layers then decide whether a change takes effect: whether the ConfigMap can be edited at all, whether the Pod sees the new file, and whether nginx rereads it. An immutable ConfigMap closes the first door, so you need a different path.
ConfigMap (immutable: true) ──→ volume in Pod ──→ /etc/nginx/conf.d/vault.conf ──→ nginx reads it at start
change = new ConfigMap + new Pods, never an in-place edit
1. Deploy a server that only speaks TLS 1.2
Save this as tls-vault.yaml. The official nginx image includes every /etc/nginx/conf.d/*.conf file inside its http block, so mounting the ConfigMap there is enough.
apiVersion: v1
kind: ConfigMap
metadata:
name: vault-nginx-v1
namespace: prepium-tutorial
immutable: true
data:
vault.conf: |
server {
listen 8443 ssl;
ssl_certificate /etc/nginx/tls/tls.crt;
ssl_certificate_key /etc/nginx/tls/tls.key;
ssl_protocols TLSv1.2;
location / { return 200 "vault ok\n"; }
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: vault
namespace: prepium-tutorial
spec:
replicas: 1
selector:
matchLabels:
app: vault
template:
metadata:
labels:
app: vault
spec:
containers:
- name: web
image: nginx:1.27-alpine
ports:
- containerPort: 8443
volumeMounts:
- name: conf
mountPath: /etc/nginx/conf.d # included by the image's nginx.conf
- name: tls
mountPath: /etc/nginx/tls
readOnly: true
volumes:
- name: conf
configMap:
name: vault-nginx-v1
- name: tls
secret:
secretName: vault-cert
kubectl create namespace prepium-tutorial
openssl req -x509 -newkey rsa:2048 -noenc -days 7 -keyout vault.key -out vault.crt -subj "/CN=vault.local"
kubectl -n prepium-tutorial create secret tls vault-cert --cert=vault.crt --key=vault.key
kubectl apply -f tls-vault.yaml
kubectl -n prepium-tutorial rollout status deployment/vault
2. Test each version explicitly
kubectl -n prepium-tutorial port-forward deployment/vault 8443:8443 &
openssl s_client -connect 127.0.0.1:8443 -tls1_2 -brief </dev/null
openssl s_client -connect 127.0.0.1:8443 -tls1_3 -brief </dev/null
A plain connection would negotiate the highest version both sides share, which proves little. Forcing a version does: the TLS 1.2 attempt completes and the brief summary names the protocol, while the TLS 1.3 attempt fails the handshake with an error. Test both directions every time, the allowed version and the refused one.
3. Try to edit, and see why you cannot
kubectl -n prepium-tutorial edit configmap vault-nginx-v1
Change the protocol line and save: the API server refuses, because once immutable: true is set neither data nor binaryData can change, and the flag itself cannot be switched back. The payoff is that the kubelet stops watching the object, which reduces API server load in large clusters.
4. Roll out a new version
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: vault-nginx-v2
namespace: prepium-tutorial
immutable: true
data:
vault.conf: |
server {
listen 8443 ssl;
ssl_certificate /etc/nginx/tls/tls.crt;
ssl_certificate_key /etc/nginx/tls/tls.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / { return 200 "vault ok\n"; }
}
EOF
kubectl -n prepium-tutorial patch deployment vault --type=json \
-p='[{"op":"replace","path":"/spec/template/spec/volumes/0/configMap/name","value":"vault-nginx-v2"}]'
kubectl -n prepium-tutorial rollout status deployment/vault
Pointing the Pod template at a new name is a template change, so the Deployment rolls out fresh Pods by itself, and vault-nginx-v1 stays behind for an instant rollback. Restart the port-forward (it was attached to a Pod that has now been replaced) and repeat both s_client tests: now both versions succeed.
The alternative is to delete the ConfigMap and recreate it under the same name. Running Pods keep their existing mount, and nginx only rereads configuration on reload or restart, so you must follow with kubectl rollout restart deployment/vault. The Kubernetes documentation recommends recreating Pods after replacing an immutable ConfigMap for exactly this reason.
Common mistakes
- Recreating the ConfigMap and stopping there. The old Pods keep serving the old file, and the test still shows the previous behaviour.
- Mounting the file with
subPath. Such mounts never receive ConfigMap updates, even when the ConfigMap is mutable. - Verifying with a single unforced connection. Always force each version so a refusal is proven, not assumed.
- Forgetting the
immutablefield on the new ConfigMap when the original policy required it.
| Example check | Evidence |
|---|---|
| Config holds the intended directive | kubectl get configmap -o yaml shows the ssl_protocols line and immutable: true |
| Pods use the current config | Pod start times are after the change; the volume names the intended ConfigMap |
| Allowed version works | openssl s_client -tls1_2 or -tls1_3 completes the handshake as intended |
| Refused version is refused | The forced handshake for the excluded version fails |
Cleanup: stop the port-forward, then kubectl delete namespace prepium-tutorial and rm vault.key vault.crt. References: ConfigMaps, nginx ssl module, openssl s_client. Next, try TLS Secrets or TLS termination with Gateway API.
Video walkthrough: TLS Version Restriction on YouTube. It shows the full solution, so try the task first.
What this tests
Expose workloads with services and ingress, and restrict traffic with network policies. On the CKA exam, Services & Networking tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.
Practice it for real
The public description lets you study the task before signing up. Full access includes every live lab. Six selected CKA scenarios are included in Free Practice, with five shared sessions each week. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.