CKS_FINALE

This commit is contained in:
Mosiur Rahman
2026-09-14 16:44:59 +06:00
commit c0da43e51f
57 changed files with 2331 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
# CKS-001 — NetworkPolicy containment under pressure
Status: **Attempt 1 — awaiting learner answer**
Domain: **Cluster Setup (15%)**
Topic: **NetworkPolicy and metadata protection**
Target time: **12 minutes**
Difficulty: **Harder than CKS**
## Scenario
Use context:
```bash
kubectl config use-context prod-sec
```
Namespace `payments` already contains these Pods:
| Pod | Labels |
|---|---|
| `api-0` | `app=api`, `tier=backend` |
| `worker-0` | `app=worker`, `tier=backend` |
| `debug-0` | `app=debug` |
Namespace `gateway` has label `access=payments` and contains Pods labeled `app=ingress`.
Namespace `observability` has label `team=monitoring` and contains Pods labeled `app=prometheus`.
The cluster DNS Pods are in namespace `kube-system` with label `k8s-app=kube-dns`. The cloud instance-metadata endpoint is `169.254.169.254/32`.
## Required end state
Create one or more NetworkPolicies in namespace `payments` so that:
1. Every Pod in `payments` is isolated for both ingress and egress.
2. Only `gateway` Pods labeled `app=ingress` may connect to TCP port `8443` on Pods labeled `app=api`.
3. Only `observability` Pods labeled `app=prometheus` may scrape TCP port `9090` on Pods labeled `tier=backend`.
4. `api` and `worker` Pods may query cluster DNS using UDP and TCP port `53`; `debug-0` must have no egress, including DNS.
5. Pods labeled `app=api` may make TCP connections to any IPv4 destination on port `443`, except `169.254.169.254/32`.
6. No other ingress or egress is permitted.
7. Do not modify Pods, namespaces, Services, or existing labels.
Assume the installed CNI enforces Kubernetes `NetworkPolicy`. Do not use Cilium-specific resources.
## Deliverables
Write your proposed commands/manifests and verification plan below. Do not ask the teacher to apply them.
### A. Context and pre-check commands
```bash
kubectl config use-context prod-sec
k get pods -n payment
k get pods -n payment -o wide
k get pods -n payment --show-labels
k get ns --show-labels
k get pods -n gateway --show-labels
k get pods -n observability --show-labels
k get pods -n kube-system --show-labels
```
### B. NetworkPolicy manifest(s)
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy-for-gateway
namespace: payments
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
access: payments
podSelector:
matchLabels:
app: ingress
ports:
- protocol: TCP
port: 8443
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32
ports:
- protocol: TCP
port: 443
- to:
- podSelector:
matchLabels:
k8s-app: kube-dns
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy-for-monitoring
namespace: payments
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
team: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090
egress:
- to:
- podSelector:
matchLabels:
k8s-app: kube-dns
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy-for-debug
namespace: payments
spec:
podSelector:
matchLabels:
app: debug
policyTypes:
- Ingress
- Egress
```
### C. Verification commands and expected outcomes
Show at least one positive and one negative test for every distinct rule. Tests must demonstrate that metadata access is denied and that `debug-0` has no DNS egress.
```bash
k apply -f np-payments.yaml
k get networkpolicy -n payments
POD=$(k get pod -n gateway -l app=ingress -o name)
API_IP=$(k get pod api-0 -n payments -o jsonpath='{.status.podIP}')
WORKER_IP=$(k get pod worker-0 -n payments -o jsonpath='{.status.podIP}')
k exec -n gateway $POD -- telnet $API_IP 8443
POD=$(k get pod -n observability -l app=prometheus -o name)
k exec -n observability $POD -- telnet $WORKER_IP 9090
```
### D. Short reasoning
Explain briefly why your selectors do not accidentally allow:
- every Pod in an allowed namespace;
- every port from an allowed source;
- metadata access over a non-443 port;
- DNS access from `debug-0`.
Write here:
>
## Submission
- [ ] I checked the context.
- [ ] I validated the YAML client-side or server-side.
- [ ] I covered all seven required outcomes.
- [ ] I included both allowed and denied verification cases.
- [ ] I am ready for grading.
When ready, save this file and tell the teacher: **Grade CKS-001 attempt 1**.

View File

@@ -0,0 +1,63 @@
# CKS-002 — NetworkPolicy two-sided isolation
Status: **Fresh mastery attempt — awaiting learner answer**
Domain: **Cluster Setup (15%)**
Topic: **NetworkPolicy and cluster-level network restriction**
Target time: **15 minutes**
Difficulty: **Harder than CKS**
## Scenario
Use context:
```bash
kubectl config use-context secure-prod
```
Namespace `blue` contains:
| Pod | Labels | Listens on |
|---|---|---|
| `web-0` | `app=web` | TCP `443` |
| `api-0` | `app=api` | TCP `8080` |
| `batch-0` | `app=batch` | none |
Additional facts:
- Namespace `edge` has label `zone=trusted-edge`; its approved clients have label `role=proxy`.
- Namespace `monitoring` has label `purpose=metrics`; its approved clients have label `role=scraper`.
- Cluster DNS Pods are in `kube-system` with label `k8s-app=kube-dns`.
- The internal database range is `10.40.0.0/16`, but subnet `10.40.8.0/24` is reserved and forbidden.
- Future unlabeled Pods may be added to namespace `blue`.
## Required end state
Create Kubernetes `NetworkPolicy` resources in namespace `blue` so that:
1. Every current and future Pod in `blue` is default-denied for both ingress and egress.
2. Only `role=proxy` Pods from the `zone=trusted-edge` namespace may reach `app=web` on TCP `443`.
3. Only `app=web` Pods in `blue` may reach `app=api` on TCP `8080`.
4. Only `role=scraper` Pods from the `purpose=metrics` namespace may reach both `web` and `api` Pods on TCP `9100`.
5. Only `web` and `api` Pods may query cluster DNS over both UDP and TCP `53`.
6. Only `api` Pods may connect to `10.40.0.0/16` on TCP `5432`, excluding `10.40.8.0/24`.
7. `batch` Pods must have no ingress or egress. No other traffic is permitted.
8. Do not modify existing Pods, namespaces, labels, or Services. Use standard Kubernetes `NetworkPolicy` only.
## Section B — scored implementation
Section B is the only scored section. Write all required manifests below.
```yaml
# Replace this comment with your complete NetworkPolicy manifests.
```
## Optional self-check — unscored
- [ ] An empty-selector policy covers future unlabeled Pods.
- [ ] Every cross-namespace peer requires the correct namespace and Pod labels together.
- [ ] The `web → api:8080` path is permitted in both relevant policy directions.
- [ ] DNS does not become available to `batch` or future unlabeled Pods.
- [ ] The reserved database subnet remains unreachable.
- [ ] No rule accidentally opens all ports or all sources.
When ready, save this file and tell the teacher: **Grade CKS-002 fresh attempt**.

View File

@@ -0,0 +1,43 @@
# CKS-003 — Repair a CIS API-server failure
Domain: **1.2 CIS benchmark review**
Target time: **6 minutes**
Score: **Section B only**
## Task
On host `controlplane`, `kube-bench` reports:
```text
[FAIL] 1.2.7 Ensure that the --authorization-mode argument is not set to AlwaysAllow
```
The kube-apiserver is a static Pod defined at:
```text
/etc/kubernetes/manifests/kube-apiserver.yaml
```
Its current command contains:
```yaml
- --authorization-mode=AlwaysAllow,RBAC
```
Change only what is necessary so authorization modes are exactly `Node,RBAC`. Back up the manifest first. Then verify that the API server recovers and check `1.2.7` passes.
## Section B — scored answer
Write the commands you would execute and the final corrected manifest line:
```bash
cp /etc/kubernetes/manifests/kube-apiserver.yaml ~/kube-apiserver.yaml.bkp
vi /etc/kubernetes/manifests/kube-apiserver.yaml
- --authorization-mode=Node,RBAC
crictl ps -a | grep -i apiserver
k get pods -n kube-system
ps -aux | grep -i apiserver
kube-bench run --targets master --check 1.2.7
```
When ready, say: **Grade CKS-003**.

View File

@@ -0,0 +1,43 @@
# CKS-004 — Repair one kubelet CIS failure
Domain: **1.2 CIS benchmark review**
Target time: **5 minutes**
Score: **Section B only**
## Task
On worker `node01`, kube-bench reports:
```text
[FAIL] 4.2.1 Ensure that the --anonymous-auth argument is set to false
```
`/var/lib/kubelet/config.yaml` currently contains:
```yaml
authentication:
anonymous:
enabled: true
```
Back up the file outside its directory, correct only this setting, restart the appropriate service, verify `node01` returns to `Ready`, and rerun only check `4.2.1` for the node target.
## Section B — scored answer
```bash
ssh node01
cp /var/lib/kubelet/config.yaml ~/config.yaml.bkp-$(date +%Y-%m-%d)
nano /var/lib/kubelet/config.yaml
authentication:
anonymous:
enabled: false
systemctl restart kubelet
systemctl status kubelet
journalctl -xeu kubelet
kube-bench run --targets node --check 4.2.1
exit
k get node node01
```
When ready, say: **Grade CKS-004**.

View File

@@ -0,0 +1,51 @@
# CKS-005 — Close the kubelet read-only port
Domain: **1.2 CIS benchmark review**
Target time: **4 minutes**
Score: **Section B only**
## Task
On `node02`, kube-bench reports that the kubelet read-only port is enabled. The complete exercise file `/var/lib/kubelet/config.yaml` is:
```yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 0.0.0.0
port: 10250
readOnlyPort: 10255
authentication:
anonymous:
enabled: false
webhook:
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: Webhook
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
rotateCertificates: true
serverTLSBootstrap: true
staticPodPath: /etc/kubernetes/manifests
```
Back up the file outside its directory, disable the read-only port, restart kubelet, confirm TCP `10255` is no longer listening, rerun only check `4.2.4` on the node, then verify `node02` is `Ready` from the original host.
## Section B — scored answer
```bash
ssh node02
cp /var/lib/kubelet/config.yaml ~/config.yaml-$(date +%Y-%m-%d)
nano /var/lib/kubelet/config.yaml
readOnlyPort: 0
systemctl restart kubelet
systemctl status kubelet
ss -tunlp | grep -i 10255
kube-bench run --targets node --check 4.2.4
exit
k get nodes
```
When ready, say: **Grade CKS-005**.

View File

@@ -0,0 +1,77 @@
# CKS-006 — Correct kubelet authorization
Domain: **1.2 CIS benchmark review**
Target time: **4 minutes**
Score: **Section B only**
## Task
On `node03`, kube-bench reports that kubelet authorization is not set to `Webhook`. The complete exercise file `/var/lib/kubelet/config.yaml` is:
```yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 0.0.0.0
port: 10250
readOnlyPort: 0
authentication:
anonymous:
enabled: false
webhook:
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: AlwaysAllow
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
rotateCertificates: true
serverTLSBootstrap: true
staticPodPath: /etc/kubernetes/manifests
```
Back up the file outside its directory, correct only the insecure setting, restart and check kubelet, rerun only check `4.2.2` on the node, then verify `node03` is `Ready` from the original host.
## Section B — scored answer
```bash
ssh node03
cp /var/lib/kubelet/config.yaml ~/config.yaml-$(date +%Y-%m-%d)
nano /var/lib/kubelet/config.yaml
```
```yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 0.0.0.0
port: 10250
readOnlyPort: 0
authentication:
anonymous:
enabled: false
webhook:
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: Webhook
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
rotateCertificates: true
serverTLSBootstrap: true
staticPodPath: /etc/kubernetes/manifests
```
```bash
systemctl restart kubelet
systemctl status kubelet
kube-bench run --targets node --check 4.2.2
exit
k get nodes
```
When ready, say: **Grade CKS-006**.

View File

@@ -0,0 +1,26 @@
# CKS-007 — Repair control-plane manifest permissions
Domain: **1.2 CIS benchmark review**
Target time: **3 minutes**
Reward: **100 XP clean win**
## Task
On `controlplane`, kube-bench check `1.1.3` fails because:
```text
/etc/kubernetes/manifests/kube-controller-manager.yaml mode=0644 owner=root group=root
```
Back up the file outside the manifest directory, make its permissions `0600` without changing ownership, verify mode and ownership, then rerun only check `1.1.3` for the master target.
## Section B — scored answer
```bash
cp /etc/kubernetes/manifests/kube-controller-manager.yaml ~/kube-controller-manager.yaml-$(date +%Y-%m-%d)
chmod 0600 /etc/kubernetes/manifests/kube-controller-manager.yaml
ls -la /etc/kubernetes/manifests/kube-controller-manager.yaml
kube-bench run --targets master --check 1.1.3
```
When ready, say **Grade CKS-007**.

View File

@@ -0,0 +1,27 @@
# CKS-008 — Correct manifest ownership
Domain: **1.2 CIS benchmark review**
Time: **2 minutes** · Reward: **100 XP + topic bonus**
## Task
On `controlplane`, check `1.1.4` fails:
```text
/etc/kubernetes/manifests/kube-scheduler.yaml mode=0600 owner=root group=adm
```
Required commands:
- back up outside the manifest directory;
- change ownership to `root:root` without changing mode;
- verify numeric mode and `owner:group`;
- rerun only master check `1.1.4`.
## Section B — scored answer
```bash
# Your four commands
```
Say **Grade CKS-008** when saved.

View File

@@ -0,0 +1,85 @@
# CKS-009 — Add TLS to an existing Ingress
Domain: **1.3 Ingress TLS**
Time: **6 minutes** · Score: **Section B only**
## Existing configuration
Certificate files already exist:
```text
/opt/certs/store.crt
/opt/certs/store.key
```
The complete existing Ingress is:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: storefront
namespace: shop
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
rules:
- host: store.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: storefront
port:
number: 80
```
## Task
1. Inspect the certificate and confirm it covers `store.example.com`.
2. Create TLS Secret `storefront-tls` in namespace `shop` from the supplied files.
3. Update the Ingress for TLS using that Secret without changing its existing route.
4. Verify the Secret type and show the final Ingress.
## Section B — scored answer
```bash
k create secret -n shop tls storefront-tls --cert=/opt/certs/store.crt --key=/opt/certs/store.key
```
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: storefront
namespace: shop
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- store.example.com
secretName: storefront-tls
rules:
- host: store.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: storefront
port:
number: 80
k get ingress -n shop storefront
k describe ingress -n shop storefront
```
Say **Grade CKS-009** when saved.

View File

@@ -0,0 +1,67 @@
# CKS-010 — Remove an exposed dashboard endpoint
Domain: **1.4 Protect node metadata and endpoints**
Time: **4 minutes** · Score: **Section B only**
## Existing objects
```yaml
apiVersion: v1
kind: Service
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: NodePort
selector:
k8s-app: kubernetes-dashboard
ports:
- port: 443
targetPort: 8443
nodePort: 30443
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dashboard-admin
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
```
## Task
The dashboard must remain reachable only inside the cluster and must no longer hold `cluster-admin` privileges. Modify/delete only what is necessary, then verify both outcomes.
## Section B — scored answer
```bash
k delete clusterrolebinding dashboard-admin
k edit service kubernetes-dashboard -n kubernetes-dashboard
kubectl get service kubernetes-dashboard \
-n kubernetes-dashboard \
-o jsonpath='{.spec.type}{"\n"}{.spec.ports[*].nodePort}{"\n"}'
kubectl get clusterrolebinding dashboard-admin
```
```yaml
apiVersion: v1
kind: Service
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: ClusterIP
selector:
k8s-app: kubernetes-dashboard
ports:
- port: 443
targetPort: 8443
```
Say **Grade CKS-010** when saved.

View File

@@ -0,0 +1,66 @@
# CKS-010 — Remove an exposed dashboard endpoint
Domain: **1.4 Protect node metadata and endpoints**
Time: **4 minutes** · Score: **Section B only**
## Existing objects
```yaml
apiVersion: v1
kind: Service
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: NodePort
selector:
k8s-app: kubernetes-dashboard
ports:
- port: 443
targetPort: 8443
nodePort: 30443
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dashboard-admin
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
```
## Task
The dashboard must remain reachable only inside the cluster and must no longer hold `cluster-admin` privileges. Modify/delete only what is necessary, then verify both outcomes.
## Section B — scored answer
```bash
k create clusterrole dashboard-role --verb=get,list,update,delete,patch --resource=pods
k delete clusterrolebinding dashboard-admin
k create clusterrolebinding dashboard-role-binding --clusterrole=dashboard-role --serviceaccount=kubernetes-dashboard:kubernetes-dashborad
k auth can-i get pod --as=system:serviceaccount:kubernetes-cluster:kubernetes-cluster
k edit service kubernetes-dashboard -n kubernetes-dashboard
```
```yaml
apiVersion: v1
kind: Service
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: ClusterIP
selector:
k8s-app: kubernetes-dashboard
ports:
- port: 443
targetPort: 8443
```
Say **Grade CKS-010** when saved.

View File

@@ -0,0 +1,29 @@
# CKS-011 — Verify and install a Kubernetes binary
Domain: **1.5 Platform binary verification**
Time: **4 minutes** · Score: **Section B only**
## Supplied files
```text
/opt/release/kubectl
/opt/release/kubectl.sha256
```
`kubectl.sha256` contains the official raw SHA-256 value but no filename.
## Task
Verify the downloaded binary against the checksum. Install it as `/usr/local/bin/kubectl` with owner `root:root` and mode `0755` only if verification succeeds. Then show its client version and installed ownership/mode.
## Section B — scored answer
```bash
echo "$(cat /opt/release/kubectl.sha256) /opt/release/kubectl" | sha256sum --check && cp /opt/release/kubectl /usr/local/bin/ || echo "Verification failed"
chown root:root /usr/local/bin/kubectl
chmod 0755 /usr/local/bin/kubectl
stat /usr/local/bin/kubectl
/usr/local/bin/kubectl version
```
Say **Grade CKS-011** when saved.

View File

@@ -0,0 +1,35 @@
# CKS-012 — Block cloud metadata
Domain: **1.4 Protect node metadata and endpoints**
Time: **3 minutes** · Reward: **100 XP clean win**
## Task
In namespace `build`, isolate egress for Pods labeled `role=runner`. Allow them to reach any IPv4 destination over TCP `443`, except the metadata endpoint `169.254.169.254/32`. Permit no other egress.
## Section B — scored answer
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: metadata-protector
namespace: build
spec:
podSelector:
matchLabels:
role: runner
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32
ports:
- protocol: TCP
port: 443
```
Say **Grade CKS-012** when saved.

View File

@@ -0,0 +1,33 @@
# CKS-013 — Verify a kubelet binary
Domain: **1.5 Platform binary verification**
Time: **2 minutes** · Reward: **100 XP clean win**
## Task
Files:
```text
/opt/release/kubelet
/opt/release/kubelet.sha256 # raw official hash only
```
Write commands to:
1. verify the binary with `sha256sum --check`;
2. after observing `OK`, install it at `/usr/local/bin/kubelet` as `root:root` with mode `0755` using `install`;
3. show its version and installed mode/owner.
No conditional shell scripting is required.
## Section B — scored answer
```bash
echo "$(cat /opt/release/kubelet.sha256) /opt/release/kubelet" | sha256sum --check
echo "if ok"
install -o root -g root -m 0755 /opt/release/kubelet /usr/local/bin/kubelet
stat /usr/local/bin/kubelet
/usr/local/bin/kubelet --version
```
Say **Grade CKS-013** when saved.

View File

@@ -0,0 +1,23 @@
# CKS-014 — Verify kube-apiserver
Domain: **1.5 Platform binary verification**
Time: **1 minute** · Reward: **100 XP + topic mastery**
## Task
The binary and its official raw checksum are:
```text
/opt/kubernetes/kube-apiserver
/opt/kubernetes/kube-apiserver.sha512
```
Write one command that feeds the raw hash and binary path to `sha512sum --check`.
## Section B — scored answer
```bash
echo "$(cat /opt/kubernetes/kube-apiserver.sha512) /opt/kubernetes/kube-apiserver" | sha512sum --check
```
Say **Grade CKS-014** when saved.

View File

@@ -0,0 +1,23 @@
# CKS-015 — Verify a standard checksum file
Domain: **1.5 Platform binary verification**
Time: **30 seconds** · Reward: **100 XP + topic mastery**
## Task
Directory `/opt/release` contains:
```text
kube-scheduler
kube-scheduler.sha256
```
Unlike earlier tasks, the checksum file already contains both the hash and filename in standard `sha256sum` format. Write the command or commands to verify it.
## Section B — scored answer
```bash
sha256sum --check kube-scheduler.sha256
```
Say **Grade CKS-015** when saved.

View File

@@ -0,0 +1,41 @@
# CKS-016 — Replace cluster-admin with namespaced RBAC
Domain: **2.1 Least-privilege RBAC**
Time: **5 minutes** · Score: **Section B only**
## Existing dangerous binding
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: maya-admin
subjects:
- kind: User
name: maya
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
```
## Task
Remove this binding. In namespace `blue`, allow user `maya` only to:
- `get`, `list`, and `watch` Pods;
- `get` Pod logs.
Create namespaced Role `maya-observer` and RoleBinding `maya-observer`. Include commands that verify one allowed action and that reading Secrets is denied.
## Section B — scored answer
```bash
# Commands
```
```yaml
# Complete Role and RoleBinding
```
Say **Grade CKS-016** when saved.

View File

@@ -0,0 +1,64 @@
# CKS-017 — Remove unnecessary ServiceAccount tokens
Domain: **2.2 Service-account safety**
Time: **5 minutes** · Score: **Section B only**
## Existing workload
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: reporter
namespace: jobs
spec:
schedule: "*/10 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: reporter
image: registry.example.com/reporter:2.1
restartPolicy: Never
```
## Task
The workload does not use the Kubernetes API. Create a dedicated ServiceAccount named `reporter`, disable token automounting on it, and update the complete CronJob Pod template to use it and explicitly disable token automounting. Verify the ServiceAccount and CronJob settings.
## Section B — scored answer
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: reporter
namespace: jobs
automountServiceAccountToken: false
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: reporter
namespace: jobs
spec:
schedule: "*/10 * * * *"
jobTemplate:
spec:
template:
spec:
serviceAccountName: reporter
automountServiceAccountToken: false
containers:
- name: reporter
image: registry.example.com/reporter:2.1
restartPolicy: Never
```
```bash
k create sa reporter -n jobs --dry-run=client -o yaml > reporter-sa.yaml
kubectl apply -f reporter-sa.yaml
```
Say **Grade CKS-017** when saved.

View File

@@ -0,0 +1,97 @@
# CKS-018 — Disable anonymous API access
Domain: **2.3 Restrict Kubernetes API access**
Time: **5 minutes** · Score: **Section B only**
## Complete exercise manifest
`/etc/kubernetes/manifests/kube-apiserver.yaml`:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
hostNetwork: true
containers:
- name: kube-apiserver
image: registry.k8s.io/kube-apiserver:v1.35.0
command:
- kube-apiserver
- --advertise-address=10.0.0.10
- --anonymous-auth=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --secure-port=6443
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
volumeMounts:
- name: pki
mountPath: /etc/kubernetes/pki
readOnly: true
volumes:
- name: pki
hostPath:
path: /etc/kubernetes/pki
type: DirectoryOrCreate
```
## Task
Back up the manifest outside its directory, disable anonymous authentication, wait for API recovery, then verify authenticated readiness and that an unauthenticated request to `https://127.0.0.1:6443/api` returns HTTP `401`.
## Section B — scored answer
```bash
cp /etc/kubernetes/manifests/kube-apiserver.yaml ~/kube-apiserver.yaml-$(date +%Y-%m-%d)
nano /etc/kubernetes/manifests/kube-apiserver.yaml
```
```yaml
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
hostNetwork: true
containers:
- name: kube-apiserver
image: registry.k8s.io/kube-apiserver:v1.35.0
command:
- kube-apiserver
- --advertise-address=10.0.0.10
- --anonymous-auth=false
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --secure-port=6443
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
volumeMounts:
- name: pki
mountPath: /etc/kubernetes/pki
readOnly: true
volumes:
- name: pki
hostPath:
path: /etc/kubernetes/pki
type: DirectoryOrCreate
```
```bash
kubectl get --raw='/readyz'
curl -k -s -o /dev/null -w "%{http_code}\n" https://127.0.0.1:6443/api
```
Say **Grade CKS-018** when saved.

View File

@@ -0,0 +1,24 @@
# CKS-019 — Protect administrator kubeconfig
Domain: **2.3 Restrict Kubernetes API access**
Time: **2 minutes** · Reward: **100 XP + topic mastery**
## Task
On `controlplane`:
```text
/etc/kubernetes/admin.conf mode=0644 owner=root group=root
```
Back it up to `/root/admin.conf.backup`, change only its mode to `0600`, and verify numeric mode plus `owner:group`.
## Section B — scored answer
```bash
cp /etc/kubernetes/admin.conf /root/admin.conf.backup
chmod 0600 /etc/kubernetes/admin.conf
stat /etc/kubernetes/admin.conf
```
Say **Grade CKS-019** when saved.