Minimize the host OS
Cut services, ports, and packages on the node.
Kubernetes runs on top of a Linux host, and every cluster-level control you configure is only as trustworthy as the node underneath it. Root on a node means the kubelet’s credentials, every pod’s secrets sitting in tmpfs, and the container runtime socket — so system hardening is not optional polish, it is the floor the whole cluster stands on. The method is footprint reduction: every running service, listening port, and installed package is attack surface, and a cluster node should run what Kubernetes needs and close to nothing else.
Start by inventorying what is actually running and listening, then disable and mask the services that have no business on a node — a print spooler, a mail transfer agent, a desktop daemon. Masking is stronger than disabling: it points the unit at /dev/null so nothing can start it again, on purpose or by dependency.
$ systemctl list-units --type=service --state=running # what runs today$ ss -tulnp # every TCP/UDP listener + owning process$ sudo systemctl disable --now cups # stop + never start on boot$ sudo systemctl mask cups # hard-block any future startCreated symlink /etc/systemd/system/cups.service -> /dev/null.
Cut packages and close ports
Remove build toolchains, compilers, and interactive extras from production nodes — an attacker who lands on the box should not find gcc, git, and a package manager waiting. Then put a default-deny host firewall in front of the node and open only the ports Kubernetes genuinely uses: the API server and etcd on control-plane nodes, the kubelet and NodePort range on workers. Fewer ways onto the machine means fewer ways off it after a breach.
$ sudo ufw default deny incoming$ sudo ufw allow 6443/tcp # kube-apiserver (control plane)$ sudo ufw allow 2379:2380/tcp # etcd client + peer (control plane)$ sudo ufw allow 10250/tcp # kubelet API (all nodes)$ sudo ufw allow 30000:32767/tcp # NodePort range, if you use it$ sudo ufw enable
Kernel and access hardening
Trim the kernel’s own surface: blacklist filesystem and network modules you never load (a container should not be mounting exotic filesystems), and set the sysctl knobs that stop a node being used as a router or a pivot. On the access side, no shared root SSH and least-privilege sudo — logins should be attributable to named humans before any escalation, because a shared root login destroys your audit trail before an incident even starts.
# stop the node forwarding traffic it should not (unless it is a router)$ echo "net.ipv4.ip_forward=0" | sudo tee /etc/sysctl.d/90-hardening.conf$ sudo sysctl --system# refuse to load a module a workload has no reason to need$ echo "install dccp /bin/true" | sudo tee /etc/modprobe.d/blocklist.conf