CIS Benchmarks as controls
Automated hardening assessment, continuously.
Picture the moment an auditor points at your cluster and says, "Prove the control plane is hardened." You could hand over a wiki page describing how you configured it last year, or you could run one command that inspects the live API server, controller manager, kubelet, and etcd against a published standard and prints a pass/fail line for every setting. CIS Benchmarks are what make the second option possible, and they are the most directly automatable framework in this course: not prose about intent, but a numbered list of concrete settings you can check, score, and gate a pipeline on.
Consensus hardening baselines, defined
A CIS Benchmark is a consensus-developed hardening guide published by the Center for Internet Security for one specific target: Ubuntu 22.04, Amazon Web Services, Kubernetes, Docker, PostgreSQL, and dozens more. Each benchmark is a numbered list of recommendations, and each recommendation carries a rationale, an audit procedure (how to check it), and a remediation (how to fix it). Two dimensions matter in practice. Recommendations split into Level 1 (sensible baseline hardening with minimal operational impact) and Level 2 (defense-in-depth that may break workloads), and each item is either scored (objectively pass/fail) or not-scored / manual (needs human judgement). Because the audit procedures are concrete, "ensure the --anonymous-auth argument is set to false", "ensure CloudTrail is enabled in all regions", they map one-to-one onto automated checks. That is the whole trick of compliance as code: a control stops being a sentence and becomes an executable assertion with a verdict. Benchmarks are also versioned (CIS AWS Foundations v3.0, CIS Kubernetes 1.9), so pin the version your auditor expects rather than whatever the tool defaults to.
Assessing Kubernetes with kube-bench
kube-bench is the open-source scanner from Aqua Security that implements the CIS Kubernetes Benchmark. It reports [PASS], [FAIL], [WARN], or [INFO] for each item, where [WARN] means the check is manual or could not be verified automatically and [INFO] is purely informational. Install the binary on a control-plane node and point it at both the master and node profiles in one run.
# On a control-plane node: fetch and install the latest kube-bench binaryVER=$(curl -s https://api.github.com/repos/aquasecurity/kube-bench/releases/latest \| grep -oP '"tag_name":\s*"v\K[^"]+')curl -sL "https://github.com/aquasecurity/kube-bench/releases/download/v${VER}/kube-bench_${VER}_linux_amd64.tar.gz" | tar -xzsudo install kube-bench /usr/local/bin/sudo mkdir -p /etc/kube-bench && sudo cp -r cfg /etc/kube-bench/# Assess the control plane AND this node against the CIS Kubernetes Benchmark.# kube-bench auto-detects the benchmark version from the running kube-apiserver.sudo kube-bench run --targets master,node
A trimmed run makes the shape obvious: grouped section headers, one verdict per item, remediation text for anything that failed, and a per-target summary.
[INFO] 1 Control Plane Security Configuration[INFO] 1.1 Control Plane Node Configuration Files[PASS] 1.1.1 Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)[INFO] 1.2 API Server[PASS] 1.2.19 Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Automated)[WARN] 1.2.1 Ensure that the --anonymous-auth argument is set to false (Manual)[INFO] 1.3 Controller Manager[FAIL] 1.3.2 Ensure that the --profiling argument is set to false (Automated)== Remediation master ==1.3.2 Edit the Controller Manager pod specification file/etc/kubernetes/manifests/kube-controller-manager.yaml on the control-planenode and set the below parameter:--profiling=false== Summary master ==41 checks PASS9 checks FAIL11 checks WARN0 checks INFO
Read a [FAIL] as an actionable ticket. Item 1.3.2 names the exact file and flag to change, and kube-bench prints that remediation verbatim so an engineer never has to guess. The summary line is your score, 41 pass against 9 fail, the single number a dashboard and an auditor both understand. Under the hood kube-bench does not call the Kubernetes API at all: it reads the command-line flags of the running processes from /proc, checks file ownership and permissions on the manifests and kubeconfigs, and compares them to the benchmark's expected values. That is why it must run on the node itself (or as a privileged Job with hostPID), and why it physically cannot inspect a control plane you do not operate.
Turning a scan into a gate
A scanner that only prints is a report; a scanner that fails the build is a control. Here is the trap most teams hit first: kube-bench returns exit code 0 even when checks fail, so a naive pipeline stays green while the cluster drifts. Force a non-zero exit with --exit-code, or emit JSON and gate on the fail count yourself.
# kube-bench returns 0 even when checks FAIL, so a naive job stays green.# Option A: force a non-zero exit code when any check fails.kube-bench run --targets master,node --exit-code 1# Option B: emit JSON and gate on the fail count in your pipeline.kube-bench run --targets master,node --json > kb.jsonfails=$(jq '.Totals.total_fail' kb.json)echo "failed CIS checks: $fails"[ "$fails" -gt 0 ] && { echo "CIS gate: BLOCK merge"; exit 1; }
failed CIS checks: 10CIS gate: BLOCK merge$ echo $?1
Cloud posture with Prowler
For cloud accounts the equivalent is Prowler, an open-source scanner that assesses AWS, Azure, GCP, and Kubernetes and ships the CIS mappings built in. One flag runs the entire CIS AWS Foundations Benchmark v3.0 and labels every finding with its CIS control number.
# Install Prowler (open-source, Python) and run the full CIS AWS v3.0 assessment.pipx install prowlerprowler aws --compliance cis_3.0_aws
FAIL cloudtrail_multi_region_enabled [us-east-1]No multi-region CloudTrail trail capturing management events was found.CIS 3.0 AWS -> 3.1 Ensure CloudTrail is enabled in all regionsRemediation: aws cloudtrail update-trail --name my-trail \--is-multi-region-trail --enable-log-file-validationFAIL iam_rotate_access_key_90_days [global]CIS 3.0 AWS -> 1.14 Ensure access keys are rotated every 90 days or lessCompliance Status of CIS 3.0 AWS:Requirements passed: 60 / 62 (96.8%)Failing requirements: 3.1, 1.14Detailed report written to: output/prowler-output-123456789012-cis_3.0_aws.csv$ echo $?3 # Prowler exits 3 when any check fails -> it gates CI without extra wiring
Because a single Prowler run evaluates hundreds of checks and each maps to one or more CIS controls, one scan produces evidence for dozens of controls at once, the leverage that makes benchmark automation worth wiring in. Note the exit codes differ by tool: Prowler returns 3 on any failure, so it gates CI out of the box, while kube-bench returns 0 and must be forced. Steampipe's CIS compliance mods are a SQL-based alternative that query the same posture and emit the same pass/fail rows if you prefer to store results in a database.
Run these in two places. In CI you assess images and IaC before they ship (shift-left), catching a misconfiguration before it ever exists. Against live accounts and clusters you assess continuously (runtime posture management), because a setting that passed at deploy can regress an hour later when someone toggles it in the console. The benchmark becomes a living control set that re-proves itself on every run and flags drift the moment it appears, which is exactly the continuous compliance the rest of this course builds toward.
kube-bench run --targets master,node, the log clearly shows 9 [FAIL] items, yet the pipeline reports success. What is the most likely cause?kube-bench run --targets master,node and the master section returns FAIL for API server and etcd items you have no way to change. What do you do next?CIS Benchmarks give you hundreds of pre-written checks for free, but they only cover what the consensus anticipated. The moment you need an organization-specific rule, "every S3 bucket must carry a data-classification tag", "no container may run as root in the payments namespace", you have to write the check yourself. That is exactly what the next lesson does with OPA and Rego: the general-purpose policy engine that lets you express your own controls in the same executable, gate-able form you just saw a benchmark produce.
Try this
Work through “Cloud posture with Prowler” yourself on a sandbox you can throw away, following the commands above in order. Then break one step deliberately and re-run, so you have seen the failure before it finds you.
Takeaway
The trap worth remembering here: managed control planes turn master checks into false positives. Check that on your own systems before you need to, because it is cheaper to find on a quiet afternoon than during an incident.