CKS_FINALE
This commit is contained in:
20
ANSWER.md
Normal file
20
ANSWER.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# CKS-020 — Protect super-admin credentials
|
||||||
|
|
||||||
|
Domain: **2.3 Restrict Kubernetes API access**
|
||||||
|
Time: **1 minute** · Reward: **100 XP + topic mastery**
|
||||||
|
|
||||||
|
## Task
|
||||||
|
|
||||||
|
Secure `/etc/kubernetes/super-admin.conf`, currently `0644 root:root`:
|
||||||
|
|
||||||
|
1. Back it up to `/root/super-admin.conf.backup`.
|
||||||
|
2. Set the live file to mode `0600`.
|
||||||
|
3. Verify the live file with `stat -c '%a %U:%G'`.
|
||||||
|
|
||||||
|
## Section B — scored answer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Your three commands
|
||||||
|
```
|
||||||
|
|
||||||
|
Say **Grade CKS-020** when saved.
|
||||||
264
CKS-SYLLABUS-CHECKLIST.md
Normal file
264
CKS-SYLLABUS-CHECKLIST.md
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
# CKS Mastery Syllabus and Checklist
|
||||||
|
|
||||||
|
Baseline: official six-domain curriculum for the current CKS exam. The Linux Foundation currently advertises a two-hour, performance-based command-line exam. Before exam day, re-check the official page for the Kubernetes version and environment changes.
|
||||||
|
|
||||||
|
Status key: `[ ]` not assessed, `[~]` learning/remediation, `[x]` satisfactory on a fresh first attempt.
|
||||||
|
|
||||||
|
## 0. Exam fluency and prerequisites
|
||||||
|
|
||||||
|
- [ ] Switch and verify `kubectl` contexts before every task.
|
||||||
|
- [ ] SSH to the designated host and distinguish the base node from task hosts.
|
||||||
|
- [ ] Use `kubectl explain`, imperative generators, dry-run YAML, JSONPath, and `yq` efficiently.
|
||||||
|
- [ ] Edit YAML safely with Vim/Nano and validate before applying.
|
||||||
|
- [ ] Inspect static Pod manifests and wait for control-plane recovery.
|
||||||
|
- [ ] Use `crictl`, `systemctl`, `journalctl`, `ss`, `ps`, `find`, `openssl`, `sha256sum`/`sha512sum`, and text-processing tools.
|
||||||
|
- [ ] Back up files before mutation and verify every requested outcome.
|
||||||
|
- [ ] Search permitted documentation quickly without external search results.
|
||||||
|
- [ ] Apply time management: read all requirements, prioritize by weight, verify, and move on.
|
||||||
|
|
||||||
|
## 1. Cluster Setup — 15%
|
||||||
|
|
||||||
|
### 1.1 NetworkPolicy and cluster-level network restriction
|
||||||
|
|
||||||
|
- [x] Explain default-allow behavior and policy isolation triggers.
|
||||||
|
- [x] Build namespace default-deny ingress, egress, and combined policies.
|
||||||
|
- [x] Select exact sources/destinations with `podSelector` and `namespaceSelector`.
|
||||||
|
- [x] Combine selectors correctly (AND within one peer; OR across peer entries).
|
||||||
|
- [x] Allow DNS with the minimum necessary egress.
|
||||||
|
- [x] Restrict ports and protocols without accidentally widening peer access.
|
||||||
|
- [x] Use `ipBlock` and `except`, including cloud metadata endpoint protection.
|
||||||
|
- [x] Account for reply traffic and CNI NetworkPolicy support.
|
||||||
|
- [x] Test allowed and denied flows from representative Pods.
|
||||||
|
|
||||||
|
### 1.2 CIS benchmark review
|
||||||
|
|
||||||
|
- [x] Run `kube-bench` for the correct node/component/configuration.
|
||||||
|
- [x] Interpret PASS, FAIL, WARN, and manual checks.
|
||||||
|
- [x] Map a failed control to kube-apiserver, controller-manager, scheduler, etcd, or kubelet configuration.
|
||||||
|
- [x] Remediate static Pod flags without breaking YAML or mounts.
|
||||||
|
- [x] Remediate kubelet configuration/systemd arguments and restart safely.
|
||||||
|
- [x] Verify component health and rerun the targeted benchmark check.
|
||||||
|
|
||||||
|
### 1.3 Ingress TLS
|
||||||
|
|
||||||
|
- [x] Inspect certificate subject/SAN, issuer, dates, and key/cert correspondence.
|
||||||
|
- [x] Create a TLS Secret in the correct namespace.
|
||||||
|
- [x] Configure `ingressClassName`, TLS hosts, rules, paths, path types, Services, and ports.
|
||||||
|
- [x] Distinguish TLS termination from upstream/mTLS encryption.
|
||||||
|
- [x] Verify HTTPS routing, SNI/Host behavior, certificate presentation, and HTTP exposure.
|
||||||
|
|
||||||
|
### 1.4 Node metadata and endpoint protection
|
||||||
|
|
||||||
|
- [x] Identify link-local metadata endpoints and the associated credential risk.
|
||||||
|
- [x] Block workload access to metadata while preserving required egress.
|
||||||
|
- [x] Recognize host-network and CNI limitations in the threat model.
|
||||||
|
- [x] Minimize exposure of kubelet, etcd, API server, dashboards, and other management endpoints.
|
||||||
|
- [x] Verify externally listening sockets and authentication requirements.
|
||||||
|
|
||||||
|
### 1.5 Platform binary verification
|
||||||
|
|
||||||
|
- [x] Determine the exact version and architecture required.
|
||||||
|
- [x] Download binaries and official checksum material from trusted locations.
|
||||||
|
- [x] Verify SHA-256/SHA-512 checksums before installation.
|
||||||
|
- [x] Detect mismatch and refuse deployment of unverified artifacts.
|
||||||
|
- [x] Check installed binary location/version and preserve permissions/ownership.
|
||||||
|
|
||||||
|
## 2. Cluster Hardening — 15%
|
||||||
|
|
||||||
|
### 2.1 Least-privilege RBAC
|
||||||
|
|
||||||
|
- [x] Enumerate effective permissions with `kubectl auth can-i`, including impersonation.
|
||||||
|
- [x] Trace RoleBinding/ClusterRoleBinding subjects to Role/ClusterRole rules.
|
||||||
|
- [x] Distinguish namespaced and cluster-scoped resources.
|
||||||
|
- [x] Create minimal verbs, API groups, resources, subresources, and `resourceNames` rules.
|
||||||
|
- [x] Choose RoleBinding versus ClusterRoleBinding without unintended cluster-wide access.
|
||||||
|
- [x] Detect wildcards, privilege escalation, bind, impersonate, and secret-reading risks.
|
||||||
|
- [x] Repair overprivileged existing bindings while preserving required access.
|
||||||
|
- [x] Validate allowed and forbidden operations as the target identity.
|
||||||
|
|
||||||
|
### 2.2 Service-account safety
|
||||||
|
|
||||||
|
- [x] Disable token automounting at ServiceAccount and/or Pod level.
|
||||||
|
- [x] Create dedicated ServiceAccounts per workload.
|
||||||
|
- [x] Grant the smallest required RBAC permissions.
|
||||||
|
- [x] Understand projected, time-bound ServiceAccount tokens and TokenRequest.
|
||||||
|
- [x] Detect use of default ServiceAccounts and unnecessary API credentials.
|
||||||
|
- [x] Verify token absence/presence and effective permissions.
|
||||||
|
|
||||||
|
### 2.3 Restrict Kubernetes API access
|
||||||
|
|
||||||
|
- [ ] Harden API server authentication and anonymous access.
|
||||||
|
- [ ] Configure and reason about authorization modes and admission plugins.
|
||||||
|
- [ ] Protect API server network exposure and insecure endpoints.
|
||||||
|
- [ ] Protect kubeconfig files, client certificates, and bootstrap credentials.
|
||||||
|
- [ ] Inspect certificate identity/group mapping and expiration.
|
||||||
|
- [ ] Verify API access from authorized and unauthorized identities/locations.
|
||||||
|
|
||||||
|
### 2.4 Secure Kubernetes upgrades
|
||||||
|
|
||||||
|
- [ ] Read the version-skew policy and choose a supported upgrade path.
|
||||||
|
- [ ] Upgrade kubeadm control plane components in the safe order.
|
||||||
|
- [ ] Drain/uncordon and upgrade worker nodes safely.
|
||||||
|
- [ ] Preserve configuration and verify node/control-plane health.
|
||||||
|
- [ ] Recognize security risk from unsupported or vulnerable versions.
|
||||||
|
|
||||||
|
## 3. System Hardening — 10%
|
||||||
|
|
||||||
|
### 3.1 Minimize host OS footprint
|
||||||
|
|
||||||
|
- [ ] Inventory running services, packages, processes, sockets, and startup units.
|
||||||
|
- [ ] Disable/remove unnecessary services and packages without harming Kubernetes.
|
||||||
|
- [ ] Reduce installed tooling and writable/executable attack surface.
|
||||||
|
- [ ] Verify removed exposure and required component health.
|
||||||
|
|
||||||
|
### 3.2 Least-privilege host identity and access
|
||||||
|
|
||||||
|
- [ ] Inspect users, groups, sudo rules, file ownership, and sensitive permissions.
|
||||||
|
- [ ] Restrict Kubernetes PKI, kubeconfigs, manifests, etcd data, and logs.
|
||||||
|
- [ ] Avoid running services as root where unnecessary.
|
||||||
|
- [ ] Recognize Linux capabilities and remove unnecessary capabilities.
|
||||||
|
|
||||||
|
### 3.3 Minimize external network access
|
||||||
|
|
||||||
|
- [ ] Enumerate listening TCP/UDP ports and owning processes.
|
||||||
|
- [ ] Distinguish required Kubernetes ports by node role.
|
||||||
|
- [ ] Stop/disable unauthorized listeners and validate closure.
|
||||||
|
- [ ] Apply host firewall controls where appropriate without partitioning the cluster.
|
||||||
|
|
||||||
|
### 3.4 Kernel hardening: seccomp and AppArmor
|
||||||
|
|
||||||
|
- [ ] Inspect seccomp/AppArmor availability and loaded profiles on the scheduled node.
|
||||||
|
- [ ] Apply `RuntimeDefault` and Localhost seccomp profiles with current API fields.
|
||||||
|
- [ ] Write/load an AppArmor profile and attach it with current API fields.
|
||||||
|
- [ ] Distinguish enforce, complain, unconfined, RuntimeDefault, and Localhost behavior.
|
||||||
|
- [ ] Schedule workloads only where required local profiles exist.
|
||||||
|
- [ ] Trigger a forbidden action and verify enforcement via status/logs.
|
||||||
|
|
||||||
|
## 4. Minimize Microservice Vulnerabilities — 20%
|
||||||
|
|
||||||
|
### 4.1 Pod Security Standards and security contexts
|
||||||
|
|
||||||
|
- [ ] Distinguish Privileged, Baseline, and Restricted Pod Security Standards.
|
||||||
|
- [ ] Configure Pod Security Admission namespace labels for enforce/audit/warn and versions.
|
||||||
|
- [ ] Set non-root UID/GID, `runAsNonRoot`, and filesystem group deliberately.
|
||||||
|
- [ ] Disable privilege escalation and privileged mode.
|
||||||
|
- [ ] Drop all capabilities and add back only the minimum required.
|
||||||
|
- [ ] Set `readOnlyRootFilesystem` and provide minimal writable volumes.
|
||||||
|
- [ ] Restrict host namespaces, hostPath, host ports, proc mounts, devices, and unsafe sysctls.
|
||||||
|
- [ ] Validate admission behavior and runtime identity/capabilities.
|
||||||
|
|
||||||
|
### 4.2 Secret management and encryption at rest
|
||||||
|
|
||||||
|
- [ ] Avoid secrets in images, command arguments, environment dumps, and plain manifests.
|
||||||
|
- [ ] Create and consume Secrets with minimal exposure and RBAC.
|
||||||
|
- [ ] Mount Secret volumes read-only and understand update behavior.
|
||||||
|
- [ ] Inspect the API server encryption configuration and provider order.
|
||||||
|
- [ ] Configure authenticated encryption plus an intentional fallback provider.
|
||||||
|
- [ ] Mount the encryption configuration into the API server safely.
|
||||||
|
- [ ] Re-encrypt existing Secrets and verify ciphertext directly in etcd.
|
||||||
|
- [ ] Rotate keys/providers without making existing data unreadable.
|
||||||
|
|
||||||
|
### 4.3 Workload isolation and multi-tenancy
|
||||||
|
|
||||||
|
- [ ] Combine namespaces, RBAC, NetworkPolicy, quotas/limits, and Pod Security Admission.
|
||||||
|
- [ ] Identify cross-tenant risks from nodes, volumes, credentials, and cluster-scoped resources.
|
||||||
|
- [ ] Configure RuntimeClass for sandboxed runtimes such as gVisor/Kata when present.
|
||||||
|
- [ ] Apply RuntimeClass scheduling/overhead concepts where required.
|
||||||
|
- [ ] Verify the sandbox runtime rather than trusting Pod status alone.
|
||||||
|
|
||||||
|
### 4.4 Pod-to-Pod encryption
|
||||||
|
|
||||||
|
- [ ] Distinguish NetworkPolicy authorization from encryption in transit.
|
||||||
|
- [ ] Identify when Cilium WireGuard/IPsec or Istio mTLS is appropriate.
|
||||||
|
- [ ] Inspect encryption/mTLS status using tools supplied by the environment.
|
||||||
|
- [ ] Configure or repair workload participation and policies as requested.
|
||||||
|
- [ ] Verify encrypted traffic and avoid claiming encryption from policy alone.
|
||||||
|
|
||||||
|
## 5. Supply Chain Security — 20%
|
||||||
|
|
||||||
|
### 5.1 Minimal and reproducible images
|
||||||
|
|
||||||
|
- [ ] Review Dockerfiles for oversized/untrusted bases, extra packages, shells, secrets, and root execution.
|
||||||
|
- [ ] Use minimal/distroless/multi-stage patterns appropriate to the workload.
|
||||||
|
- [ ] Pin images by digest or controlled version and understand mutability tradeoffs.
|
||||||
|
- [ ] Set a non-root user and minimize filesystem permissions/capabilities.
|
||||||
|
- [ ] Build and functionally test a hardened replacement image.
|
||||||
|
|
||||||
|
### 5.2 Supply-chain understanding and SBOM
|
||||||
|
|
||||||
|
- [ ] Map source, dependencies, build runner, registry, deployment, and runtime trust boundaries.
|
||||||
|
- [ ] Generate and inspect an SBOM with `bom` in the required format.
|
||||||
|
- [ ] Find unexpected/vulnerable components in an SBOM.
|
||||||
|
- [ ] Protect CI credentials and use isolated/ephemeral build workers.
|
||||||
|
- [ ] Recognize provenance, attestations, artifact repositories, and dependency pinning.
|
||||||
|
|
||||||
|
### 5.3 Artifact and registry policy
|
||||||
|
|
||||||
|
- [ ] Restrict permitted registries/repositories without bypass gaps.
|
||||||
|
- [ ] Understand signature/attestation verification at admission.
|
||||||
|
- [ ] Configure or repair supplied admission policy/webhook material.
|
||||||
|
- [ ] Use fail-closed behavior when explicitly required and evaluate availability tradeoffs.
|
||||||
|
- [ ] Test both allowed and denied images and inspect admission errors.
|
||||||
|
|
||||||
|
### 5.4 Static workload and image analysis
|
||||||
|
|
||||||
|
- [ ] Run Kubesec/KubeLinter or supplied static analyzers against manifests.
|
||||||
|
- [ ] Interpret findings and remediate the manifest, not merely silence the tool.
|
||||||
|
- [ ] Scan images with Trivy using requested severity and vulnerability filters.
|
||||||
|
- [ ] Distinguish OS-package and application-dependency vulnerabilities.
|
||||||
|
- [ ] Select a compliant image while preserving workload function.
|
||||||
|
- [ ] Record evidence of scan result and deployed digest/tag.
|
||||||
|
|
||||||
|
## 6. Monitoring, Logging, and Runtime Security — 20%
|
||||||
|
|
||||||
|
### 6.1 Behavioral analytics and Falco
|
||||||
|
|
||||||
|
- [ ] Locate Falco configuration, rule files, service, and output logs.
|
||||||
|
- [ ] Understand rule, condition, output, priority, tags, macros, and lists.
|
||||||
|
- [ ] Use supported event fields to scope host/container/Kubernetes activity.
|
||||||
|
- [ ] Create a precise custom rule with low false-positive risk.
|
||||||
|
- [ ] Reload/restart safely, generate the event, and verify the alert.
|
||||||
|
|
||||||
|
### 6.2 Threat detection across infrastructure and workloads
|
||||||
|
|
||||||
|
- [ ] Investigate suspicious processes, shells, files, sockets, users, and containers.
|
||||||
|
- [ ] Correlate Kubernetes objects with container-runtime/process information.
|
||||||
|
- [ ] Detect crypto-mining, reverse shells, credential access, persistence, and unexpected tooling patterns.
|
||||||
|
- [ ] Preserve evidence and avoid destroying the signal before recording it.
|
||||||
|
|
||||||
|
### 6.3 Attack investigation and attribution
|
||||||
|
|
||||||
|
- [ ] Build a timeline from audit, runtime, host, and workload evidence.
|
||||||
|
- [ ] Identify initial access, execution, persistence, privilege escalation, credential access, lateral movement, and impact.
|
||||||
|
- [ ] Find the responsible identity, source address, user agent, object, command, and affected workload.
|
||||||
|
- [ ] Write exact requested findings to the required path/format.
|
||||||
|
|
||||||
|
### 6.4 Runtime immutability
|
||||||
|
|
||||||
|
- [ ] Enforce read-only root filesystems and provide only necessary writable mounts.
|
||||||
|
- [ ] Prevent privilege escalation and remove mutation tooling/capabilities.
|
||||||
|
- [ ] Understand immutable tags versus image digests and runtime filesystem immutability.
|
||||||
|
- [ ] Verify application health and failed unauthorized writes.
|
||||||
|
|
||||||
|
### 6.5 Kubernetes audit logging
|
||||||
|
|
||||||
|
- [ ] Write ordered audit-policy rules with appropriate levels and stages.
|
||||||
|
- [ ] Scope users, groups, verbs, namespaces, resources, subresources, and non-resource URLs.
|
||||||
|
- [ ] Configure API server audit policy/log flags and host mounts.
|
||||||
|
- [ ] Set log rotation parameters and file permissions.
|
||||||
|
- [ ] Recover the API server and verify audit event generation.
|
||||||
|
- [ ] Query audit JSON efficiently to answer forensic questions.
|
||||||
|
|
||||||
|
## 7. Legacy awareness — not a current mastery gate
|
||||||
|
|
||||||
|
- [ ] Recognize PodSecurityPolicy as removed and translate old exercises to Pod Security Admission/security contexts.
|
||||||
|
- [ ] Recognize deprecated annotations or beta API versions and rewrite them using current fields/APIs.
|
||||||
|
- [ ] Treat community-repository exam prices, versions, weights, and question counts as historical unless confirmed officially.
|
||||||
|
|
||||||
|
## Progress summary
|
||||||
|
|
||||||
|
- Current domain: Cluster Hardening
|
||||||
|
- Current topic: Restrict Kubernetes API access
|
||||||
|
- Active question: CKS-020
|
||||||
|
- Topic first-attempt mastery streak: 0
|
||||||
|
- Completed mastery topics: 7 (Service-account safety completed with direct assistance; verification was skipped)
|
||||||
20
MISSION.md
Normal file
20
MISSION.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Mission: Certified Kubernetes Security Specialist (CKS)
|
||||||
|
|
||||||
|
## Why
|
||||||
|
Pass the CKS exam and develop security skills strong enough to secure and investigate real Kubernetes clusters under severe time pressure.
|
||||||
|
|
||||||
|
## Success looks like
|
||||||
|
- Complete difficult, hands-on CKS tasks accurately from a Linux terminal.
|
||||||
|
- Diagnose and correct security mistakes without being shown the solution.
|
||||||
|
- Solve a fresh, harder-than-CKS question in one attempt for every curriculum topic.
|
||||||
|
- Navigate only exam-permitted documentation efficiently.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
- Use one question at a time.
|
||||||
|
- The learner writes every solution; the teacher never completes an active task.
|
||||||
|
- After an imperfect attempt, address one mistake at a time with progressively stronger hints.
|
||||||
|
- A topic is satisfactory only after a fresh question is solved correctly on the first attempt.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
- Exam dumps, recalled confidential questions, and unauthorized exam assistance.
|
||||||
|
- Memorizing obsolete Kubernetes APIs as if they were current exam objectives.
|
||||||
42
NOTES.md
Normal file
42
NOTES.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Teaching notes
|
||||||
|
|
||||||
|
- Prefer practical, scenario-based tasks that are harder than the real CKS exam.
|
||||||
|
- Present exactly one active question in `ANSWER.md`.
|
||||||
|
- Make answering easy with explicit headings, checkboxes, and fenced blocks.
|
||||||
|
- Do not reveal or implement the solution to an active question.
|
||||||
|
- Review only what the learner wrote in `ANSWER.md`.
|
||||||
|
- On an imperfect submission, create a checklist of observed mistakes, but expose and coach only one mistake at a time.
|
||||||
|
- Hint ladder: (1) goal-oriented nudge, (2) concept pointer, (3) narrow official-doc reference, (4) command/API-shape clue, (5) near-complete scaffold with blanks. Never fill the final answer.
|
||||||
|
- Score after the submitted answer is functionally complete, then require a fresh transfer question before declaring the topic satisfactory if any coaching was needed.
|
||||||
|
- One-attempt success on a fresh question means satisfactory; otherwise continue the same topic with a new question after remediation.
|
||||||
|
- Interleave older mastered skills into later questions for retention.
|
||||||
|
- Keep questions compact: one focused operation, minimal prose, and a short Section B answer area.
|
||||||
|
- For configuration-edit tasks, provide the complete realistic exercise file (or a clearly labeled complete exam-supplied file), not only the line to change. The learner should practice locating the field and preserving structure/indentation.
|
||||||
|
|
||||||
|
## Mastery and scoring protocol
|
||||||
|
|
||||||
|
1. Learner edits `ANSWER.md` and says the attempt is ready.
|
||||||
|
2. Teacher evaluates the answer against an unpublished functional rubric.
|
||||||
|
3. If incomplete, teacher records all detected issues internally but presents only the first unresolved mistake.
|
||||||
|
4. Learner fixes that mistake in the file. Teacher then verifies it and reveals the next mistake, if any.
|
||||||
|
5. Only Section B (the implementation/manifests) is scored, out of 100:
|
||||||
|
- Functional correctness against the requested end state: 70
|
||||||
|
- Security and least privilege: 20
|
||||||
|
- Manifest/API precision: 10
|
||||||
|
Sections A, C, and D are checked for correctness and used for feedback, but contribute zero points.
|
||||||
|
6. If the original attempt earned at least 90 and required no correction, mark the topic satisfactory.
|
||||||
|
7. If correction or hints were needed, give a new, harder transfer question in the same topic. Only a first-attempt score of at least 90 with no critical security error unlocks the next topic.
|
||||||
|
8. Attempts are counted per question; topic status is tracked in `CKS-SYLLABUS-CHECKLIST.md`.
|
||||||
|
|
||||||
|
## Gamification
|
||||||
|
|
||||||
|
- Clean first-attempt completion: 100 XP.
|
||||||
|
- Completion after remediation: 40 XP.
|
||||||
|
- Topic mastery bonus: 150 XP.
|
||||||
|
- Skipped question: 0 XP.
|
||||||
|
- Ranks: Cadet (0), Scout (100), Defender (250), Sentinel (500), Guardian (900), CKS Elite (1400).
|
||||||
|
- Keep `dashboard.html` synchronized whenever a question is graded, completed, skipped, or unlocked.
|
||||||
|
- Ignore harmless spelling/formatting slips when intent and executability remain unambiguous. Still flag errors that break a command, invalidate YAML, change security behavior, or omit a required outcome.
|
||||||
|
- Learner reports strong confidence with Ingress and TLS but frequently forgets or mistypes NGINX Ingress annotations; interleave annotation recall later.
|
||||||
|
- Never skip, archive as incomplete, or advance past an active question unless the learner explicitly uses the word `skip` or clearly says to move to the next topic. Treat `go`, `check`, `verify`, and similar short messages as requests to continue checking the current answer.
|
||||||
|
- Keep tasks close to realistic CKS execution complexity. Do not require defensive shell control flow (`if`, elaborate `&&` chains, scripting) when a candidate can run a verification command, observe its result, and then perform the next command. Hardness should come from Kubernetes security judgment, not incidental shell puzzles.
|
||||||
39
RESOURCES.md
Normal file
39
RESOURCES.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# CKS Resources
|
||||||
|
|
||||||
|
## Knowledge
|
||||||
|
|
||||||
|
- [Linux Foundation: CKS certification and current domains](https://training.linuxfoundation.org/certification/certified-kubernetes-security-specialist/)
|
||||||
|
Authority for domain weights, prerequisites, current exam version, duration, and performance-based format.
|
||||||
|
- [CNCF certification curriculum repository](https://github.com/cncf/curriculum)
|
||||||
|
Authority for the published CKS curriculum. Check for version changes before changing the syllabus.
|
||||||
|
- [Linux Foundation: Important CKS instructions](https://docs.linuxfoundation.org/tc-docs/certification/important-instructions-cks)
|
||||||
|
Authority for task/host rules, exam environment, installed tools, and operational constraints.
|
||||||
|
- [Linux Foundation: Resources allowed during CKS](https://docs.linuxfoundation.org/tc-docs/certification/certification-resources-allowed)
|
||||||
|
Authority for documentation permitted in the exam VM.
|
||||||
|
- [Kubernetes documentation](https://kubernetes.io/docs/)
|
||||||
|
Primary reference for Kubernetes APIs, security concepts, administration, and tasks; permitted during the exam.
|
||||||
|
- [Falco documentation](https://falco.org/docs/)
|
||||||
|
Primary reference for runtime detection and Falco rules; permitted during the exam.
|
||||||
|
- [etcd documentation](https://etcd.io/docs/)
|
||||||
|
Primary reference for etcd operation and verification; permitted during the exam.
|
||||||
|
- [Kubernetes SIGs BOM CLI reference](https://kubernetes-sigs.github.io/bom/cli-reference/)
|
||||||
|
Primary reference for SBOM generation; permitted during the exam.
|
||||||
|
- [Techiescamp CKS certification guide](https://github.com/techiescamp/cks-certification-guide)
|
||||||
|
Community explanations and command examples. Use as practice input, not as the authority for current exam metadata.
|
||||||
|
- [Moabukar CKS exercises](https://github.com/moabukar/CKS-Exercises-Certified-Kubernetes-Security-Specialist)
|
||||||
|
Community exercises across the six domains. Some content is legacy, including PodSecurityPolicy; modernize before reuse.
|
||||||
|
- [Killer.sh CKS challenge series](https://github.com/killer-sh/cks-challenge-series)
|
||||||
|
Public, scenario-oriented practice useful for studying task wording and difficulty without using exam dumps.
|
||||||
|
|
||||||
|
## Wisdom (Communities)
|
||||||
|
|
||||||
|
- [Kubernetes Slack](https://slack.k8s.io/)
|
||||||
|
Practitioner community; use relevant security and certification-preparation channels to discuss concepts, never confidential exam content.
|
||||||
|
- [Kubernetes Discuss](https://discuss.kubernetes.io/)
|
||||||
|
Public community for operational questions and implementation feedback.
|
||||||
|
|
||||||
|
## Source policy
|
||||||
|
|
||||||
|
- Official CNCF/Linux Foundation objectives override community repositories.
|
||||||
|
- Official project documentation overrides remembered syntax and old examples.
|
||||||
|
- Never use or request confidential exam questions or dumps.
|
||||||
278
assets/course.css
Normal file
278
assets/course.css
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: light;
|
||||||
|
--ink: #14213d;
|
||||||
|
--muted: #5f6b7a;
|
||||||
|
--paper: #fffdf8;
|
||||||
|
--canvas: #eef3f8;
|
||||||
|
--line: #d9e1ea;
|
||||||
|
--accent: #087e8b;
|
||||||
|
--accent-dark: #075a63;
|
||||||
|
--warm: #ffb703;
|
||||||
|
--code: #101827;
|
||||||
|
--code-ink: #e6edf7;
|
||||||
|
--shadow: 0 24px 70px rgba(20, 33, 61, 0.12);
|
||||||
|
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
html { scroll-behavior: smooth; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--ink);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 5% 0%, rgba(8, 126, 139, 0.12), transparent 28rem),
|
||||||
|
linear-gradient(180deg, #f7fafc 0%, var(--canvas) 100%);
|
||||||
|
line-height: 1.65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
width: min(1120px, calc(100% - 32px));
|
||||||
|
margin: 32px auto 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: clamp(32px, 6vw, 72px);
|
||||||
|
color: #fff;
|
||||||
|
background: linear-gradient(135deg, #14213d 0%, #0e5262 68%, #087e8b 100%);
|
||||||
|
border-radius: 28px;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 290px;
|
||||||
|
height: 290px;
|
||||||
|
right: -105px;
|
||||||
|
top: -135px;
|
||||||
|
border: 36px solid rgba(255, 183, 3, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
color: #9fe7e9;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
line-height: 1.15;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
max-width: 760px;
|
||||||
|
margin: 0;
|
||||||
|
font-family: Georgia, "Times New Roman", serif;
|
||||||
|
font-size: clamp(2.25rem, 6vw, 5rem);
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.045em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deck {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 22px 0 0;
|
||||||
|
color: #d9f1f2;
|
||||||
|
font-size: clamp(1rem, 2vw, 1.2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
padding: 7px 12px;
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
border: 1px solid rgba(255,255,255,0.22);
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 0.86rem;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) 280px;
|
||||||
|
gap: 24px;
|
||||||
|
margin-top: 24px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: clamp(24px, 4vw, 44px);
|
||||||
|
background: var(--paper);
|
||||||
|
border: 1px solid rgba(217, 225, 234, 0.9);
|
||||||
|
border-radius: 22px;
|
||||||
|
box-shadow: 0 12px 40px rgba(20, 33, 61, 0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card + .card { margin-top: 20px; }
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0 0 20px;
|
||||||
|
font-family: Georgia, "Times New Roman", serif;
|
||||||
|
font-size: clamp(1.55rem, 3vw, 2.2rem);
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 28px 0 10px;
|
||||||
|
font-size: 1.02rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p { margin: 0 0 16px; }
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0.12em 0.35em;
|
||||||
|
background: #e8f0f3;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: "Cascadia Code", "SFMono-Regular", Consolas, monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 18px 0;
|
||||||
|
padding: 18px 20px;
|
||||||
|
color: var(--code-ink);
|
||||||
|
background: var(--code);
|
||||||
|
border-radius: 14px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code { padding: 0; color: inherit; background: transparent; }
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 12px 14px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
th { color: var(--accent-dark); font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; }
|
||||||
|
|
||||||
|
.requirements {
|
||||||
|
padding-left: 0;
|
||||||
|
list-style: none;
|
||||||
|
counter-reset: requirement;
|
||||||
|
}
|
||||||
|
|
||||||
|
.requirements li {
|
||||||
|
position: relative;
|
||||||
|
min-height: 44px;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
padding: 9px 8px 9px 54px;
|
||||||
|
counter-increment: requirement;
|
||||||
|
}
|
||||||
|
|
||||||
|
.requirements li::before {
|
||||||
|
content: counter(requirement);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 5px;
|
||||||
|
display: grid;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
place-items: center;
|
||||||
|
color: #fff;
|
||||||
|
background: var(--accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
background: #fff7df;
|
||||||
|
border-left: 4px solid var(--warm);
|
||||||
|
border-radius: 0 10px 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rail { position: sticky; top: 24px; }
|
||||||
|
|
||||||
|
.rail h2 { font-size: 1.35rem; }
|
||||||
|
.rail .eyebrow { color: var(--accent-dark); }
|
||||||
|
|
||||||
|
.steps {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps li {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 0 22px 28px;
|
||||||
|
color: var(--muted);
|
||||||
|
border-left: 2px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps li::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
left: -6px;
|
||||||
|
top: 6px;
|
||||||
|
background: var(--paper);
|
||||||
|
border: 2px solid #aeb9c7;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps li.active { color: var(--ink); font-weight: 750; }
|
||||||
|
.steps li.active::before { background: var(--warm); border-color: var(--warm); }
|
||||||
|
.steps li:last-child { padding-bottom: 0; border-left-color: transparent; }
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: block;
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 13px 16px;
|
||||||
|
color: #fff;
|
||||||
|
background: var(--accent);
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 750;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover { background: var(--accent-dark); }
|
||||||
|
|
||||||
|
.small { color: var(--muted); font-size: 0.86rem; }
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 28px 8px 0;
|
||||||
|
color: var(--muted);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.86rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
.layout { grid-template-columns: 1fr; }
|
||||||
|
.rail { position: static; order: -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
body { background: #fff; }
|
||||||
|
.page { width: 100%; margin: 0; }
|
||||||
|
.hero, .card { box-shadow: none; }
|
||||||
|
.hero { color: var(--ink); background: #fff; border: 2px solid var(--ink); }
|
||||||
|
.hero .deck, .hero .eyebrow { color: var(--ink); }
|
||||||
|
.pill { color: var(--ink); border-color: var(--ink); }
|
||||||
|
.layout { grid-template-columns: 1fr; }
|
||||||
|
.rail, footer { display: none; }
|
||||||
|
}
|
||||||
74
assets/game.css
Normal file
74
assets/game.css
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
.game-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
margin: 22px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-stat {
|
||||||
|
padding: 18px;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-stat strong { display: block; font-size: 1.65rem; line-height: 1.1; }
|
||||||
|
.game-stat span { color: #c6e8eb; font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; }
|
||||||
|
|
||||||
|
.xp-track {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 12px;
|
||||||
|
margin: 10px 0 6px;
|
||||||
|
background: #dce7ea;
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xp-fill { width: 24%; height: 100%; background: linear-gradient(90deg, var(--accent), var(--warm)); }
|
||||||
|
|
||||||
|
.mission-map { display: grid; gap: 12px; }
|
||||||
|
|
||||||
|
.mission {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 42px 1fr auto;
|
||||||
|
gap: 14px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 14px 0;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission:last-child { border-bottom: 0; }
|
||||||
|
|
||||||
|
.mission-mark {
|
||||||
|
display: grid;
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
place-items: center;
|
||||||
|
color: #fff;
|
||||||
|
background: var(--accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission.locked .mission-mark { color: var(--muted); background: #e4e9ef; }
|
||||||
|
.mission.active .mission-mark { color: var(--ink); background: var(--warm); }
|
||||||
|
.mission h3 { margin: 0 0 2px; }
|
||||||
|
.mission p { margin: 0; color: var(--muted); font-size: 0.88rem; }
|
||||||
|
.mission-status { color: var(--accent-dark); font-size: 0.8rem; font-weight: 800; text-transform: uppercase; }
|
||||||
|
|
||||||
|
.achievement-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
||||||
|
.achievement { padding: 16px; background: #edf7f7; border-radius: 14px; }
|
||||||
|
.achievement.locked { color: var(--muted); background: #f1f3f6; filter: grayscale(1); }
|
||||||
|
.achievement strong { display: block; margin-bottom: 4px; }
|
||||||
|
|
||||||
|
.rules { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 20px; padding: 0; list-style: none; }
|
||||||
|
.rules li { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--line); }
|
||||||
|
.hero-note { color: #d9f1f2; }
|
||||||
|
.light-eyebrow { color: var(--accent-dark); }
|
||||||
|
.light-pill { color: var(--ink); border-color: var(--line); }
|
||||||
|
.button-secondary { background: var(--ink); }
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.game-grid, .achievement-grid, .rules { grid-template-columns: 1fr; }
|
||||||
|
.mission { grid-template-columns: 42px 1fr; }
|
||||||
|
.mission-status { grid-column: 2; }
|
||||||
|
}
|
||||||
73
dashboard.html
Normal file
73
dashboard.html
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CKS Security Quest</title>
|
||||||
|
<link rel="stylesheet" href="assets/course.css"><link rel="stylesheet" href="assets/game.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="page">
|
||||||
|
<header class="hero">
|
||||||
|
<p class="eyebrow">CKS Security Quest</p>
|
||||||
|
<h1>Guardian · 1020 XP</h1>
|
||||||
|
<p class="deck">Advance by completing real security operations. Accuracy earns progress; first-attempt mastery unlocks the next territory.</p>
|
||||||
|
<div class="game-grid">
|
||||||
|
<div class="game-stat"><strong>15</strong><span>Missions completed</span></div>
|
||||||
|
<div class="game-stat"><strong>2 / 4</strong><span>Cluster Hardening covered</span></div>
|
||||||
|
<div class="game-stat"><strong>2</strong><span>Clean wins</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="xp-track" aria-label="120 of 500 XP from Guardian toward CKS Elite"><div class="xp-fill"></div></div>
|
||||||
|
<p class="small hero-note">380 XP to rank: CKS Elite</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
<div>
|
||||||
|
<section class="card">
|
||||||
|
<p class="eyebrow light-eyebrow">Campaign map</p>
|
||||||
|
<h2>Cluster Setup — complete</h2>
|
||||||
|
<div class="mission-map">
|
||||||
|
<div class="mission"><div class="mission-mark">✓</div><div><h3>1.1 NetworkPolicy</h3><p>Covered after guided remediation; scheduled for later review.</p></div><span class="mission-status">Covered</span></div>
|
||||||
|
<div class="mission"><div class="mission-mark">✓</div><div><h3>1.2 CIS benchmark</h3><p>Covered after guided remediation; scheduled for later review.</p></div><span class="mission-status">Covered</span></div>
|
||||||
|
<div class="mission"><div class="mission-mark">✓</div><div><h3>1.3 Ingress TLS</h3><p>Covered; NGINX annotations remain an interleaved review target.</p></div><span class="mission-status">Covered</span></div>
|
||||||
|
<div class="mission"><div class="mission-mark">✓</div><div><h3>1.4 Metadata protection</h3><p>Mastered with a clean first-attempt transfer.</p></div><span class="mission-status">Mastered</span></div>
|
||||||
|
<div class="mission"><div class="mission-mark">✓</div><div><h3>1.5 Binary verification</h3><p>Mastered with clean checksum transfer.</p></div><span class="mission-status">Mastered</span></div>
|
||||||
|
<div class="mission"><div class="mission-mark">✓</div><div><h3>2.1 Least-privilege RBAC</h3><p>Learner-confirmed coverage; scheduled for interleaved review.</p></div><span class="mission-status">Covered</span></div>
|
||||||
|
<div class="mission"><div class="mission-mark">✓</div><div><h3>2.2 Service accounts</h3><p>Completed with assistance; verification review remains.</p></div><span class="mission-status">Covered</span></div>
|
||||||
|
<div class="mission active"><div class="mission-mark">!</div><div><h3>2.3 API access</h3><p>One-minute mastery gate: protect super-admin credentials.</p></div><span class="mission-status">Active</span></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h2>Achievements</h2>
|
||||||
|
<div class="achievement-grid">
|
||||||
|
<div class="achievement"><strong>Policy Tamer</strong><span>Completed a multi-policy containment scenario.</span></div>
|
||||||
|
<div class="achievement"><strong>Recovery Operator</strong><span>Recovered API server and kubelet after hardening.</span></div>
|
||||||
|
<div class="achievement"><strong>Clean Sweep</strong><span>Solved CKS-012 with no correction.</span></div>
|
||||||
|
<div class="achievement"><strong>Domain Defender</strong><span>Completed every Cluster Setup topic.</span></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="rail">
|
||||||
|
<section class="card">
|
||||||
|
<p class="eyebrow light-eyebrow">Active mission</p>
|
||||||
|
<h2>CKS-020</h2>
|
||||||
|
<p>Back up, secure, and verify the super-admin kubeconfig.</p>
|
||||||
|
<div class="meta"><span class="pill light-pill">1 min</span><span class="pill light-pill">250 XP available</span></div>
|
||||||
|
<a class="button" href="ANSWER.md">Start mission</a>
|
||||||
|
<a class="button button-secondary" href="lessons/0020-protect-super-admin.html">Read mission card</a>
|
||||||
|
</section>
|
||||||
|
<section class="card">
|
||||||
|
<h2>XP rules</h2>
|
||||||
|
<ul class="rules">
|
||||||
|
<li><span>Clean win</span><strong>+100</strong></li>
|
||||||
|
<li><span>Corrected win</span><strong>+40</strong></li>
|
||||||
|
<li><span>Topic mastery</span><strong>+150</strong></li>
|
||||||
|
<li><span>Skipped task</span><strong>+0</strong></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
learning-records/0001-networkpolicy-progress.md
Normal file
3
learning-records/0001-networkpolicy-progress.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# NetworkPolicy topic accepted by learner
|
||||||
|
|
||||||
|
The learner produced a functionally correct policy set after guided remediation, then chose to move to CIS benchmark review instead of completing the fresh first-attempt transfer gate. Treat NetworkPolicy as covered by learner request, but interleave it into later exercises because first-attempt mastery was not demonstrated.
|
||||||
3
learning-records/0002-cis-benchmark-progress.md
Normal file
3
learning-records/0002-cis-benchmark-progress.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# CIS benchmark topic accepted by learner
|
||||||
|
|
||||||
|
The learner successfully remediated kube-apiserver and kubelet CIS findings after guided corrections, including backup placement, service recovery, host context, targeted reruns, and socket verification. The learner chose to move on before a clean first-attempt transfer, so interleave CIS remediation later.
|
||||||
3
learning-records/0003-ingress-tls-confidence.md
Normal file
3
learning-records/0003-ingress-tls-confidence.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Ingress and TLS confidence established
|
||||||
|
|
||||||
|
The learner produced a correct TLS Secret command and final TLS-enabled Ingress, and reports strong confidence with Ingress/TLS. Certificate inspection and Secret verification were omitted in the submitted workflow; NGINX annotation recall is the learner's stated weak point and should be interleaved later.
|
||||||
3
learning-records/0004-metadata-protection-mastered.md
Normal file
3
learning-records/0004-metadata-protection-mastered.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Metadata endpoint protection mastered
|
||||||
|
|
||||||
|
The learner independently produced a correct egress-isolating NetworkPolicy that retained HTTPS access while excluding the link-local cloud metadata endpoint. CKS-012 scored 100/100 on the first attempt, providing clean transfer evidence.
|
||||||
3
learning-records/0005-binary-verification-mastered.md
Normal file
3
learning-records/0005-binary-verification-mastered.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Platform binary verification mastered
|
||||||
|
|
||||||
|
The learner correctly distinguished raw checksum files from standard checksum records, verified SHA-256/SHA-512 data, installed binaries with explicit ownership/mode, and independently completed CKS-015 on the first attempt.
|
||||||
3
learning-records/0006-rbac-prior-knowledge.md
Normal file
3
learning-records/0006-rbac-prior-knowledge.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# RBAC confidence reported
|
||||||
|
|
||||||
|
The learner reports being comfortable with Kubernetes RBAC and requested advancement without attempting CKS-016. Treat RBAC as covered but not independently demonstrated in this workspace; interleave least-privilege and subresource checks later.
|
||||||
3
learning-records/0007-service-account-assisted.md
Normal file
3
learning-records/0007-service-account-assisted.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Service-account token safety completed with assistance
|
||||||
|
|
||||||
|
The learner reviewed a correct dedicated ServiceAccount and CronJob configuration with token automounting disabled at both levels. The final manifests were applied from a combined file; verification was explicitly skipped, so schedule a later interleaved check of projected token absence.
|
||||||
89
lessons/0001-networkpolicy-containment.html
Normal file
89
lessons/0001-networkpolicy-containment.html
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="A hard, hands-on CKS NetworkPolicy mastery question.">
|
||||||
|
<title>CKS-001 · NetworkPolicy Containment</title>
|
||||||
|
<link rel="stylesheet" href="../assets/course.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="page">
|
||||||
|
<header class="hero">
|
||||||
|
<p class="eyebrow">CKS mastery journey · Question 001</p>
|
||||||
|
<h1>NetworkPolicy containment under pressure</h1>
|
||||||
|
<p class="deck">Build a precise default-deny posture, preserve only required flows, and prove that the cloud metadata endpoint remains unreachable.</p>
|
||||||
|
<div class="meta" aria-label="Question metadata">
|
||||||
|
<span class="pill">Cluster Setup</span>
|
||||||
|
<span class="pill">Attempt 1</span>
|
||||||
|
<span class="pill">12 minutes</span>
|
||||||
|
<span class="pill">Harder than CKS</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
<div>
|
||||||
|
<section class="card" aria-labelledby="scenario">
|
||||||
|
<h2 id="scenario">Scenario</h2>
|
||||||
|
<p>Begin by selecting the designated cluster:</p>
|
||||||
|
<pre><code>kubectl config use-context prod-sec</code></pre>
|
||||||
|
<p>Namespace <code>payments</code> already contains these Pods:</p>
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Pod</th><th>Labels</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><code>api-0</code></td><td><code>app=api</code>, <code>tier=backend</code></td></tr>
|
||||||
|
<tr><td><code>worker-0</code></td><td><code>app=worker</code>, <code>tier=backend</code></td></tr>
|
||||||
|
<tr><td><code>debug-0</code></td><td><code>app=debug</code></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>Namespace <code>gateway</code> has label <code>access=payments</code> and contains Pods labeled <code>app=ingress</code>.</p>
|
||||||
|
<p>Namespace <code>observability</code> has label <code>team=monitoring</code> and contains Pods labeled <code>app=prometheus</code>.</p>
|
||||||
|
<p>The cluster DNS Pods are in <code>kube-system</code> with label <code>k8s-app=kube-dns</code>. The cloud instance-metadata endpoint is <code>169.254.169.254/32</code>.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card" aria-labelledby="outcome">
|
||||||
|
<h2 id="outcome">Required end state</h2>
|
||||||
|
<ol class="requirements">
|
||||||
|
<li>Every Pod in <code>payments</code> is isolated for both ingress and egress.</li>
|
||||||
|
<li>Only <code>gateway</code> Pods labeled <code>app=ingress</code> may connect to TCP port <code>8443</code> on Pods labeled <code>app=api</code>.</li>
|
||||||
|
<li>Only <code>observability</code> Pods labeled <code>app=prometheus</code> may scrape TCP port <code>9090</code> on Pods labeled <code>tier=backend</code>.</li>
|
||||||
|
<li><code>api</code> and <code>worker</code> Pods may query cluster DNS over UDP and TCP port <code>53</code>; <code>debug-0</code> must have no egress, including DNS.</li>
|
||||||
|
<li>Pods labeled <code>app=api</code> may make TCP connections to any IPv4 destination on port <code>443</code>, except <code>169.254.169.254/32</code>.</li>
|
||||||
|
<li>No other ingress or egress is permitted.</li>
|
||||||
|
<li>Do not modify Pods, namespaces, Services, or existing labels.</li>
|
||||||
|
</ol>
|
||||||
|
<div class="note"><strong>Environment:</strong> Assume the installed CNI enforces Kubernetes <code>NetworkPolicy</code>. Do not use Cilium-specific resources.</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card" aria-labelledby="deliverables">
|
||||||
|
<h2 id="deliverables">Your deliverables</h2>
|
||||||
|
<h3>A · Context and pre-checks</h3>
|
||||||
|
<p>Record the commands you would use to confirm the context, labels, Pods, namespaces, and existing policy state.</p>
|
||||||
|
<h3>B · Complete manifest set</h3>
|
||||||
|
<p>Provide every NetworkPolicy needed to achieve the required end state.</p>
|
||||||
|
<h3>C · Verification evidence</h3>
|
||||||
|
<p>Include at least one positive and one negative test for each distinct rule. Explicitly prove that metadata access is denied and <code>debug-0</code> cannot use DNS.</p>
|
||||||
|
<h3>D · Selector reasoning</h3>
|
||||||
|
<p>Explain why the policies do not accidentally allow every Pod in an approved namespace, every port from an approved source, metadata on another port, or DNS from <code>debug-0</code>.</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="rail card" aria-labelledby="journey">
|
||||||
|
<p class="eyebrow">Mastery loop</p>
|
||||||
|
<h2 id="journey">Your journey</h2>
|
||||||
|
<ol class="steps">
|
||||||
|
<li class="active">Solve CKS-001</li>
|
||||||
|
<li>Submit attempt</li>
|
||||||
|
<li>Repair one mistake</li>
|
||||||
|
<li>Earn a score</li>
|
||||||
|
<li>Pass fresh transfer task</li>
|
||||||
|
</ol>
|
||||||
|
<a class="button" href="../ANSWER.md">Open answer file</a>
|
||||||
|
<p class="small">Edit only the Markdown answer file. This page remains your clean question sheet.</p>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>CKS Mastery Journey · Ask your teacher about anything unclear, but remember: hints become stronger gradually.</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
54
lessons/0002-networkpolicy-two-sided-isolation.html
Normal file
54
lessons/0002-networkpolicy-two-sided-isolation.html
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="A harder-than-CKS NetworkPolicy transfer question.">
|
||||||
|
<title>CKS-002 · Two-Sided Isolation</title>
|
||||||
|
<link rel="stylesheet" href="../assets/course.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="page">
|
||||||
|
<header class="hero">
|
||||||
|
<p class="eyebrow">CKS mastery journey · Fresh transfer</p>
|
||||||
|
<h1>Two-sided NetworkPolicy isolation</h1>
|
||||||
|
<p class="deck">Secure current and future workloads while composing namespace, Pod, port, DNS, and CIDR restrictions without unintended additive access.</p>
|
||||||
|
<div class="meta"><span class="pill">Cluster Setup</span><span class="pill">CKS-002</span><span class="pill">15 minutes</span><span class="pill">Section B scored</span></div>
|
||||||
|
</header>
|
||||||
|
<div class="layout">
|
||||||
|
<div>
|
||||||
|
<section class="card">
|
||||||
|
<h2>Scenario</h2>
|
||||||
|
<pre><code>kubectl config use-context secure-prod</code></pre>
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Pod</th><th>Labels</th><th>Listens on</th></tr></thead>
|
||||||
|
<tbody><tr><td><code>web-0</code></td><td><code>app=web</code></td><td>TCP 443</td></tr><tr><td><code>api-0</code></td><td><code>app=api</code></td><td>TCP 8080</td></tr><tr><td><code>batch-0</code></td><td><code>app=batch</code></td><td>none</td></tr></tbody>
|
||||||
|
</table>
|
||||||
|
<p><code>edge</code> is labeled <code>zone=trusted-edge</code>; approved clients are <code>role=proxy</code>. <code>monitoring</code> is labeled <code>purpose=metrics</code>; approved clients are <code>role=scraper</code>.</p>
|
||||||
|
<p>DNS Pods are in <code>kube-system</code> with <code>k8s-app=kube-dns</code>. Database CIDR is <code>10.40.0.0/16</code>; <code>10.40.8.0/24</code> is forbidden. Future unlabeled Pods may appear in <code>blue</code>.</p>
|
||||||
|
</section>
|
||||||
|
<section class="card">
|
||||||
|
<h2>Required end state</h2>
|
||||||
|
<ol class="requirements">
|
||||||
|
<li>Default-deny ingress and egress for every current and future Pod in <code>blue</code>.</li>
|
||||||
|
<li>Trusted edge proxies alone may reach <code>web</code> on TCP 443.</li>
|
||||||
|
<li>Only <code>web</code> may reach <code>api</code> on TCP 8080.</li>
|
||||||
|
<li>Approved monitoring scrapers may reach <code>web</code> and <code>api</code> on TCP 9100.</li>
|
||||||
|
<li>Only <code>web</code> and <code>api</code> may query cluster DNS over UDP/TCP 53.</li>
|
||||||
|
<li>Only <code>api</code> may reach the database CIDR on TCP 5432, excluding its reserved subnet.</li>
|
||||||
|
<li><code>batch</code> has no traffic; no other traffic is permitted.</li>
|
||||||
|
<li>Do not modify existing objects or use CNI-specific policy kinds.</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<aside class="rail card">
|
||||||
|
<p class="eyebrow">Mastery gate</p><h2>One clean attempt</h2>
|
||||||
|
<ol class="steps"><li class="active">Write Section B</li><li>Submit once</li><li>Score implementation</li><li>Unlock or remediate</li></ol>
|
||||||
|
<a class="button" href="../ANSWER.md">Open answer file</a>
|
||||||
|
<p class="small">Only your manifests are scored. The checklist is a private self-check.</p>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<footer>CKS Mastery Journey · Question 002</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
34
lessons/0003-cis-apiserver-authorization.html
Normal file
34
lessons/0003-cis-apiserver-authorization.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CKS-003 · CIS API Server Repair</title>
|
||||||
|
<link rel="stylesheet" href="../assets/course.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="page">
|
||||||
|
<header class="hero">
|
||||||
|
<p class="eyebrow">CKS mastery journey · 1.2 CIS benchmark</p>
|
||||||
|
<h1>Repair one API-server failure</h1>
|
||||||
|
<p class="deck">Make a minimal static-Pod correction, recover the control plane, and prove the targeted CIS check passes.</p>
|
||||||
|
<div class="meta"><span class="pill">CKS-003</span><span class="pill">6 minutes</span><span class="pill">Focused task</span></div>
|
||||||
|
</header>
|
||||||
|
<div class="layout">
|
||||||
|
<section class="card">
|
||||||
|
<h2>Task</h2>
|
||||||
|
<pre><code>[FAIL] 1.2.7 Ensure that the --authorization-mode argument is not set to AlwaysAllow</code></pre>
|
||||||
|
<p>On <code>controlplane</code>, edit <code>/etc/kubernetes/manifests/kube-apiserver.yaml</code>.</p>
|
||||||
|
<p>The current line is:</p>
|
||||||
|
<pre><code>- --authorization-mode=AlwaysAllow,RBAC</code></pre>
|
||||||
|
<p>Back up the manifest. Change only what is necessary so the modes are exactly <code>Node,RBAC</code>. Verify API-server recovery and rerun check <code>1.2.7</code>.</p>
|
||||||
|
</section>
|
||||||
|
<aside class="rail card">
|
||||||
|
<p class="eyebrow">Answer</p><h2>Keep it short</h2>
|
||||||
|
<p>Submit only the commands and corrected manifest line.</p>
|
||||||
|
<a class="button" href="../ANSWER.md">Open answer file</a>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<footer>CKS Mastery Journey · Question 003</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
30
lessons/0004-cis-kubelet-anonymous-auth.html
Normal file
30
lessons/0004-cis-kubelet-anonymous-auth.html
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CKS-004 · Kubelet CIS Repair</title><link rel="stylesheet" href="../assets/course.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="page">
|
||||||
|
<header class="hero">
|
||||||
|
<p class="eyebrow">CKS mastery journey · CIS transfer</p>
|
||||||
|
<h1>Repair one kubelet failure</h1>
|
||||||
|
<p class="deck">One configuration change, one recovery check, one targeted benchmark.</p>
|
||||||
|
<div class="meta"><span class="pill">CKS-004</span><span class="pill">5 minutes</span></div>
|
||||||
|
</header>
|
||||||
|
<div class="layout">
|
||||||
|
<section class="card">
|
||||||
|
<h2>Task</h2>
|
||||||
|
<pre><code>[FAIL] 4.2.1 Ensure that the --anonymous-auth argument is set to false</code></pre>
|
||||||
|
<p>On <code>node01</code>, back up <code>/var/lib/kubelet/config.yaml</code> outside its directory. Change <code>authentication.anonymous.enabled: true</code> to the secure value.</p>
|
||||||
|
<p>Restart the correct service, verify the node becomes <code>Ready</code>, and rerun only check <code>4.2.1</code> for the node target.</p>
|
||||||
|
</section>
|
||||||
|
<aside class="rail card">
|
||||||
|
<p class="eyebrow">Fresh attempt</p><h2>Keep it compact</h2>
|
||||||
|
<p>Only commands and corrected YAML are required.</p>
|
||||||
|
<a class="button" href="../ANSWER.md">Open answer file</a>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
29
lessons/0005-cis-kubelet-readonly-port.html
Normal file
29
lessons/0005-cis-kubelet-readonly-port.html
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CKS-005 · Close Kubelet Read-Only Port</title><link rel="stylesheet" href="../assets/course.css"></head>
|
||||||
|
<body><main class="page"><header class="hero"><p class="eyebrow">CKS mastery journey · Final CIS transfer</p>
|
||||||
|
<h1>Close one unsafe port</h1><p class="deck">A four-minute kubelet hardening task.</p>
|
||||||
|
<div class="meta"><span class="pill">CKS-005</span><span class="pill">4 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><p>On <code>node02</code>, locate and disable the insecure read-only port in this complete exercise file, <code>/var/lib/kubelet/config.yaml</code>:</p>
|
||||||
|
<pre><code>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</code></pre>
|
||||||
|
<p>Back up first, restart kubelet, prove TCP 10255 is not listening, rerun only CIS check <code>4.2.4</code> on the node, and verify <code>node02</code> is <code>Ready</code> from the original host.</p></section>
|
||||||
|
<aside class="rail card"><p class="eyebrow">Fresh attempt</p><h2>Four minutes</h2><p>Submit commands and corrected YAML only.</p><a class="button" href="../ANSWER.md">Open answer file</a></aside></div></main></body></html>
|
||||||
27
lessons/0006-cis-kubelet-authorization.html
Normal file
27
lessons/0006-cis-kubelet-authorization.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CKS-006 · Kubelet Authorization</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS mastery journey · CIS final transfer</p><h1>Correct kubelet authorization</h1>
|
||||||
|
<p class="deck">Find one insecure field in a complete configuration and perform the full remediation safely.</p><div class="meta"><span class="pill">CKS-006</span><span class="pill">4 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Complete exercise file</h2><p><code>/var/lib/kubelet/config.yaml</code> on <code>node03</code>:</p>
|
||||||
|
<pre><code>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</code></pre>
|
||||||
|
<p>Back up outside the directory, set authorization to <code>Webhook</code>, restart/check kubelet, rerun node check <code>4.2.2</code>, then verify <code>node03</code> is Ready from the original host.</p></section>
|
||||||
|
<aside class="rail card"><p class="eyebrow">Fresh attempt</p><h2>Four minutes</h2><p>Commands and corrected YAML only.</p><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
8
lessons/0007-cis-manifest-permissions.html
Normal file
8
lessons/0007-cis-manifest-permissions.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CKS-007 · Manifest Permissions</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Clean-win mission</p><h1>Repair manifest permissions</h1>
|
||||||
|
<p class="deck">Three minutes. One file. One targeted CIS proof.</p><div class="meta"><span class="pill">CKS-007</span><span class="pill">100 XP</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><pre><code>/etc/kubernetes/manifests/kube-controller-manager.yaml
|
||||||
|
mode=0644 owner=root group=root</code></pre>
|
||||||
|
<p>On <code>controlplane</code>, back up outside the manifest directory, set mode <code>0600</code> without changing ownership, verify both, and rerun only master check <code>1.1.3</code>.</p></section>
|
||||||
|
<aside class="rail card"><p class="eyebrow">Mastery gate</p><h2>Clean win</h2><p>Commands only.</p><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
8
lessons/0008-cis-manifest-ownership.html
Normal file
8
lessons/0008-cis-manifest-ownership.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CKS-008 · Manifest Ownership</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Boss gate</p><h1>Correct manifest ownership</h1><p class="deck">Two minutes. Four commands. Unlock Ingress TLS.</p>
|
||||||
|
<div class="meta"><span class="pill">CKS-008</span><span class="pill">250 XP available</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><pre><code>/etc/kubernetes/manifests/kube-scheduler.yaml
|
||||||
|
mode=0600 owner=root group=adm</code></pre>
|
||||||
|
<ol class="requirements"><li>Back up outside the manifest directory.</li><li>Set owner and group to root without changing mode.</li><li>Verify numeric mode and owner:group.</li><li>Rerun only master check 1.1.4.</li></ol></section>
|
||||||
|
<aside class="rail card"><p class="eyebrow">Boss gate</p><h2>Four commands</h2><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
27
lessons/0009-ingress-tls.html
Normal file
27
lessons/0009-ingress-tls.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CKS-009 · Ingress TLS</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Ingress TLS</p><h1>Secure an existing route</h1><p class="deck">Inspect the certificate, create the Secret, and add TLS without breaking routing.</p>
|
||||||
|
<div class="meta"><span class="pill">CKS-009</span><span class="pill">6 minutes</span></div></header>
|
||||||
|
<div class="layout"><div><section class="card"><h2>Supplied files</h2><pre><code>/opt/certs/store.crt
|
||||||
|
/opt/certs/store.key</code></pre></section>
|
||||||
|
<section class="card"><h2>Complete existing Ingress</h2><pre><code>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</code></pre></section></div>
|
||||||
|
<aside class="rail card"><p class="eyebrow">Mission</p><h2>Four actions</h2><p>Inspect certificate, create TLS Secret, add TLS, verify resources.</p><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
4
lessons/0010-dashboard-endpoint.html
Normal file
4
lessons/0010-dashboard-endpoint.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-010 · Dashboard Endpoint</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Endpoint protection</p><h1>Contain the dashboard</h1><p class="deck">Remove public exposure and cluster-wide administrative privilege.</p><div class="meta"><span class="pill">CKS-010</span><span class="pill">4 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><p>The supplied dashboard Service is <code>NodePort</code> on <code>30443</code>. ClusterRoleBinding <code>dashboard-admin</code> grants its ServiceAccount <code>cluster-admin</code>.</p>
|
||||||
|
<p>Keep the dashboard reachable inside the cluster, remove cluster-admin access, and verify both outcomes. Modify/delete only what is necessary.</p></section><aside class="rail card"><p class="eyebrow">Mission</p><h2>Two exposures</h2><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../reference/nginx-ingress-annotations.html">Annotation card</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
4
lessons/0011-binary-verification.html
Normal file
4
lessons/0011-binary-verification.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-011 · Binary Verification</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Binary verification</p><h1>Trust, then install</h1><p class="deck">Verify provenance before a binary reaches a privileged path.</p><div class="meta"><span class="pill">CKS-011</span><span class="pill">4 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><pre><code>/opt/release/kubectl
|
||||||
|
/opt/release/kubectl.sha256</code></pre><p>The checksum file contains a raw official SHA-256 value without a filename.</p><ol class="requirements"><li>Verify the binary.</li><li>Install only on success as root:root and 0755.</li><li>Show client version.</li><li>Show installed owner and mode.</li></ol></section><aside class="rail card"><p class="eyebrow">Mission</p><h2>Four minutes</h2><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
4
lessons/0012-block-cloud-metadata.html
Normal file
4
lessons/0012-block-cloud-metadata.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-012 · Block Metadata</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Metadata transfer</p><h1>Block cloud metadata</h1><p class="deck">Keep HTTPS egress, exclude one credential-bearing endpoint.</p><div class="meta"><span class="pill">CKS-012</span><span class="pill">3 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><p>In <code>build</code>, isolate egress for <code>role=runner</code> Pods. Allow TCP 443 to any IPv4 destination except <code>169.254.169.254/32</code>. Permit nothing else.</p></section>
|
||||||
|
<aside class="rail card"><p class="eyebrow">Clean gate</p><h2>One manifest</h2><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
4
lessons/0013-kubelet-binary.html
Normal file
4
lessons/0013-kubelet-binary.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-013 · Kubelet Binary</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Binary transfer</p><h1>Verify the kubelet</h1><p class="deck">Two minutes. Verify, install, inspect.</p><div class="meta"><span class="pill">CKS-013</span><span class="pill">No shell puzzle</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><pre><code>/opt/release/kubelet
|
||||||
|
/opt/release/kubelet.sha256</code></pre><ol class="requirements"><li>Verify the raw checksum with <code>sha256sum --check</code>.</li><li>After observing OK, use <code>install</code> for root:root mode 0755.</li><li>Show version and final mode/owner.</li></ol></section><aside class="rail card"><p class="eyebrow">Clean gate</p><h2>Three steps</h2><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
4
lessons/0014-kube-apiserver-checksum.html
Normal file
4
lessons/0014-kube-apiserver-checksum.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-014 · SHA-512 Check</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · One-command gate</p><h1>Verify kube-apiserver</h1><p class="deck">Feed a raw checksum and binary path to sha512sum.</p><div class="meta"><span class="pill">CKS-014</span><span class="pill">1 minute</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Files</h2><pre><code>/opt/kubernetes/kube-apiserver
|
||||||
|
/opt/kubernetes/kube-apiserver.sha512</code></pre><p>Submit one <code>sha512sum --check</code> command.</p></section><aside class="rail card"><p class="eyebrow">Mastery gate</p><h2>One command</h2><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
4
lessons/0015-standard-checksum.html
Normal file
4
lessons/0015-standard-checksum.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-015 · Standard Checksum</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Final binary gate</p><h1>Verify standard checksum</h1><p class="deck">No string construction. Just verify the supplied record.</p><div class="meta"><span class="pill">CKS-015</span><span class="pill">30 seconds</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><pre><code>/opt/release/kube-scheduler
|
||||||
|
/opt/release/kube-scheduler.sha256</code></pre><p>The checksum file already contains the hash and filename in standard format. Verify it.</p></section><aside class="rail card"><p class="eyebrow">Mastery gate</p><h2>One check</h2><a class="button" href="../ANSWER.md">Open answer file</a></aside></div></main></body></html>
|
||||||
3
lessons/0016-rbac-least-privilege.html
Normal file
3
lessons/0016-rbac-least-privilege.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-016 · Least Privilege RBAC</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Cluster Hardening</p><h1>Replace cluster-admin</h1><p class="deck">Give one user exact observation rights in one namespace.</p><div class="meta"><span class="pill">CKS-016</span><span class="pill">5 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><p>Delete ClusterRoleBinding <code>maya-admin</code>. In <code>blue</code>, create Role <code>maya-observer</code> and matching RoleBinding so user <code>maya</code> can get/list/watch Pods and get Pod logs—nothing more.</p><p>Verify one allowed operation and denied Secret access.</p></section><aside class="rail card"><p class="eyebrow">New domain</p><h2>Least privilege</h2><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
3
lessons/0017-service-account-token.html
Normal file
3
lessons/0017-service-account-token.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-017 · ServiceAccount Safety</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · Service accounts</p><h1>Remove unnecessary API tokens</h1><p class="deck">A dedicated identity with token automounting disabled at both layers.</p><div class="meta"><span class="pill">CKS-017</span><span class="pill">5 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><p>Namespace <code>jobs</code> contains CronJob <code>reporter</code>, which does not use the Kubernetes API.</p><ol class="requirements"><li>Create dedicated ServiceAccount <code>reporter</code>.</li><li>Disable automounting on the ServiceAccount.</li><li>Use it in the complete CronJob Pod template and explicitly disable automounting there.</li><li>Apply and verify both settings.</li></ol></section><aside class="rail card"><p class="eyebrow">Mission</p><h2>Least token exposure</h2><a class="button" href="../ANSWER.md">Open answer file</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
3
lessons/0018-disable-anonymous-api.html
Normal file
3
lessons/0018-disable-anonymous-api.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-018 · Disable Anonymous API</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · API access</p><h1>Reject anonymous clients</h1><p class="deck">Change one static-Pod flag, recover safely, and test both authenticated and anonymous paths.</p><div class="meta"><span class="pill">CKS-018</span><span class="pill">5 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><p>The complete exercise manifest is in <code>ANSWER.md</code>. Back it up outside the static-manifest directory, change <code>--anonymous-auth=true</code>, wait for recovery, verify <code>/readyz</code> with kubectl, and confirm an unauthenticated HTTPS request returns 401.</p></section><aside class="rail card"><p class="eyebrow">Mission</p><h2>One security flag</h2><a class="button" href="../ANSWER.md">Open full task</a><a class="button" href="../dashboard.html">Quest dashboard</a></aside></div></main></body></html>
|
||||||
4
lessons/0019-protect-admin-kubeconfig.html
Normal file
4
lessons/0019-protect-admin-kubeconfig.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-019 · Protect Kubeconfig</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · API access</p><h1>Protect admin credentials</h1><p class="deck">Three commands to secure a high-privilege kubeconfig.</p><div class="meta"><span class="pill">CKS-019</span><span class="pill">2 minutes</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><pre><code>/etc/kubernetes/admin.conf
|
||||||
|
mode=0644 owner=root group=root</code></pre><ol class="requirements"><li>Back up to <code>/root/admin.conf.backup</code>.</li><li>Set mode 0600 only.</li><li>Verify numeric mode and owner:group.</li></ol></section><aside class="rail card"><p class="eyebrow">Clean gate</p><h2>Three commands</h2><a class="button" href="../ANSWER.md">Open answer file</a></aside></div></main></body></html>
|
||||||
3
lessons/0020-protect-super-admin.html
Normal file
3
lessons/0020-protect-super-admin.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>CKS-020 · Protect Super Admin</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS Security Quest · One-minute gate</p><h1>Protect super-admin</h1><p class="deck">Back up, secure, verify.</p><div class="meta"><span class="pill">CKS-020</span><span class="pill">3 commands</span></div></header>
|
||||||
|
<div class="layout"><section class="card"><h2>Task</h2><ol class="requirements"><li>Back up the live kubeconfig to the specified path.</li><li>Set the live file to 0600.</li><li>Run the supplied stat format against the live file.</li></ol></section><aside class="rail card"><p class="eyebrow">Mastery gate</p><h2>Exact targets</h2><a class="button" href="../ANSWER.md">Open answer file</a></aside></div></main></body></html>
|
||||||
25
queued/CKS-011.md
Normal file
25
queued/CKS-011.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# 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
|
||||||
|
# Your commands
|
||||||
|
```
|
||||||
|
|
||||||
|
Say **Grade CKS-011** when saved.
|
||||||
15
reference/nginx-ingress-annotations.html
Normal file
15
reference/nginx-ingress-annotations.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>NGINX Ingress Annotation Field Card</title><link rel="stylesheet" href="../assets/course.css"></head><body><main class="page">
|
||||||
|
<header class="hero"><p class="eyebrow">CKS field card</p><h1>NGINX annotations</h1><p class="deck">Default prefix: <code>nginx.ingress.kubernetes.io/</code>. Annotation values are strings—quote booleans and numbers.</p></header>
|
||||||
|
<section class="card"><h2>High-value recall set</h2><table><thead><tr><th>Intent</th><th>Annotation</th><th>Example value</th></tr></thead><tbody>
|
||||||
|
<tr><td>Redirect HTTP when TLS exists</td><td><code>ssl-redirect</code></td><td><code>"true"</code></td></tr>
|
||||||
|
<tr><td>Force redirect with external TLS offload</td><td><code>force-ssl-redirect</code></td><td><code>"true"</code></td></tr>
|
||||||
|
<tr><td>Encrypt NGINX-to-backend traffic</td><td><code>backend-protocol</code></td><td><code>"HTTPS"</code></td></tr>
|
||||||
|
<tr><td>Allow client CIDRs</td><td><code>whitelist-source-range</code></td><td><code>"10.0.0.0/8"</code></td></tr>
|
||||||
|
<tr><td>Rewrite matched URI</td><td><code>rewrite-target</code></td><td><code>/$2</code></td></tr>
|
||||||
|
<tr><td>Limit request body</td><td><code>proxy-body-size</code></td><td><code>"8m"</code></td></tr>
|
||||||
|
<tr><td>Enable regex paths</td><td><code>use-regex</code></td><td><code>"true"</code></td></tr>
|
||||||
|
<tr><td>TLS passthrough</td><td><code>ssl-passthrough</code></td><td><code>"true"</code></td></tr>
|
||||||
|
</tbody></table>
|
||||||
|
<div class="note"><strong>Security caution:</strong> snippet annotations can be critical-risk. TLS passthrough requires controller support and bypasses normal HTTP-layer annotation behavior.</div>
|
||||||
|
<p><a href="https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/">Official annotation reference</a> · <a href="https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations-risk/">Official risk table</a></p></section></main></body></html>
|
||||||
197
submissions/CKS-001-attempt-1.md
Normal file
197
submissions/CKS-001-attempt-1.md
Normal 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**.
|
||||||
63
submissions/CKS-002-skipped.md
Normal file
63
submissions/CKS-002-skipped.md
Normal 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**.
|
||||||
43
submissions/CKS-003-completed.md
Normal file
43
submissions/CKS-003-completed.md
Normal 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**.
|
||||||
43
submissions/CKS-004-completed.md
Normal file
43
submissions/CKS-004-completed.md
Normal 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**.
|
||||||
51
submissions/CKS-005-completed.md
Normal file
51
submissions/CKS-005-completed.md
Normal 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**.
|
||||||
77
submissions/CKS-006-completed.md
Normal file
77
submissions/CKS-006-completed.md
Normal 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**.
|
||||||
26
submissions/CKS-007-completed.md
Normal file
26
submissions/CKS-007-completed.md
Normal 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**.
|
||||||
27
submissions/CKS-008-skipped.md
Normal file
27
submissions/CKS-008-skipped.md
Normal 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.
|
||||||
85
submissions/CKS-009-reviewed.md
Normal file
85
submissions/CKS-009-reviewed.md
Normal 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.
|
||||||
67
submissions/CKS-010-completed.md
Normal file
67
submissions/CKS-010-completed.md
Normal 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.
|
||||||
66
submissions/CKS-010-incomplete.md
Normal file
66
submissions/CKS-010-incomplete.md
Normal 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.
|
||||||
29
submissions/CKS-011-completed.md
Normal file
29
submissions/CKS-011-completed.md
Normal 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.
|
||||||
35
submissions/CKS-012-clean-win.md
Normal file
35
submissions/CKS-012-clean-win.md
Normal 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.
|
||||||
33
submissions/CKS-013-completed.md
Normal file
33
submissions/CKS-013-completed.md
Normal 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.
|
||||||
23
submissions/CKS-014-completed.md
Normal file
23
submissions/CKS-014-completed.md
Normal 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.
|
||||||
23
submissions/CKS-015-clean-win.md
Normal file
23
submissions/CKS-015-clean-win.md
Normal 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.
|
||||||
41
submissions/CKS-016-skipped-by-topic-advance.md
Normal file
41
submissions/CKS-016-skipped-by-topic-advance.md
Normal 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.
|
||||||
64
submissions/CKS-017-completed-verification-skipped.md
Normal file
64
submissions/CKS-017-completed-verification-skipped.md
Normal 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.
|
||||||
97
submissions/CKS-018-completed.md
Normal file
97
submissions/CKS-018-completed.md
Normal 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.
|
||||||
24
submissions/CKS-019-completed.md
Normal file
24
submissions/CKS-019-completed.md
Normal 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.
|
||||||
Reference in New Issue
Block a user