CKS — CIS Benchmarks & kube-bench Lab
Lab Overview
This hands-on lab covers the complete CIS benchmark workflow: install kube-bench, scan the cluster, identify failures, remediate them, and verify the fixes. By the end of this lab, you’ll have practiced the exact skills tested in the CKS exam for CIS benchmark questions.
Estimated time: 45–60 minutes
Prerequisites
| Requirement | Details |
|---|---|
| Kubernetes cluster | kubeadm cluster with at least 1 control plane + 1 worker node |
| SSH access | Root/sudo access to both control plane and worker nodes |
| kubectl | Configured and connected to the cluster |
| Internet access | For downloading kube-bench (or pre-download the binary) |
Important: This lab requires a kubeadm-based cluster. Managed Kubernetes services (EKS, GKE, AKS) don’t expose control plane configuration files and won’t work for CIS benchmark exercises.
Verify Lab Readiness
# Verify cluster is running
kubectl cluster-info
# Verify you have control plane and worker nodes
kubectl get nodes
# Verify SSH access to control plane
ssh controlplane "hostname"
# Verify SSH access to worker node
ssh node01 "hostname"
Expected output:
NAME STATUS ROLES AGE VERSION
controlplane Ready control-plane 30m v1.31.0
node01 Ready <none> 28m v1.31.0
What You’ll Practice
- Install kube-bench on cluster nodes
- Run kube-bench as a Kubernetes Job
- Scan control plane and identify FAIL results
- Scan worker node and identify FAIL results
- Fix top 5 FAIL results on the API server
- Fix kubelet security configuration
- Re-run kube-bench and verify all fixes
Exercise 1: Install kube-bench and Run Initial Scan
Objective: Install kube-bench on the control plane node and run an initial baseline scan.
Step 1.1: Install kube-bench Binary
SSH into the control plane node:
ssh controlplane
Download and install kube-bench:
# Download kube-bench
KUBE_BENCH_VERSION=0.8.0
curl -L https://github.com/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH_VERSION}/kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.tar.gz -o kube-bench.tar.gz
# Extract
tar -xvf kube-bench.tar.gz
# Move to PATH
sudo mv kube-bench /usr/local/bin/
# Verify installation
kube-bench version
Expected output:
v0.8.0
Step 1.2: Run Baseline Scan (Control Plane)
# Run the full master scan and save to a file
sudo kube-bench run --targets master 2>&1 | tee /tmp/kube-bench-master-before.txt
# View the summary
tail -5 /tmp/kube-bench-master-before.txt
Expected output (approximately):
== Summary master ==
38 checks PASS
9 checks FAIL
4 checks WARN
0 checks INFO
Step 1.3: List All FAIL Results
# Extract only FAIL lines
grep '^\[FAIL\]' /tmp/kube-bench-master-before.txt
Expected output (your results may vary):
[FAIL] 1.2.1 Ensure that the --anonymous-auth argument is set to false
[FAIL] 1.2.16 Ensure that the --profiling argument is set to false
[FAIL] 1.2.19 Ensure that the --audit-log-path argument is set
[FAIL] 1.2.20 Ensure that the --audit-log-maxage argument is set to 30 or as appropriate
[FAIL] 1.2.21 Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate
[FAIL] 1.2.22 Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate
[FAIL] 1.3.2 Ensure that the --profiling argument is set to false
[FAIL] 1.4.1 Ensure that the --profiling argument is set to false
[FAIL] 1.4.2 Ensure that the --bind-address argument is set to 127.0.0.1
Record these results — you’ll compare them against the post-remediation scan.
Step 1.4: Install kube-bench on Worker Node
SSH into the worker node and repeat the installation:
ssh node01
KUBE_BENCH_VERSION=0.8.0
curl -L https://github.com/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH_VERSION}/kube-bench_${KUBE_BENCH_VERSION}_linux_amd64.tar.gz -o kube-bench.tar.gz
tar -xvf kube-bench.tar.gz
sudo mv kube-bench /usr/local/bin/
kube-bench version
Exercise 2: Run kube-bench as a Kubernetes Job
Objective: Deploy kube-bench as a Kubernetes Job to scan the control plane — the most common exam approach.
Step 2.1: Create the Job Manifest
Back on your workstation (or control plane node with kubectl):
cat <<'EOF' > kube-bench-master-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench-master
namespace: default
spec:
template:
metadata:
labels:
app: kube-bench
spec:
hostPID: true
nodeSelector:
node-role.kubernetes.io/control-plane: ""
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: kube-bench
image: aquasec/kube-bench:v0.8.0
command: ["kube-bench", "run", "--targets", "master"]
volumeMounts:
- name: var-lib-etcd
mountPath: /var/lib/etcd
readOnly: true
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
readOnly: true
- name: var-lib-kube-scheduler
mountPath: /var/lib/kube-scheduler
readOnly: true
- name: var-lib-kube-controller-manager
mountPath: /var/lib/kube-controller-manager
readOnly: true
- name: etc-systemd
mountPath: /etc/systemd
readOnly: true
- name: lib-systemd
mountPath: /lib/systemd/
readOnly: true
- name: srv-kubernetes
mountPath: /srv/kubernetes/
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
- name: usr-bin
mountPath: /usr/local/mount-from-host/bin
readOnly: true
- name: etc-cni-netd
mountPath: /etc/cni/net.d/
readOnly: true
- name: opt-cni-bin
mountPath: /opt/cni/bin/
readOnly: true
restartPolicy: Never
volumes:
- name: var-lib-etcd
hostPath:
path: /var/lib/etcd
- name: var-lib-kubelet
hostPath:
path: /var/lib/kubelet
- name: var-lib-kube-scheduler
hostPath:
path: /var/lib/kube-scheduler
- name: var-lib-kube-controller-manager
hostPath:
path: /var/lib/kube-controller-manager
- name: etc-systemd
hostPath:
path: /etc/systemd
- name: lib-systemd
hostPath:
path: /lib/systemd
- name: srv-kubernetes
hostPath:
path: /srv/kubernetes
- name: etc-kubernetes
hostPath:
path: /etc/kubernetes
- name: usr-bin
hostPath:
path: /usr/bin
- name: etc-cni-netd
hostPath:
path: /etc/cni/net.d/
- name: opt-cni-bin
hostPath:
path: /opt/cni/bin/
backoffLimit: 0
EOF
Step 2.2: Deploy and View Results
# Apply the Job
kubectl apply -f kube-bench-master-job.yaml
# Wait for Job to complete
kubectl wait --for=condition=complete job/kube-bench-master --timeout=120s
# View full results
kubectl logs job/kube-bench-master
# View only FAIL results
kubectl logs job/kube-bench-master | grep '^\[FAIL\]'
# View summary
kubectl logs job/kube-bench-master | tail -5
Step 2.3: Compare Results
The Job results should match what you saw from the direct binary run. This confirms the Job is working correctly.
Step 2.4: Cleanup
kubectl delete job kube-bench-master
Exercise 3: Scan Master Node and Identify FAIL Results
Objective: Analyze the control plane scan results and categorize failures by priority.
Step 3.1: Run Targeted Scans
SSH into the control plane node:
ssh controlplane
Run scans per section:
# API Server checks
sudo kube-bench run --group 1.2 2>&1 | grep -E '^\[(FAIL|WARN)\]'
# Controller Manager checks
sudo kube-bench run --group 1.3 2>&1 | grep -E '^\[(FAIL|WARN)\]'
# Scheduler checks
sudo kube-bench run --group 1.4 2>&1 | grep -E '^\[(FAIL|WARN)\]'
# etcd checks
sudo kube-bench run --targets etcd 2>&1 | grep -E '^\[(FAIL|WARN)\]'
Step 3.2: Create a Prioritized Fix List
Based on the results, create your fix list (example):
Priority: CRITICAL
[ ] 1.2.1 - API server anonymous auth → --anonymous-auth=false
Priority: HIGH
[ ] 1.2.19 - Audit log path → --audit-log-path=/var/log/kubernetes/audit/audit.log
[ ] 1.2.20 - Audit log maxage → --audit-log-maxage=30
[ ] 1.2.21 - Audit log maxbackup → --audit-log-maxbackup=10
[ ] 1.2.22 - Audit log maxsize → --audit-log-maxsize=100
Priority: MEDIUM
[ ] 1.2.16 - API server profiling → --profiling=false
[ ] 1.3.2 - Controller manager profiling → --profiling=false
[ ] 1.4.1 - Scheduler profiling → --profiling=false
[ ] 1.4.2 - Scheduler bind address → --bind-address=127.0.0.1
Exercise 4: Scan Worker Node and Identify FAIL Results
Objective: Run kube-bench on the worker node and identify kubelet failures.
Step 4.1: Run Worker Node Scan
SSH into the worker node:
ssh node01
# Run full worker node scan
sudo kube-bench run --targets node 2>&1 | tee /tmp/kube-bench-node-before.txt
# View FAIL results
grep '^\[FAIL\]' /tmp/kube-bench-node-before.txt
Expected output (approximately):
[FAIL] 4.2.1 Ensure that the --anonymous-auth argument is set to false
[FAIL] 4.2.4 Ensure that the --read-only-port argument is set to 0
[FAIL] 4.2.6 Ensure that the --protect-kernel-defaults argument is set to true
Step 4.2: View Summary
tail -5 /tmp/kube-bench-node-before.txt
Expected output:
== Summary node ==
18 checks PASS
3 checks FAIL
1 checks WARN
0 checks INFO
Exercise 5: Fix Top 5 FAIL Results on the API Server
Objective: Remediate the most critical API server failures.
Step 5.1: Back Up the Current Manifest
SSH into the control plane node:
ssh controlplane
# Always back up before editing
sudo cp /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/kube-apiserver.yaml.bak
Step 5.2: Fix Anonymous Auth (Critical)
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
Add in the command section (after existing flags):
- --anonymous-auth=false
Do NOT save yet — we’ll apply multiple fixes at once to minimize restarts.
Step 5.3: Fix Profiling (Medium)
In the same file, add:
- --profiling=false
Step 5.4: Fix Audit Logging (High)
First, create the audit policy and directories:
# Create directories
sudo mkdir -p /var/log/kubernetes/audit
sudo mkdir -p /etc/kubernetes/audit
# Create audit policy
cat <<'EOF' | sudo tee /etc/kubernetes/audit/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets", "configmaps"]
- level: RequestResponse
resources:
- group: ""
resources: ["pods"]
verbs: ["create", "delete", "patch"]
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
- level: Metadata
stages:
- ResponseComplete
EOF
Now add the audit flags to the API server manifest:
- --audit-policy-file=/etc/kubernetes/audit/audit-policy.yaml
- --audit-log-path=/var/log/kubernetes/audit/audit.log
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-log-maxsize=100
Add volume mounts (inside the existing volumeMounts array):
- name: audit-log
mountPath: /var/log/kubernetes/audit
- name: audit-policy
mountPath: /etc/kubernetes/audit
readOnly: true
Add volumes (inside the existing volumes array):
- name: audit-log
hostPath:
path: /var/log/kubernetes/audit
type: DirectoryOrCreate
- name: audit-policy
hostPath:
path: /etc/kubernetes/audit
type: DirectoryOrCreate
Step 5.5: Save and Verify API Server Restart
Save the file. The kubelet detects the change and restarts the API server.
# Watch the API server Pod restart
kubectl get pods -n kube-system -l component=kube-apiserver -w
Wait until the Pod shows Running and READY 1/1:
NAME READY STATUS RESTARTS AGE
kube-apiserver-controlplane 1/1 Running 0 15s
If the API server doesn’t come back: Check for YAML syntax errors:
# Check kubelet logs
sudo journalctl -u kubelet -f | grep apiserver
# Restore from backup if needed
sudo cp /tmp/kube-apiserver.yaml.bak /etc/kubernetes/manifests/kube-apiserver.yaml
Step 5.6: Verify API Server Fixes
# Check all the checks we fixed
sudo kube-bench run --check 1.2.1
sudo kube-bench run --check 1.2.16
sudo kube-bench run --check 1.2.19
sudo kube-bench run --check 1.2.20
sudo kube-bench run --check 1.2.21
sudo kube-bench run --check 1.2.22
Expected output for each:
[PASS] 1.2.1 Ensure that the --anonymous-auth argument is set to false
[PASS] 1.2.16 Ensure that the --profiling argument is set to false
[PASS] 1.2.19 Ensure that the --audit-log-path argument is set
[PASS] 1.2.20 Ensure that the --audit-log-maxage argument is set to 30 or as appropriate
[PASS] 1.2.21 Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate
[PASS] 1.2.22 Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate
Step 5.7: Fix Controller Manager and Scheduler
# Back up
sudo cp /etc/kubernetes/manifests/kube-controller-manager.yaml /tmp/kube-controller-manager.yaml.bak
sudo cp /etc/kubernetes/manifests/kube-scheduler.yaml /tmp/kube-scheduler.yaml.bak
# Fix controller manager
sudo vi /etc/kubernetes/manifests/kube-controller-manager.yaml
Add:
- --profiling=false
# Fix scheduler
sudo vi /etc/kubernetes/manifests/kube-scheduler.yaml
Add:
- --profiling=false
- --bind-address=127.0.0.1
Wait for both Pods to restart:
kubectl get pods -n kube-system -l 'component in (kube-controller-manager,kube-scheduler)' -w
Verify:
sudo kube-bench run --check 1.3.2
sudo kube-bench run --check 1.4.1
sudo kube-bench run --check 1.4.2
Expected — all PASS.
Exercise 6: Fix Kubelet Security Configuration
Objective: Remediate kubelet FAIL results on the worker node.
Step 6.1: Back Up Current Configuration
SSH into the worker node:
ssh node01
# Back up
sudo cp /var/lib/kubelet/config.yaml /tmp/kubelet-config.yaml.bak
Step 6.2: Review Current kubelet Config
cat /var/lib/kubelet/config.yaml
Note the current values for authentication, authorization, readOnlyPort, and protectKernelDefaults.
Step 6.3: Apply Security Fixes
sudo vi /var/lib/kubelet/config.yaml
Add or modify these sections:
# Fix 4.2.1: Disable anonymous authentication
authentication:
anonymous:
enabled: false
webhook:
enabled: true
cacheTTL: 0s
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
# Fix 4.2.2: Use Webhook authorization
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 0s
cacheUnauthorizedTTL: 0s
# Fix 4.2.4: Disable read-only port
readOnlyPort: 0
Step 6.4: Fix Kernel Defaults (if 4.2.6 failed)
Before enabling protectKernelDefaults, set the required kernel parameters:
# Set required kernel parameters
sudo sysctl -w vm.overcommit_memory=1
sudo sysctl -w vm.panic_on_oom=0
sudo sysctl -w kernel.panic=10
sudo sysctl -w kernel.panic_on_oops=1
# Make persistent
cat <<EOF | sudo tee /etc/sysctl.d/90-kubelet.conf
vm.overcommit_memory=1
vm.panic_on_oom=0
kernel.panic=10
kernel.panic_on_oops=1
EOF
sudo sysctl --system
Now add to kubelet config:
protectKernelDefaults: true
Step 6.5: Restart kubelet
sudo systemctl restart kubelet
# Verify kubelet is running
sudo systemctl status kubelet
# Check for errors
sudo journalctl -u kubelet --no-pager | tail -20
Step 6.6: Verify Kubelet Fixes
sudo kube-bench run --check 4.2.1
sudo kube-bench run --check 4.2.2
sudo kube-bench run --check 4.2.4
sudo kube-bench run --check 4.2.6
Expected:
[PASS] 4.2.1 Ensure that the --anonymous-auth argument is set to false
[PASS] 4.2.2 Ensure that the --authorization-mode argument is not set to AlwaysAllow
[PASS] 4.2.4 Ensure that the --read-only-port argument is set to 0
[PASS] 4.2.6 Ensure that the --protect-kernel-defaults argument is set to true
Exercise 7: Re-Run kube-bench and Verify Improvements
Objective: Run a complete post-remediation scan and compare with the baseline.
Step 7.1: Full Master Rescan
SSH into the control plane node:
ssh controlplane
# Run post-remediation master scan
sudo kube-bench run --targets master 2>&1 | tee /tmp/kube-bench-master-after.txt
# View new summary
echo "=== BEFORE ==="
tail -5 /tmp/kube-bench-master-before.txt
echo ""
echo "=== AFTER ==="
tail -5 /tmp/kube-bench-master-after.txt
Expected output:
=== BEFORE ===
== Summary master ==
38 checks PASS
9 checks FAIL
4 checks WARN
0 checks INFO
=== AFTER ===
== Summary master ==
47 checks PASS
0 checks FAIL
4 checks WARN
0 checks INFO
Step 7.2: Full Worker Rescan
SSH into the worker node:
ssh node01
# Run post-remediation worker scan
sudo kube-bench run --targets node 2>&1 | tee /tmp/kube-bench-node-after.txt
# Compare
echo "=== BEFORE ==="
tail -5 /tmp/kube-bench-node-before.txt
echo ""
echo "=== AFTER ==="
tail -5 /tmp/kube-bench-node-after.txt
Expected output:
=== BEFORE ===
== Summary node ==
18 checks PASS
3 checks FAIL
1 checks WARN
0 checks INFO
=== AFTER ===
== Summary node ==
21 checks PASS
0 checks FAIL
1 checks WARN
0 checks INFO
Step 7.3: Verify No Remaining FAILs
# Master
grep '^\[FAIL\]' /tmp/kube-bench-master-after.txt
# Should return empty
# Worker
grep '^\[FAIL\]' /tmp/kube-bench-node-after.txt
# Should return empty
Step 7.4: Review Remaining WARNs
# Master WARNs
grep '^\[WARN\]' /tmp/kube-bench-master-after.txt
Example remaining WARNs:
[WARN] 1.2.10 Ensure that the admission control plugin EventRateLimit is set
[WARN] 1.2.12 Ensure that the admission control plugin AlwaysPullImages is set
These are Level 2 checks that require organizational decisions — acceptable to leave as WARN with documented justification.
Lab Cleanup
# On control plane — delete kube-bench Job if still running
kubectl delete job kube-bench-master --ignore-not-found
# Optionally remove kube-bench binary
ssh controlplane "sudo rm /usr/local/bin/kube-bench"
ssh node01 "sudo rm /usr/local/bin/kube-bench"
# Keep the security fixes in place — don't revert!
Important: Do NOT revert the security fixes. The hardened configuration is the desired state.
Lab Summary
| Exercise | What You Did | Key Skill |
|---|---|---|
| 1 | Installed kube-bench, ran baseline scan | Tool installation, scan execution |
| 2 | Deployed kube-bench as Kubernetes Job | Job YAML creation (exam skill) |
| 3 | Analyzed control plane failures | Result interpretation, prioritization |
| 4 | Analyzed worker node failures | kubelet-specific checks |
| 5 | Fixed API server, controller-manager, scheduler | Static Pod manifest editing |
| 6 | Fixed kubelet configuration | kubelet config.yaml editing, systemctl |
| 7 | Verified all fixes with re-scan | Complete workflow validation |
Practice Questions
Q1: On the CKS exam, you need to run kube-bench but it’s not installed. What’s the fastest method?
Show Answer
Run kube-bench as a Kubernetes Job. This doesn’t require installing anything on the node:
kubectl apply -f kube-bench-job.yaml
kubectl wait --for=condition=complete job/kube-bench-master --timeout=120s
kubectl logs job/kube-bench-master
If the exam provides kube-bench pre-installed on nodes, use the binary directly:
sudo kube-bench run --targets master
The Job approach is preferred because it doesn’t require SSH access to the node, which may be faster in the exam environment.
Q2: After editing the API server manifest, kubectl stops working. What happened and how do you fix it?
Show Answer
The API server failed to restart due to a YAML syntax error or invalid flag value in /etc/kubernetes/manifests/kube-apiserver.yaml.
Diagnosis:
# kubectl won't work — use crictl or check kubelet logs directly
sudo crictl ps -a | grep apiserver
sudo journalctl -u kubelet --no-pager | tail -50 | grep -i error
Fix:
# Option 1: Fix the YAML error
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
# Option 2: Restore from backup
sudo cp /tmp/kube-apiserver.yaml.bak /etc/kubernetes/manifests/kube-apiserver.yaml
The kubelet will automatically detect the corrected file and restart the API server. Wait 30-60 seconds, then verify with kubectl get pods -n kube-system.
Prevention: Always back up the manifest before editing: sudo cp /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/kube-apiserver.yaml.bak
Q3: A kube-bench scan shows that kubelet protectKernelDefaults is failing. After setting it to true, kubelet won’t start. Why?
Show Answer
When protectKernelDefaults: true, the kubelet checks that required kernel parameters are already set correctly before starting. If they’re not, kubelet refuses to start.
Fix: Set the required kernel parameters first:
sudo sysctl -w vm.overcommit_memory=1
sudo sysctl -w vm.panic_on_oom=0
sudo sysctl -w kernel.panic=10
sudo sysctl -w kernel.panic_on_oops=1
# Make persistent
cat <<EOF | sudo tee /etc/sysctl.d/90-kubelet.conf
vm.overcommit_memory=1
vm.panic_on_oom=0
kernel.panic=10
kernel.panic_on_oops=1
EOF
sudo sysctl --system
Then restart kubelet:
sudo systemctl restart kubelet
Q4: Write a complete kube-bench Job YAML that scans a worker node. Include the critical spec fields.
Show Answer
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench-node
namespace: default
spec:
template:
spec:
hostPID: true
nodeSelector:
node-role.kubernetes.io/worker: ""
containers:
- name: kube-bench
image: aquasec/kube-bench:v0.8.0
command: ["kube-bench", "run", "--targets", "node"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
readOnly: true
- name: etc-systemd
mountPath: /etc/systemd
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
- name: lib-systemd
mountPath: /lib/systemd
readOnly: true
restartPolicy: Never
volumes:
- name: var-lib-kubelet
hostPath:
path: /var/lib/kubelet
- name: etc-systemd
hostPath:
path: /etc/systemd
- name: etc-kubernetes
hostPath:
path: /etc/kubernetes
- name: lib-systemd
hostPath:
path: /lib/systemd
backoffLimit: 0
Key differences from master Job: different nodeSelector (worker node), no control-plane tolerations, --targets node instead of --targets master, fewer volume mounts (no etcd directories).
Q5: After all remediation, your scan shows 0 FAIL but still 4 WARN. The exam asks you to “ensure the cluster passes CIS benchmarks.” Is your work done?
Show Answer
Yes — for the CKS exam, 0 FAIL is the target. WARN results mean kube-bench couldn’t automatically verify the check, not that it failed. Common WARNs like EventRateLimit and AlwaysPullImages are Level 2 advisory checks.
On the exam:
- Fix all FAIL results — this is what’s being scored
- WARN results don’t need to be fixed unless the exam specifically asks for them
- Focus your time on FAILs and move on once they’re all PASS
In production, you’d review each WARN and document a justification for accepting or fixing it. But on the exam, time is limited — don’t chase WARNs.
Congratulations! You’ve completed the CIS Benchmarks & kube-bench chapter. You can now scan clusters, interpret results, and fix the most common security misconfigurations — the exact skills tested on the CKS exam.
| ← Remediation Strategies | Next Chapter → |