Minimize the host OS

Cut services, ports, and packages on the node.

Advanced12 min · lesson 10 of 24

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.

terminal
$ 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 start
Created 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.

terminal
$ 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.

terminal
# 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
The node is inside the trust boundary
A compromised node hands over the kubelet credential, the secrets of every pod scheduled there, and the container runtime socket — which is root-equivalent control of the host. Host hardening protects the layer that every NetworkPolicy, RBAC rule, and Pod Security label silently depends on.