Merge branch 'main' of https://git.jutsugen.top/mosiur/CKS-Lessons
This commit is contained in:
@@ -4,7 +4,7 @@ Canonical progress record for CKS preparation. Update this file after every grad
|
|||||||
|
|
||||||
## Dashboard
|
## Dashboard
|
||||||
|
|
||||||
Last updated: 2026-08-26
|
Last updated: 2026-08-27
|
||||||
|
|
||||||
| Domain | Weight | Hero | Training | Not started | Total |
|
| Domain | Weight | Hero | Training | Not started | Total |
|
||||||
|---|---:|---:|---:|---:|---:|
|
|---|---:|---:|---:|---:|---:|
|
||||||
@@ -38,6 +38,15 @@ A topic becomes Hero only when every item is checked:
|
|||||||
|
|
||||||
Minor syntax corrections during training are expected. A cold retest fails when the solution needs field-by-field coaching, grants excess privilege, breaks required behavior, omits proof, or changes resources outside scope.
|
Minor syntax corrections during training are expected. A cold retest fails when the solution needs field-by-field coaching, grants excess privilege, breaks required behavior, omits proof, or changes resources outside scope.
|
||||||
|
|
||||||
|
## Exam-Simulation Rules
|
||||||
|
|
||||||
|
- Every graded question is a Linux command-line lab on a designated host or Kubernetes context.
|
||||||
|
- Prompts specify an end state; the learner changes files, resources, processes, or policies to reach it.
|
||||||
|
- Deliverables are the resulting state plus short command/output evidence. Essays, prose tables, PowerShell parsing, and multiple-choice answers are not graded tasks.
|
||||||
|
- Tools are used directly when the syllabus names them. A scanner task requires running the scanner and changing the system, not writing about scanner theory.
|
||||||
|
- Harder-than-exam means more interacting controls, realistic faults, or a tighter time limit. It does not mean more literature questions.
|
||||||
|
- When the current computer lacks the required Linux/Kubernetes environment, mark the task environment-blocked and use a Linux lab; do not replace it with a written substitute.
|
||||||
|
|
||||||
## Resume Protocol
|
## Resume Protocol
|
||||||
|
|
||||||
1. Read `MISSION.md`, `NOTES.md`, the newest learning record, and this file.
|
1. Read `MISSION.md`, `NOTES.md`, the newest learning record, and this file.
|
||||||
@@ -49,7 +58,19 @@ Minor syntax corrections during training are expected. A cold retest fails when
|
|||||||
|
|
||||||
## Next Task
|
## Next Task
|
||||||
|
|
||||||
`CS-02.Q1` in progress as Lesson 0036: run a kube-bench-based CIS review and separate automated findings from manual checks across kube-apiserver, etcd, kubelet, and CoreDNS.
|
Finish the one-time local lab installation after Windows restarts, then run `CS-02.Q1` as revised Lesson 0036 on `kind-cks-lab`. Do not use the learner's Kubernetes v1.36 production cluster: kube-bench v0.16.0 has automatic version mappings only through v1.34.
|
||||||
|
|
||||||
|
## Local Lab Environment
|
||||||
|
|
||||||
|
Status: `WINDOWS RESTART REQUIRED`
|
||||||
|
|
||||||
|
- Platform: Ubuntu 24.04 on WSL2, Docker Engine, two-node Kind cluster, and Calico.
|
||||||
|
- Kubernetes: pinned to v1.34.0; kube-bench: pinned to v0.16.0.
|
||||||
|
- Cluster and context: `cks-lab` / `kind-cks-lab`.
|
||||||
|
- Kubeconfig: `/opt/cks-lab/kubeconfig`; it is never merged with a production config.
|
||||||
|
- Setup and recovery files: `lab-environment/`.
|
||||||
|
- Resume after restart: run `lab-environment\\02-install-and-build.ps1`, then `lab-environment\\verify-lab.ps1`.
|
||||||
|
- Lesson scope: use Kind nodes for Kubernetes tasks and the disposable Ubuntu WSL2 VM for host/kernel hardening tasks.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
4
NOTES.md
4
NOTES.md
@@ -7,3 +7,7 @@
|
|||||||
- Review preference: list all detected issues, then correct only one issue per round.
|
- Review preference: list all detected issues, then correct only one issue per round.
|
||||||
- Mastery tracking: use `CKS-MASTERY-CHECKLIST.md` as the canonical cross-session dashboard. Never award Hero from lesson completion alone; require the topic's tool drill, timed capstone, verification evidence, and cold retest.
|
- Mastery tracking: use `CKS-MASTERY-CHECKLIST.md` as the canonical cross-session dashboard. Never award Hero from lesson completion alone; require the topic's tool drill, timed capstone, verification evidence, and cold retest.
|
||||||
- Confidence priority: deliberately rotate through named CKS tools instead of relying mainly on hand-written YAML.
|
- Confidence priority: deliberately rotate through named CKS tools instead of relying mainly on hand-written YAML.
|
||||||
|
- Exam-simulation rule: graded questions must be Linux command-line performance labs. Do not require PowerShell, essays, explanatory tables, multiple-choice answers, or tool-installation research as task deliverables. Grade the resulting system state and concise command/output evidence.
|
||||||
|
- Use short CKS-style prompts: designated host/context, exact end state, files/resources in scope, time limit, and verification. Hard mode may combine more controls or tighter time, but must remain a lab rather than a literature exercise.
|
||||||
|
- Local lab rule: run all future lessons in the disposable `cks-lab` environment under Ubuntu 24.04 WSL2. Use `/opt/cks-lab/kubeconfig` and require context `kind-cks-lab`; never use, copy, or merge a production kubeconfig.
|
||||||
|
- Lab targeting: use the Kind control-plane and worker containers for Kubernetes component/runtime exercises, and the enclosing Ubuntu WSL2 VM for host OS, IAM, firewall, AppArmor, seccomp, and kernel exercises.
|
||||||
|
|||||||
28
lab-environment/01-enable-wsl.ps1
Normal file
28
lab-environment/01-enable-wsl.ps1
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$principal = New-Object Security.Principal.WindowsPrincipal(
|
||||||
|
[Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
$arguments = @(
|
||||||
|
'-NoProfile',
|
||||||
|
'-ExecutionPolicy', 'Bypass',
|
||||||
|
'-File', ('"{0}"' -f $PSCommandPath)
|
||||||
|
)
|
||||||
|
Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList $arguments -Wait
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host 'Enabling Windows Subsystem for Linux...'
|
||||||
|
& dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
|
||||||
|
if ($LASTEXITCODE -notin 0, 3010) { throw "DISM failed with exit code $LASTEXITCODE" }
|
||||||
|
|
||||||
|
Write-Host 'Enabling Virtual Machine Platform...'
|
||||||
|
& dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
|
||||||
|
if ($LASTEXITCODE -notin 0, 3010) { throw "DISM failed with exit code $LASTEXITCODE" }
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Windows features are enabled. Restart Windows, then run:'
|
||||||
|
Write-Host ' .\02-install-and-build.ps1'
|
||||||
|
|
||||||
45
lab-environment/02-install-and-build.ps1
Normal file
45
lab-environment/02-install-and-build.ps1
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$distribution = 'Ubuntu-24.04'
|
||||||
|
|
||||||
|
function Test-Distribution {
|
||||||
|
$names = @(& wsl.exe --list --quiet 2>$null) | ForEach-Object { $_.Trim("`0 ") }
|
||||||
|
return $names -contains $distribution
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Distribution)) {
|
||||||
|
Write-Host "Installing $distribution..."
|
||||||
|
& wsl.exe --install --web-download --distribution $distribution --no-launch
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw 'Ubuntu installation failed. Confirm Windows was restarted after running 01-enable-wsl.ps1.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& wsl.exe --set-default-version 2
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Could not set WSL2 as the default.' }
|
||||||
|
|
||||||
|
# Starting as root avoids an interactive Unix-user prompt in this disposable VM.
|
||||||
|
& wsl.exe --distribution $distribution --user root --exec /bin/true
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "$distribution did not initialize correctly." }
|
||||||
|
|
||||||
|
$linuxDirectory = (& wsl.exe --distribution $distribution --user root --exec wslpath -a $PSScriptRoot).Trim()
|
||||||
|
if (-not $linuxDirectory) { throw 'Could not translate the lab path into WSL.' }
|
||||||
|
|
||||||
|
Write-Host 'Installing pinned lab tools...'
|
||||||
|
& wsl.exe --distribution $distribution --user root --exec bash "$linuxDirectory/bootstrap.sh"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Linux tool bootstrap failed.' }
|
||||||
|
|
||||||
|
# Reload WSL so systemd and Docker use the newly written WSL configuration.
|
||||||
|
& wsl.exe --shutdown
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
|
|
||||||
|
Write-Host 'Creating the isolated Kubernetes cluster...'
|
||||||
|
& wsl.exe --distribution $distribution --user root --exec bash "$linuxDirectory/create-lab.sh" "$linuxDirectory/kind-v1.34.yaml"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Cluster creation failed.' }
|
||||||
|
|
||||||
|
& wsl.exe --distribution $distribution --user root --exec bash "$linuxDirectory/verify-lab.sh"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Lab verification failed.' }
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'CKS lab is ready. Enter it with:'
|
||||||
|
Write-Host ' .\enter-lab.ps1'
|
||||||
|
|
||||||
64
lab-environment/README.md
Normal file
64
lab-environment/README.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# CKS Local Lab
|
||||||
|
|
||||||
|
This lab runs entirely inside an Ubuntu 24.04 WSL2 virtual machine. It uses
|
||||||
|
Docker Engine and Kind to create a two-node Kubernetes cluster.
|
||||||
|
|
||||||
|
## Isolation contract
|
||||||
|
|
||||||
|
- Cluster name: `cks-lab`
|
||||||
|
- Kubernetes version: `v1.34.0`
|
||||||
|
- Lab kubeconfig: `/opt/cks-lab/kubeconfig`
|
||||||
|
- Lab context: `kind-cks-lab`
|
||||||
|
- The scripts never read, merge, or modify `$HOME/.kube/config`.
|
||||||
|
- All destructive commands are limited to the Kind cluster named `cks-lab`.
|
||||||
|
|
||||||
|
Kubernetes v1.34 is deliberate: kube-bench v0.16.0 contains a CIS mapping for
|
||||||
|
v1.34. The production cluster is not a suitable lesson environment.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Run these commands from PowerShell in this directory:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Opens one UAC prompt and enables the required Windows features.
|
||||||
|
.\01-enable-wsl.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart Windows when requested. Then run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\02-install-and-build.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The first Ubuntu launch can take a few minutes. The script installs Docker,
|
||||||
|
kubectl, Kind, kube-bench, creates the cluster, and installs Calico.
|
||||||
|
|
||||||
|
## Daily use
|
||||||
|
|
||||||
|
Open the lab shell:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\enter-lab.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The shell sets `KUBECONFIG=/opt/cks-lab/kubeconfig` and refuses to continue if
|
||||||
|
the active context is not `kind-cks-lab`.
|
||||||
|
|
||||||
|
Verify or rebuild the lab:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\verify-lab.ps1
|
||||||
|
.\reset-lab.ps1
|
||||||
|
.\02-install-and-build.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
Run kube-bench against the Kind control-plane node:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it cks-lab-control-plane \
|
||||||
|
/opt/kube-bench/kube-bench run --benchmark cis-1.12
|
||||||
|
```
|
||||||
|
|
||||||
|
Lesson answers and progress remain in this repository, so they can be carried
|
||||||
|
to another PC. Cluster state is disposable and should be rebuilt there.
|
||||||
|
|
||||||
64
lab-environment/bootstrap.sh
Normal file
64
lab-environment/bootstrap.sh
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
KUBECTL_VERSION="v1.34.0"
|
||||||
|
KIND_VERSION="v0.32.0"
|
||||||
|
KUBE_BENCH_VERSION="0.16.0"
|
||||||
|
|
||||||
|
if [[ "${EUID}" -ne 0 ]]; then
|
||||||
|
echo "Run this script as root inside the lab WSL distribution." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >/etc/wsl.conf <<'EOF'
|
||||||
|
[boot]
|
||||||
|
systemd=true
|
||||||
|
|
||||||
|
[user]
|
||||||
|
default=root
|
||||||
|
EOF
|
||||||
|
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y ca-certificates curl gnupg jq vim git iproute2 iptables conntrack socat
|
||||||
|
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
. /etc/os-release
|
||||||
|
cat >/etc/apt/sources.list.d/docker.sources <<EOF
|
||||||
|
Types: deb
|
||||||
|
URIs: https://download.docker.com/linux/ubuntu
|
||||||
|
Suites: ${UBUNTU_CODENAME:-$VERSION_CODENAME}
|
||||||
|
Components: stable
|
||||||
|
Architectures: $(dpkg --print-architecture)
|
||||||
|
Signed-By: /etc/apt/keyrings/docker.asc
|
||||||
|
EOF
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
|
||||||
|
tmp_dir="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$tmp_dir"' EXIT
|
||||||
|
|
||||||
|
curl -fsSLo "$tmp_dir/kubectl" "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
|
||||||
|
curl -fsSLo "$tmp_dir/kubectl.sha256" "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl.sha256"
|
||||||
|
printf '%s %s\n' "$(cat "$tmp_dir/kubectl.sha256")" "$tmp_dir/kubectl" | sha256sum --check --status
|
||||||
|
install -m 0755 "$tmp_dir/kubectl" /usr/local/bin/kubectl
|
||||||
|
|
||||||
|
curl -fsSLo "$tmp_dir/kind" "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64"
|
||||||
|
curl -fsSLo "$tmp_dir/kind.sha256sum" "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64.sha256sum"
|
||||||
|
(cd "$tmp_dir" && sha256sum --check --status kind.sha256sum)
|
||||||
|
install -m 0755 "$tmp_dir/kind" /usr/local/bin/kind
|
||||||
|
|
||||||
|
curl -fsSLo "$tmp_dir/kube-bench.tar.gz" \
|
||||||
|
"https://github.com/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH_VERSION}/kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.tar.gz"
|
||||||
|
curl -fsSLo "$tmp_dir/checksums.txt" \
|
||||||
|
"https://github.com/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH_VERSION}/kube-bench_${KUBE_BENCH_VERSION}_checksums.txt"
|
||||||
|
(cd "$tmp_dir" && grep " kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.tar.gz$" checksums.txt | sha256sum --check --status)
|
||||||
|
rm -rf /opt/cks-lab/tools/kube-bench
|
||||||
|
install -d /opt/cks-lab/tools/kube-bench
|
||||||
|
tar -xzf "$tmp_dir/kube-bench.tar.gz" -C /opt/cks-lab/tools/kube-bench
|
||||||
|
ln -sfn /opt/cks-lab/tools/kube-bench/kube-bench /usr/local/bin/kube-bench
|
||||||
|
|
||||||
|
printf 'Installed kubectl %s, Kind %s, and kube-bench %s.\n' \
|
||||||
|
"$KUBECTL_VERSION" "$KIND_VERSION" "$KUBE_BENCH_VERSION"
|
||||||
46
lab-environment/create-lab.sh
Normal file
46
lab-environment/create-lab.sh
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
CLUSTER_NAME="cks-lab"
|
||||||
|
KUBECONFIG_PATH="/opt/cks-lab/kubeconfig"
|
||||||
|
NODE_IMAGE="kindest/node:v1.34.0@sha256:7416a61b42b1662ca6ca89f02028ac133a309a2a30ba309614e8ec94d976dc5a"
|
||||||
|
CALICO_VERSION="v3.32.1"
|
||||||
|
CONFIG_PATH="${1:?Pass the Kind configuration path.}"
|
||||||
|
|
||||||
|
if [[ "${EUID}" -ne 0 ]]; then
|
||||||
|
echo "Run this script as root inside the lab WSL distribution." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl start docker 2>/dev/null || service docker start
|
||||||
|
install -d -m 0700 /opt/cks-lab
|
||||||
|
|
||||||
|
if ! kind get clusters | grep -Fxq "$CLUSTER_NAME"; then
|
||||||
|
kind create cluster \
|
||||||
|
--name "$CLUSTER_NAME" \
|
||||||
|
--image "$NODE_IMAGE" \
|
||||||
|
--config "$CONFIG_PATH" \
|
||||||
|
--kubeconfig "$KUBECONFIG_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export KUBECONFIG="$KUBECONFIG_PATH"
|
||||||
|
chmod 0600 "$KUBECONFIG"
|
||||||
|
context="$(kubectl config current-context)"
|
||||||
|
if [[ "$context" != "kind-${CLUSTER_NAME}" ]]; then
|
||||||
|
echo "Safety check failed: unexpected context $context" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
kubectl apply -f "https://raw.githubusercontent.com/projectcalico/calico/${CALICO_VERSION}/manifests/operator-crds.yaml"
|
||||||
|
kubectl apply -f "https://raw.githubusercontent.com/projectcalico/calico/${CALICO_VERSION}/manifests/tigera-operator.yaml"
|
||||||
|
kubectl apply -f "https://raw.githubusercontent.com/projectcalico/calico/${CALICO_VERSION}/manifests/custom-resources.yaml"
|
||||||
|
|
||||||
|
kubectl wait --for=condition=Available deployment/tigera-operator -n tigera-operator --timeout=300s
|
||||||
|
kubectl wait --for=condition=Ready nodes --all --timeout=600s
|
||||||
|
|
||||||
|
for node in "${CLUSTER_NAME}-control-plane" "${CLUSTER_NAME}-worker"; do
|
||||||
|
docker exec "$node" rm -rf /opt/kube-bench
|
||||||
|
docker cp /opt/cks-lab/tools/kube-bench "$node":/opt/kube-bench
|
||||||
|
docker exec "$node" /opt/kube-bench/kube-bench version
|
||||||
|
done
|
||||||
|
|
||||||
17
lab-environment/enter-lab.ps1
Normal file
17
lab-environment/enter-lab.ps1
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$distribution = 'Ubuntu-24.04'
|
||||||
|
|
||||||
|
& wsl.exe --distribution $distribution --user root --exec bash -lc @'
|
||||||
|
export KUBECONFIG=/opt/cks-lab/kubeconfig
|
||||||
|
context="$(kubectl config current-context 2>/dev/null || true)"
|
||||||
|
if [[ "$context" != "kind-cks-lab" ]]; then
|
||||||
|
echo "Safety check failed: expected kind-cks-lab, got ${context:-none}." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "CKS lab: $(kubectl version --output=json | jq -r .serverVersion.gitVersion)"
|
||||||
|
echo "Context: $context"
|
||||||
|
exec bash
|
||||||
|
'@
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Could not enter the isolated CKS lab.' }
|
||||||
|
|
||||||
10
lab-environment/kind-v1.34.yaml
Normal file
10
lab-environment/kind-v1.34.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
kind: Cluster
|
||||||
|
apiVersion: kind.x-k8s.io/v1alpha4
|
||||||
|
name: cks-lab
|
||||||
|
nodes:
|
||||||
|
- role: control-plane
|
||||||
|
- role: worker
|
||||||
|
networking:
|
||||||
|
disableDefaultCNI: true
|
||||||
|
podSubnet: 192.168.0.0/16
|
||||||
|
|
||||||
5
lab-environment/reset-lab.ps1
Normal file
5
lab-environment/reset-lab.ps1
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$linuxDirectory = (& wsl.exe --distribution Ubuntu-24.04 --user root --exec wslpath -a $PSScriptRoot).Trim()
|
||||||
|
& wsl.exe --distribution Ubuntu-24.04 --user root --exec bash "$linuxDirectory/reset-lab.sh"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Lab reset failed.' }
|
||||||
|
|
||||||
19
lab-environment/reset-lab.sh
Normal file
19
lab-environment/reset-lab.sh
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
CLUSTER_NAME="cks-lab"
|
||||||
|
export KUBECONFIG="/opt/cks-lab/kubeconfig"
|
||||||
|
|
||||||
|
if [[ "$CLUSTER_NAME" != "cks-lab" ]]; then
|
||||||
|
echo "Refusing to delete an unexpected cluster name." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl start docker 2>/dev/null || service docker start
|
||||||
|
if kind get clusters | grep -Fxq "$CLUSTER_NAME"; then
|
||||||
|
kind delete cluster --name "$CLUSTER_NAME"
|
||||||
|
else
|
||||||
|
echo "The cks-lab cluster does not exist."
|
||||||
|
fi
|
||||||
|
rm -f "$KUBECONFIG"
|
||||||
|
|
||||||
5
lab-environment/verify-lab.ps1
Normal file
5
lab-environment/verify-lab.ps1
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$linuxDirectory = (& wsl.exe --distribution Ubuntu-24.04 --user root --exec wslpath -a $PSScriptRoot).Trim()
|
||||||
|
& wsl.exe --distribution Ubuntu-24.04 --user root --exec bash "$linuxDirectory/verify-lab.sh"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Lab verification failed.' }
|
||||||
|
|
||||||
23
lab-environment/verify-lab.sh
Normal file
23
lab-environment/verify-lab.sh
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
export KUBECONFIG="/opt/cks-lab/kubeconfig"
|
||||||
|
expected_context="kind-cks-lab"
|
||||||
|
context="$(kubectl config current-context 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "$context" != "$expected_context" ]]; then
|
||||||
|
echo "Safety check failed: expected $expected_context, got ${context:-none}." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
kubectl wait --for=condition=Ready nodes --all --timeout=60s
|
||||||
|
kubectl get nodes -o wide
|
||||||
|
kubectl get pods -A
|
||||||
|
|
||||||
|
for node in cks-lab-control-plane cks-lab-worker; do
|
||||||
|
docker inspect "$node" >/dev/null
|
||||||
|
docker exec "$node" /opt/kube-bench/kube-bench version
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Verified isolated context: $context"
|
||||||
|
|
||||||
15
learning-records/0010-linux-performance-labs-only.md
Normal file
15
learning-records/0010-linux-performance-labs-only.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Linux performance labs only
|
||||||
|
|
||||||
|
The learner correctly identified that the original Lesson 36 was not CKS-like: it required PowerShell parsing and extensive written analysis. Current Linux Foundation instructions describe CKS as 15–20 performance tasks solved from a Linux command line on designated hosts.
|
||||||
|
|
||||||
|
## Permanent teaching rule
|
||||||
|
|
||||||
|
All graded CKS questions must be Linux command-line labs. Ask for concrete system changes and concise verification output, not essays, explanatory tables, multiple-choice responses, or PowerShell work. Named tools must be executed directly. If the current computer lacks the required environment, record an environment block and use a Linux Kubernetes lab rather than substituting literature work.
|
||||||
|
|
||||||
|
## Difficulty rule
|
||||||
|
|
||||||
|
Make labs harder through interacting controls, realistic broken states, strict scope preservation, and tighter time limits. Do not make them harder through extra prose requirements.
|
||||||
|
|
||||||
|
## kube-bench environment finding
|
||||||
|
|
||||||
|
The learner's production cluster runs Kubernetes v1.36.0. kube-bench v0.16.0 maps Kubernetes versions only through v1.34, where it selects `cis-1.12`. Do not use forced older benchmarks as Hero evidence or modify the v1.36 production control plane for training. Lesson 36 requires a disposable v1.34 kubeadm lab.
|
||||||
57
lessons/0036-kube-bench-cis-remediation.html
Normal file
57
lessons/0036-kube-bench-cis-remediation.html
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Lesson 36: kube-bench CIS remediation</title>
|
||||||
|
<link rel="stylesheet" href="../assets/course.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<p class="eyebrow">Lesson 0036 · 25 minutes · Linux performance lab · Cluster Setup</p>
|
||||||
|
<h1>Remediate kube-bench failures</h1>
|
||||||
|
<p class="lede">Complete this task on disposable Linux hosts running a kubeadm-style Kubernetes v1.34 cluster with kube-bench v0.16.0. No written explanation is required.</p>
|
||||||
|
|
||||||
|
<div class="callout"><strong>Environment required:</strong> a disposable Kubernetes v1.34 lab with one control-plane host, one worker host, root access, kube-bench v0.16.0, and editable component configuration. Do not perform this training remediation on the learner's Kubernetes v1.36 production cluster: kube-bench v0.16.0 maps only through Kubernetes v1.34.</div>
|
||||||
|
|
||||||
|
<h2>Task</h2>
|
||||||
|
<p>Start on host <code>controlplane</code>. Become root. Do not change any setting not named below.</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>Run kube-bench checks <code>1.2.1</code>, <code>1.2.7</code>, <code>2.2</code>, and <code>2.3</code>. Save the output to <code>/root/lesson-36-controlplane-before.txt</code>.</li>
|
||||||
|
<li>Configure kube-apiserver with anonymous authentication disabled and authorization modes <code>Node,RBAC</code>.</li>
|
||||||
|
<li>Configure etcd with client-certificate authentication enabled and automatic TLS disabled. Preserve its existing CA, certificate, key, peer, data, and cluster settings.</li>
|
||||||
|
<li>Wait for kube-apiserver and etcd to become healthy.</li>
|
||||||
|
<li>Rerun the same four checks and save output to <code>/root/lesson-36-controlplane-after.txt</code>. All four checks must report PASS.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<p>Return to the base host, then connect directly to host <code>worker</code>. Become root.</p>
|
||||||
|
|
||||||
|
<ol start="6">
|
||||||
|
<li>Run kube-bench checks <code>4.2.1</code>, <code>4.2.2</code>, and <code>4.2.3</code>. Save output to <code>/root/lesson-36-worker-before.txt</code>.</li>
|
||||||
|
<li>Configure kubelet so anonymous authentication is disabled, webhook authentication is enabled, and authorization mode is <code>Webhook</code>.</li>
|
||||||
|
<li>Restart kubelet and verify the node returns to <code>Ready</code>.</li>
|
||||||
|
<li>Rerun the same three checks and save output to <code>/root/lesson-36-worker-after.txt</code>. All three checks must report PASS.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Completion evidence</h2>
|
||||||
|
<p>Send only these outputs:</p>
|
||||||
|
<pre><code># controlplane
|
||||||
|
grep -E '^\[(PASS|FAIL|WARN)\]' /root/lesson-36-controlplane-after.txt
|
||||||
|
|
||||||
|
# worker
|
||||||
|
grep -E '^\[(PASS|FAIL|WARN)\]' /root/lesson-36-worker-after.txt
|
||||||
|
systemctl is-active kubelet
|
||||||
|
kubectl get node worker</code></pre>
|
||||||
|
|
||||||
|
<p>Do not submit an essay or remediation table. The result is graded from component state, saved scanner output, and node health.</p>
|
||||||
|
|
||||||
|
<h2>Documentation</h2>
|
||||||
|
<p>Use the installed kube-bench help, component configuration files, and official <a href="https://github.com/aquasecurity/kube-bench">kube-bench documentation</a> as needed.</p>
|
||||||
|
|
||||||
|
<h2>What to send me</h2>
|
||||||
|
<p>Send the four PASS groups, <code>systemctl</code> result, and worker-node status. I will list all detected issues and correct one per round.</p>
|
||||||
|
<footer>Previous: <a href="0035-apiserver-audit-hard-mode.html">API server and audit hard mode</a> · Tracker: <a href="../CKS-MASTERY-CHECKLIST.md">CKS Mastery Checklist</a></footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
||||||
<title>Lesson 36: kube-bench CIS triage</title>
|
|
||||||
<link rel="stylesheet" href="../assets/course.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<p class="eyebrow">Lesson 0036 · 35 minutes · Tool drill · Cluster Setup</p>
|
|
||||||
<h1>Triage kube-bench findings without obeying the scanner blindly</h1>
|
|
||||||
<p class="lede">A scanner accelerates inspection; it does not replace component knowledge. Extract non-passing checks, verify them against configuration, identify uncovered components, and prioritize fixes by impact.</p>
|
|
||||||
|
|
||||||
<h2>Environment note</h2>
|
|
||||||
<p>The current Windows shell has no Docker, WSL, Kubernetes CLI, or Linux node access. Use the provided kube-bench JSON report for this training attempt. Hero remains unavailable until you rerun kube-bench on a Linux control-plane and worker node.</p>
|
|
||||||
|
|
||||||
<h2>Task: 25 minutes</h2>
|
|
||||||
<p>Analyze these files without modifying them:</p>
|
|
||||||
<ul>
|
|
||||||
<li><code>lab/lesson-36-kube-bench.json</code></li>
|
|
||||||
<li><code>lab/lesson-35-kube-apiserver-insecure.yaml</code></li>
|
|
||||||
<li><code>lab/lesson-36-etcd.yaml</code></li>
|
|
||||||
<li><code>lab/lesson-28-kubelet-review.yaml</code></li>
|
|
||||||
<li><code>lab/lesson-36-coredns.yaml</code></li>
|
|
||||||
</ul>
|
|
||||||
<p>Create <code>lab/lesson-36-answer.md</code> containing:</p>
|
|
||||||
<ol>
|
|
||||||
<li>The exact PowerShell command used to extract every non-PASS result from the JSON.</li>
|
|
||||||
<li>A table for all seven FAIL results and the one WARN result: check ID, component, status, scanner evidence, configuration evidence, verdict, and precise remediation.</li>
|
|
||||||
<li>Your first three remediations in operational priority order, with one sentence of justification each.</li>
|
|
||||||
<li>At least four security findings from the CoreDNS Deployment that kube-bench did not report.</li>
|
|
||||||
<li>One paragraph explaining why WARN does not mean FAIL, why PASS is not universal proof, and why CoreDNS requires manual review here.</li>
|
|
||||||
<li>The exact Linux commands you would use to install pinned kube-bench v0.16.0, verify its checksum, run control-plane/etcd/node/policies targets as root, save JSON, and make CI return a nonzero code when checks fail.</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<div class="callout"><strong>Tool reflex:</strong> kube-bench has PASS, FAIL, WARN, and INFO states. A WARN commonly needs manual attention. A failed check can also reflect an execution or detection problem, so compare <code>actual_value</code> and <code>reason</code> with effective configuration before changing a node.</div>
|
|
||||||
|
|
||||||
<h2>PowerShell starting shape</h2>
|
|
||||||
<pre><code>$report = Get-Content .\lab\lesson-36-kube-bench.json -Raw | ConvertFrom-Json
|
|
||||||
$report.Controls.tests.results |
|
|
||||||
Where-Object status -ne 'PASS' |
|
|
||||||
Select-Object test_number, status, test_desc, actual_value, reason</code></pre>
|
|
||||||
|
|
||||||
<h2>Linux Hero rerun shape</h2>
|
|
||||||
<pre><code># Run on a Linux node with access to component processes and files.
|
|
||||||
sudo kube-bench run --targets master,etcd,node,policies \
|
|
||||||
--json --outputfile kube-bench.json --exit-code 42</code></pre>
|
|
||||||
<p>You must determine and record the complete pinned installation and checksum-verification commands yourself.</p>
|
|
||||||
|
|
||||||
<h2>Retrieval check</h2>
|
|
||||||
<div class="quiz">
|
|
||||||
<h3>Question</h3>
|
|
||||||
<p>kube-bench reports WARN for a manual check. What should you do?</p>
|
|
||||||
<label><input type="radio" name="q" value="verify">Inspect the effective configuration and record a manual verdict.</label>
|
|
||||||
<label><input type="radio" name="q" value="fail">Treat the warning as a proven configuration failure immediately.</label>
|
|
||||||
<label><input type="radio" name="q" value="ignore">Ignore the warning because only automated checks matter.</label>
|
|
||||||
<button onclick="check()">Check answer</button>
|
|
||||||
<p id="feedback" class="feedback" aria-live="polite"></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Documentation targets</h2>
|
|
||||||
<p>Use the official <a href="https://github.com/aquasecurity/kube-bench">kube-bench repository</a>, its <a href="https://github.com/aquasecurity/kube-bench/blob/main/docs/installation.md">installation guide</a>, and the applicable CIS benchmark guidance after your first analysis.</p>
|
|
||||||
|
|
||||||
<h2>What to send me</h2>
|
|
||||||
<p>Save the Markdown answer and say <em>check</em>. I will list every issue and correct one per round. This attempt can advance CS-02 training, but the Hero tool gate requires a later real-node run.</p>
|
|
||||||
<footer>Previous: <a href="0035-apiserver-audit-hard-mode.html">API server and audit hard mode</a> · Tracker: <a href="../CKS-MASTERY-CHECKLIST.md">CKS Mastery Checklist</a></footer>
|
|
||||||
</main>
|
|
||||||
<script>
|
|
||||||
function check(){const c=document.querySelector('input[name=q]:checked'),f=document.getElementById('feedback');if(!c){f.textContent='Choose an answer.';f.className='feedback bad';return;}if(c.value==='verify'){f.textContent='Correct. WARN requires a manual, evidence-based verdict.';f.className='feedback good';}else{f.textContent='Not quite. The scanner has not proved either compliance or failure.';f.className='feedback bad';}}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Reference in New Issue
Block a user