<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Kubernetes on Ethan-ZYF</title><link>https://ethanzyf.com/tags/kubernetes/</link><description>Recent content in Kubernetes on Ethan-ZYF</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Wed, 01 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://ethanzyf.com/tags/kubernetes/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes 7-Day Intensive: Day 1 — kubectl Mastery</title><link>https://ethanzyf.com/blog/k8s-day1-notes/</link><pubDate>Wed, 01 Jul 2026 00:00:00 +0000</pubDate><guid>https://ethanzyf.com/blog/k8s-day1-notes/</guid><description>&lt;h2 id="environment-setup-day-0"&gt;
 Environment Setup (Day 0)
 &lt;a class="heading-link" href="#environment-setup-day-0"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Cluster:&lt;/strong&gt; kind (Kubernetes in Docker) — 1 control-plane + 2 worker nodes, Cilium CNI.&lt;/p&gt;
&lt;p&gt;Key &lt;code&gt;kind-cluster.yaml&lt;/code&gt; decisions:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Value&lt;/th&gt;
 &lt;th&gt;Why&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;disableDefaultCNI&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Remove kindnet, install Cilium manually&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;podSubnet&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;10.244.0.0/16&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;~65k addresses for Pods&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;serviceSubnet&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;10.96.0.0/16&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Virtual IPs for Services, must not overlap podSubnet&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;extraPortMappings&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;80/443&lt;/td&gt;
 &lt;td&gt;Map host ports into control-plane for Day 4 Ingress&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ingress-ready=true&lt;/code&gt; label&lt;/td&gt;
 &lt;td&gt;via &lt;code&gt;kubeadmConfigPatches&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Tells ingress-nginx which node to schedule on&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;kubeadmConfigPatches&lt;/code&gt; embeds a &lt;strong&gt;kubeadm YAML string&lt;/strong&gt; inside kind&amp;rsquo;s YAML (using &lt;code&gt;|&lt;/code&gt; block scalar). Two separate schemas nested together — easy to confuse.&lt;/p&gt;</description></item><item><title>Week2 Day 4: K8s security — AppArmor</title><link>https://ethanzyf.com/blog/week2-day4-apparmor/</link><pubDate>Thu, 21 May 2026 00:00:00 -0700</pubDate><guid>https://ethanzyf.com/blog/week2-day4-apparmor/</guid><description>&lt;p&gt;AppArmor as the path-based complement to seccomp&amp;rsquo;s syscall-based filtering — and a hands-on lesson in why that complement is harder to deploy. This post covers how K8s wraps AppArmor (&lt;code&gt;securityContext.appArmorProfile&lt;/code&gt;, symmetric with seccomp&amp;rsquo;s three types), why an AppArmor profile is heavier than a seccomp one (it must be pre-loaded into the node&amp;rsquo;s kernel via &lt;code&gt;apparmor_parser&lt;/code&gt;, not just dropped as a file), why &lt;code&gt;deny /data/** w&lt;/code&gt; blocks every syscall that writes there (closing the hole where &lt;code&gt;touch&lt;/code&gt; bypassed yesterday&amp;rsquo;s mkdir block), and the day&amp;rsquo;s most valuable lesson: &lt;strong&gt;the experiment couldn&amp;rsquo;t run at all&lt;/strong&gt; because a Mac/Docker-Desktop kind node has no AppArmor LSM in its kernel — a firsthand encounter with the fail-open trap and the portability problem of AppArmor-based mitigations.&lt;/p&gt;</description></item><item><title>Week2 Day 3: K8s security — seccomp</title><link>https://ethanzyf.com/blog/week2-day3-seccomp/</link><pubDate>Wed, 20 May 2026 00:00:00 -0700</pubDate><guid>https://ethanzyf.com/blog/week2-day3-seccomp/</guid><description>&lt;p&gt;Where Week 1 and Week 2 meet: K8s seccomp is the &lt;em&gt;same&lt;/em&gt; kernel seccomp from Week 1, except runc installs the BPF filter for you instead of your application. This post traces the full kubelet → containerd → runc chain (and what runc actually does — namespaces, cgroups, NNP, caps, seccomp, AppArmor, then exec), the three profile types (Unconfined / RuntimeDefault / Localhost), why the profile JSON is just the declarative form of the cBPF you&amp;rsquo;d hand-write, why &lt;code&gt;SCMP_ACT_ERRNO&lt;/code&gt; lets the process survive while &lt;code&gt;mkdir&lt;/code&gt; returns EPERM, and the crucial limitation: seccomp blocks &lt;em&gt;syscalls&lt;/em&gt;, not &lt;em&gt;intent&lt;/em&gt; — block &lt;code&gt;mkdir&lt;/code&gt; and &lt;code&gt;touch&lt;/code&gt; (which uses &lt;code&gt;openat&lt;/code&gt;) still creates files. Plus &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/status&lt;/code&gt; as the ground-truth proof a filter is loaded.&lt;/p&gt;</description></item><item><title>Week2 Day 2: K8s security — NetworkPolicy + Secrets</title><link>https://ethanzyf.com/blog/week2-day2-networkpolicy-secrets/</link><pubDate>Tue, 19 May 2026 00:00:00 -0700</pubDate><guid>https://ethanzyf.com/blog/week2-day2-networkpolicy-secrets/</guid><description>&lt;p&gt;Hands-on with the network and secret-data layers of Kubernetes security: why the default flat network is a lateral-movement risk, the deeply counterintuitive &amp;ldquo;selective activation&amp;rdquo; model of NetworkPolicy (one allow rule silently flips a pod to default-deny), why Ingress and Egress are independent dimensions (and why locking egress breaks DNS), the YAML trap where a single &lt;code&gt;-&lt;/code&gt; flips AND into OR, why NetworkPolicy needs a CNI that actually enforces it (kindnet doesn&amp;rsquo;t — another fail-open), and the truth about Secrets: they&amp;rsquo;re base64, not encrypted — RBAC is what protects them — plus the atomic-symlink-swap trick that powers volume-mounted secret rotation.&lt;/p&gt;</description></item><item><title>Week2 Day 1: K8s security — Pod Security Standards + RBAC</title><link>https://ethanzyf.com/blog/week2-day1-k8s-psa-rbac/</link><pubDate>Mon, 18 May 2026 00:00:00 -0700</pubDate><guid>https://ethanzyf.com/blog/week2-day1-k8s-psa-rbac/</guid><description>&lt;p&gt;A first hands-on pass at Kubernetes-native security: how Pod Security Admission (PSA) actually attaches to namespaces via labels (and silently fails-open when the label is misspelled), why &lt;code&gt;runAsNonRoot: true&lt;/code&gt; is necessary but not sufficient (the image&amp;rsquo;s &lt;code&gt;USER&lt;/code&gt; still has to be non-root), the four-field minimum a pod needs to clear the &lt;code&gt;restricted&lt;/code&gt; profile, and the full RBAC mental model — &lt;code&gt;Role&lt;/code&gt;/&lt;code&gt;ClusterRole&lt;/code&gt; × &lt;code&gt;RoleBinding&lt;/code&gt;/&lt;code&gt;ClusterRoleBinding&lt;/code&gt; as &lt;strong&gt;two independent dimensions&lt;/strong&gt; that produce four combinations (one of which K8s rejects outright), why RBAC evaluation is &lt;strong&gt;union, not override&lt;/strong&gt;, and the asymmetry that makes RBAC fail-closed while PSA fails open.&lt;/p&gt;</description></item></channel></rss>