TT Lab
Get started
Learn Learning paths Courses

KCSA — Kubernetes Security Associate

Open etcd and the Password Falls Out in Plaintext

Continue in TT Lab

Goal

Open etcd, the cluster's only store of truth, yourself and see with your own eyes that Secrets are stored in plaintext. Then observe, by contrast, that what RBAC blocks is only the API path through the apiserver, and that connecting to etcd directly bypasses that boundary.

Why it matters

Every Kubernetes object is ultimately stored in etcd. The data values of a Secret appear as base64, so they look as if they were hidden, but base64 is encoding, not encryption. If you do not turn on encryption at rest, anyone who can reach etcd reads the original password as it is.

Meanwhile, RBAC is powerful, but its control applies only to requests that pass through the apiserver. RBAC cannot intervene on paths that access the node, the etcd data directory, or an etcd port with no authentication directly. So etcd itself must be protected separately with TLS, mutual authentication, and encryption at rest. This lab first shows "why those are needed" by showing what it looks like without the defenses.

Steps

  1. Create the namespace kcsa-etcd.
  2. Create the Secret db-cred (password) and the ConfigMap app-cfg (mode).
  3. Connect to the etcd endpoint without a certificate and save the status to a file.
  4. Read the Secret directly from etcd and extract the original password to a file.
  5. Judge that the value is plaintext, not encryption, and record it.
  6. Check the RBAC boundary with a least-privilege ServiceAccount that reads only ConfigMaps.
  7. Organize the three controls that protect etcd (client TLS, peer TLS, and encryption at rest).
  8. Leave the observed results as a ledger in /root/kcsa-etcd/report.txt.

Notes

Open the namespace to observe

Create the namespace kcsa-etcd. You will look at the Secret inside it directly in etcd.

Create a namespace with kubectl create namespace. To make it safe to run several times, use --dry-run=client -o yaml | kubectl apply -f -.

Entrust one password to the cluster

In the namespace kcsa-etcd, create a generic Secret db-cred containing the literal password=Pl4inEtcd!. In the same namespace, create a ConfigMap app-cfg containing the key mode=prod.

Create the Secret with kubectl create secret generic <이름> --from-literal=키=값 (the placeholders are the name and key=value), and the ConfigMap with kubectl create configmap <이름> --from-literal=키=값 (the same placeholders). A Secret's data values are stored base64-encoded (it is not encryption).

Reach etcd as it is, without a certificate

Print the etcd endpoint status as a table with etcdctl and save it to /root/kcsa-etcd/etcd-status.txt. Confirm that you connect to 127.0.0.1:2379 without a client certificate.

Look at the status with ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 endpoint status -w table. The etcd in this cluster has no TLS or authentication, so it responds right away without certificate options. Redirect the output to a file.

Pick up the password at the bottom of the store

Read the db-cred Secret stored in etcd directly, and write only the password string inside it to /root/kcsa-etcd/leaked.txt (the value only, with no extra newline). The file contents must be exactly equal to the real password.

Secrets are under the etcd key /registry/secrets/<네임스페이스>/<이름> (the placeholders are the namespace and the name). The result of etcdctl get <키> (the placeholder is the key) has binary mixed in, so filter out only the text with grep -a. You must find out for yourself what the value is — if you base64-decode the Secret's data, you learn the original, and confirm that the same string is in the etcd original as it is.

Judge that this is not encryption

Judge whether the value sitting on disk (etcd) is encrypted. If it is not encrypted, write only the single word no to /root/kcsa-etcd/encrypted.txt.

Base64 is encoding, not encryption. If the original password is caught by grep as it is in the etcd original, it means encryption at rest is off. Write the verdict as a single lowercase word.

RBAC guards only the API path

In the namespace kcsa-etcd, create the ServiceAccount apponly and the Role cfg-only (get and list on ConfigMaps only), and bind this Role to apponly. Then apponly must be able to read ConfigMaps but not Secrets.

Create a least-privilege Role with kubectl create role <이름> --verb=get --verb=list --resource=configmaps (the placeholder is the name), and bind it to the ServiceAccount with kubectl create rolebinding. You can check the actual decision with kubectl auth can-i <동사> <자원> -n <ns> --as=system:serviceaccount:<ns>:<sa> (the placeholders are the verb and the resource).

Write down the three things that protect etcd

Based on the reading material, write the three controls that protect etcd to /root/kcsa-etcd/mitigations.txt in exactly three lines — client-tls=required, peer-tls=required, encryption-at-rest=required.

etcd security has three axes: TLS and mutual authentication between client and etcd, TLS between etcd nodes (peer), and the apiserver's encryption at rest (EncryptionConfiguration). Write all three keys as required. The key names and spelling must match the instructions exactly.

Leave a ledger of what you observed

Write exactly four lines to /root/kcsa-etcd/report.txt — etcd-auth=none, secret-on-disk=plaintext, rbac-blocks-apponly=yes, etcd-bypasses-rbac=yes. The values must match the actual state you observed in the earlier steps.

The grader compares these four lines with the actual state. Fill them in from the results of the earlier steps: whether etcd was connected to without authentication (none), whether the Secret was stored in plaintext (plaintext), whether apponly cannot read Secrets (yes), and whether direct etcd access bypasses that RBAC boundary (yes).