Files
CKS-Lessons/lessons/0031-cel-privileged-container-policy.html
2026-08-25 15:12:00 +06:00

58 lines
4.3 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Lesson 31: CEL privileged-container policy</title>
<link rel="stylesheet" href="../assets/course.css">
</head>
<body>
<main>
<p class="eyebrow">Lesson 0031 · 25 minutes · Cluster Hardening</p>
<h1>Reject privileged containers with native admission control</h1>
<p class="lede">Write a policy and binding from memory, then prove both enforcement and namespace scope. This is a focused retrieval drill: no webhook and no third-party policy engine.</p>
<h2>Timed task: 15 minutes</h2>
<p>Apply <code>lab/lesson-31-fixture.yaml</code>. Create <code>lab/lesson-31-answer.yaml</code> from scratch with exactly two resources:</p>
<ul>
<li>one <code>ValidatingAdmissionPolicy</code>; and</li>
<li>one <code>ValidatingAdmissionPolicyBinding</code>.</li>
</ul>
<p>The policy must evaluate Pod <code>CREATE</code> and <code>UPDATE</code> requests. It must reject a Pod when any regular container or init container explicitly sets <code>securityContext.privileged: true</code>.</p>
<p>The binding must enforce the policy with <code>Deny</code> only in namespaces labelled <code>cks-training=admission</code>. Use a helpful validation message. Do not reject containers that omit <code>securityContext</code> or omit <code>privileged</code>.</p>
<div class="callout"><strong>Retrieval target:</strong> optional fields are the trap. Your CEL must safely handle a missing <code>securityContext</code> and a missing <code>privileged</code> field. Remember that the policy defines logic and request matching; the binding supplies enforcement action and namespace scope.</div>
<h2>Verification</h2>
<pre><code>kubectl apply -f .\lab\lesson-31-fixture.yaml
kubectl apply -f .\lab\lesson-31-answer.yaml
kubectl -n admission-lab run allowed --image=nginx:1.27-alpine --restart=Never
kubectl -n admission-lab run denied --image=nginx:1.27-alpine --restart=Never --overrides='{"spec":{"containers":[{"name":"denied","image":"nginx:1.27-alpine","securityContext":{"privileged":true}}]}}'
kubectl -n admission-control run outside-scope --image=nginx:1.27-alpine --restart=Never --overrides='{"spec":{"containers":[{"name":"outside-scope","image":"nginx:1.27-alpine","securityContext":{"privileged":true}}]}}'</code></pre>
<p>Expected: <code>allowed</code> is admitted, <code>denied</code> is rejected with your message, and <code>outside-scope</code> is admitted.</p>
<h2>Retrieval check</h2>
<div class="quiz">
<h3>Question</h3>
<p>Where should the namespace selector and <code>Deny</code> action be configured?</p>
<label><input type="radio" name="q" value="binding">In the policy binding, which scopes and enforces the policy.</label>
<label><input type="radio" name="q" value="policy">In the policy validation, which replaces the binding entirely.</label>
<label><input type="radio" name="q" value="namespace">In Namespace annotations, which execute the CEL expression directly.</label>
<button onclick="check()">Check answer</button>
<p id="feedback" class="feedback" aria-live="polite"></p>
</div>
<h2>Documentation target</h2>
<p>Use the official <a href="https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/">Validating Admission Policy documentation</a> only after your first attempt. Inspect local schemas with <code>kubectl explain validatingadmissionpolicy.spec</code> and <code>kubectl explain validatingadmissionpolicybinding.spec</code>.</p>
<h2>What to send me</h2>
<p>Save the answer, run the three checks, then say <em>check</em>. I will review only the first issue at a time. Ask me a follow-up question whenever a field or CEL expression is unclear.</p>
<footer>Previous: <a href="0030-generate-image-sbom.html">Generate an image SBOM</a> · CKS path: Cluster Hardening</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==='binding'){f.textContent='Correct. The binding selects namespaces and chooses the enforcement action.';f.className='feedback good';}else{f.textContent='Not quite. The policy defines validation logic; the binding scopes and enforces it.';f.className='feedback bad';}}
</script>
</body>
</html>