CKS_FINALE

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

20
ANSWER.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>

View 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.

View 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.

View 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.

View 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.

View 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.

View 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.

View 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.

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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
View 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.

View 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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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