58 lines
4.3 KiB
HTML
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>
|