CKS — Minimize Host OS Footprint
Minimize Host OS Footprint
Every package, service, and open port on a node is an attack vector. If an attacker escapes a container, a bloated host gives them tools to pivot. A minimal host gives them nothing.
The CKS exam tests your ability to identify and remove unnecessary software, disable services, harden SSH, and choose minimal OS images.
1. Remove Unnecessary Packages
Production nodes should run only what Kubernetes needs: a container runtime, kubelet, and kube-proxy. Everything else is a liability.
Audit Installed Packages
# Debian/Ubuntu — list all installed packages
dpkg -l | wc -l
# List packages sorted by size
dpkg-query -W --showformat='${Installed-Size}\t${Package}\n' | sort -rn | head -20
# Red Hat/CentOS
rpm -qa | wc -l
Remove Packages
# Remove a package and its config files
sudo apt purge -y wget curl netcat telnet tcpdump
# Remove packages that are no longer needed
sudo apt autoremove -y
# Red Hat/CentOS
sudo yum remove -y wget curl nmap-ncat telnet tcpdump
Dangerous Packages on Nodes
| Package | Risk | Remove? |
|---|---|---|
wget / curl |
Download malicious payloads | Yes |
netcat / ncat |
Reverse shells, port scanning | Yes |
tcpdump |
Sniff network traffic | Yes |
telnet |
Unencrypted remote access | Yes |
gcc / make |
Compile malicious code on the node | Yes |
python / perl |
Scripting for post-exploitation | Evaluate |
Security Impact: A container escape with
curlandgccavailable on the host lets an attacker download and compile exploit code in seconds.
2. Disable Unnecessary Services
Every running service is a potential entry point. Audit and disable what you don’t need.
Audit Running Services
# List all active services
systemctl list-units --type=service --state=running
# List all enabled services (start on boot)
systemctl list-unit-files --type=service --state=enabled
# Check listening ports and associated services
ss -tlnp
Disable Services
# Stop and disable a service
sudo systemctl stop bluetooth.service
sudo systemctl disable bluetooth.service
# Mask a service (prevent it from starting even manually)
sudo systemctl mask bluetooth.service
# Verify
systemctl is-enabled bluetooth.service
# Output: masked
Services to Disable on Kubernetes Nodes
| Service | Purpose | Disable? |
|---|---|---|
bluetooth |
Bluetooth hardware | Yes |
cups |
Printing | Yes |
avahi-daemon |
mDNS service discovery | Yes |
rpcbind |
NFS/RPC services | Usually yes |
snapd |
Snap package manager | Usually yes |
ModemManager |
Cellular modems | Yes |
irqbalance |
IRQ distribution | Keep — improves performance |
chrony / ntp |
Time synchronization | Keep — required for TLS |
sshd |
Remote access | Keep — but harden |
3. Disable Unused Ports and Protocols
Audit Open Ports
# Show all listening TCP ports with process names
ss -tlnp
# Show all listening UDP ports
ss -ulnp
# Alternative with netstat
netstat -tlnp
Firewall Configuration with UFW
# Enable UFW
sudo ufw enable
# Default deny incoming, allow outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow only required Kubernetes ports
sudo ufw allow 6443/tcp # API server
sudo ufw allow 10250/tcp # Kubelet API
sudo ufw allow 10259/tcp # kube-scheduler
sudo ufw allow 10257/tcp # kube-controller-manager
sudo ufw allow 2379:2380/tcp # etcd
sudo ufw allow 30000:32767/tcp # NodePort range (if needed)
# Allow SSH from specific subnet only
sudo ufw allow from 10.0.0.0/8 to any port 22
# Check status
sudo ufw status verbose
Firewall with iptables
# Drop all incoming by default
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Allow established connections
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Allow API server
sudo iptables -A INPUT -p tcp --dport 6443 -j ACCEPT
# Allow kubelet
sudo iptables -A INPUT -p tcp --dport 10250 -j ACCEPT
# Allow SSH from management subnet
sudo iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
# Save rules
sudo iptables-save > /etc/iptables/rules.v4
4. Minimal OS Images
Using a minimal, purpose-built OS dramatically reduces the attack surface.
| OS | Description | Packages | Read-Only FS | Auto-Update |
|---|---|---|---|---|
| Ubuntu Minimal | Stripped-down Ubuntu server | ~300 | No | No |
| Flatcar Container Linux | Immutable, container-optimized | ~100 | Yes (rootfs) | Yes |
| Talos Linux | API-managed, no SSH, immutable | Minimal | Yes | Yes |
| Bottlerocket | AWS-optimized, immutable | Minimal | Yes | Yes |
| Ubuntu Server (full) | Standard installation | ~1000+ | No | No |
Why Immutable OS Matters
Traditional OS:
┌─────────────────────────────┐
│ Full OS + packages + SSH │ ← Large attack surface
│ Read-write filesystem │ ← Attacker can modify files
│ Manual patching │ ← Drift over time
└─────────────────────────────┘
Immutable OS (Talos/Flatcar):
┌─────────────────────────────┐
│ Minimal OS, no SSH │ ← Tiny attack surface
│ Read-only root filesystem │ ← Attacker can't persist
│ Atomic updates │ ← Consistent state
└─────────────────────────────┘
Security Impact: Flatcar Container Linux ships without a package manager. Even if an attacker escapes a container, they cannot install tools on the host.
5. File System Permissions Hardening
Critical Directories
# Kubernetes config files — restrict to root
sudo chmod 600 /etc/kubernetes/manifests/*.yaml
sudo chmod 600 /etc/kubernetes/pki/*.key
sudo chown root:root /etc/kubernetes/pki/*.key
# kubelet config
sudo chmod 644 /etc/kubernetes/kubelet.conf
sudo chown root:root /etc/kubernetes/kubelet.conf
# etcd data directory
sudo chmod 700 /var/lib/etcd
sudo chown etcd:etcd /var/lib/etcd
CIS Benchmark File Permissions
| File / Directory | Owner | Permissions |
|---|---|---|
/etc/kubernetes/manifests/ |
root:root | 600 |
/etc/kubernetes/pki/*.key |
root:root | 600 |
/etc/kubernetes/pki/*.crt |
root:root | 644 |
/etc/kubernetes/admin.conf |
root:root | 600 |
/var/lib/etcd |
etcd:etcd | 700 |
/var/lib/kubelet/config.yaml |
root:root | 644 |
Set Default umask
# Check current umask
umask
# Set restrictive umask (files: 640, dirs: 750)
echo "umask 027" >> /etc/profile
# Even more restrictive (files: 600, dirs: 700)
echo "umask 077" >> /etc/profile
6. SSH Hardening
SSH is the primary remote access method. If compromised, the attacker owns the node.
Harden /etc/ssh/sshd_config
# Disable root login
PermitRootLogin no
# Disable password authentication (key-only)
PasswordAuthentication no
ChallengeResponseAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Use SSH protocol 2 only
Protocol 2
# Limit authentication attempts
MaxAuthTries 3
# Set idle timeout (seconds)
ClientAliveInterval 300
ClientAliveCountMax 2
# Restrict SSH to specific users
AllowUsers admin deploy
# Restrict to specific subnet (via Match block)
Match Address 10.0.0.0/8
PermitRootLogin no
PasswordAuthentication no
Apply Changes
# Validate config before restart
sudo sshd -t
# Restart SSH
sudo systemctl restart sshd
# Verify
sudo sshd -T | grep -E "permitrootlogin|passwordauthentication"
SSH Key Best Practices
# Generate a strong key pair
ssh-keygen -t ed25519 -C "admin@k8s-node" -f ~/.ssh/k8s_admin
# Copy public key to node
ssh-copy-id -i ~/.ssh/k8s_admin.pub admin@node01
# Set correct permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/k8s_admin
chmod 644 ~/.ssh/k8s_admin.pub
Security Impact: Password-based SSH with root login enabled is the single easiest way to compromise a Kubernetes node. Disabling both removes the most common attack path.
7. Disable Unnecessary Kernel Modules
Kernel modules extend the kernel. Unused modules increase the attack surface.
# List loaded kernel modules
lsmod
# Blacklist unused modules — create a config file
cat <<EOF | sudo tee /etc/modprobe.d/k8s-hardening.conf
# Disable uncommon network protocols
install cramfs /bin/true
install freevxfs /bin/true
install hfs /bin/true
install hfsplus /bin/true
install jffs2 /bin/true
install udf /bin/true
# Disable uncommon network protocols
install dccp /bin/true
install sctp /bin/true
install rds /bin/true
install tipc /bin/true
# Disable USB storage
install usb-storage /bin/true
EOF
# Unload a module immediately
sudo modprobe -r usb-storage
# Verify
lsmod | grep usb-storage
Kernel Parameters Hardening (sysctl)
cat <<EOF | sudo tee /etc/sysctl.d/99-k8s-hardening.conf
# Disable IP forwarding for non-router nodes
# (Note: kube-proxy requires net.ipv4.ip_forward=1)
# Only disable if the node does not run kube-proxy
# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable SYN cookies (SYN flood protection)
net.ipv4.tcp_syncookies = 1
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
# Disable ICMP broadcast responses
net.ipv4.icmp_echo_ignore_broadcasts = 1
EOF
# Apply immediately
sudo sysctl --system
Summary
| Action | Command / Config | Impact |
|---|---|---|
| Remove packages | apt purge curl wget netcat |
Removes post-exploit tools |
| Disable services | systemctl disable --now <svc> |
Fewer listening ports |
| Firewall rules | ufw / iptables |
Block unauthorized access |
| Minimal OS | Flatcar, Talos, Bottlerocket | Immutable, tiny surface |
| File permissions | chmod 600 on keys, umask 077 |
Prevent unauthorized reads |
| SSH hardening | Key-only, no root, idle timeout | Close #1 attack vector |
| Kernel modules | Blacklist unused, sysctl tuning | Reduce kernel surface |
Practice Questions
Q1: A node is running unnecessary services including bluetooth, cups, and avahi-daemon. How do you disable and prevent them from starting?
# Stop, disable, and mask each service
for svc in bluetooth cups avahi-daemon; do
sudo systemctl stop ${svc}.service
sudo systemctl disable ${svc}.service
sudo systemctl mask ${svc}.service
done
# Verify
systemctl is-enabled bluetooth.service # masked
systemctl is-enabled cups.service # masked
systemctl is-enabled avahi-daemon.service # masked
mask prevents the service from being started even manually — stronger than disable.
Q2: You need to ensure SSH access to the node only allows key-based authentication, disables root login, and disconnects idle sessions after 10 minutes. Write the sshd_config changes.
Edit /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
ClientAliveInterval 300 × ClientAliveCountMax 2 = 600 seconds (10 minutes) before disconnect.
sudo sshd -t && sudo systemctl restart sshd
Q3: List all listening TCP ports on a node and identify which process owns each. What command do you use?
ss -tlnp
Output columns:
- State — LISTEN
- Local Address:Port — The listening address and port
- Process — The PID and command name
Example:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:6443 0.0.0.0:* users:(("kube-apiserver",pid=1234,fd=7))
LISTEN 0 128 0.0.0.0:10250 0.0.0.0:* users:(("kubelet",pid=5678,fd=8))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=910,fd=3))
Investigate any unexpected listening ports.
Q4: The /etc/kubernetes/pki/ directory on a node has private keys readable by all users. Fix the permissions to meet CIS Benchmark recommendations.
# Fix private key permissions
sudo chmod 600 /etc/kubernetes/pki/*.key
sudo chown root:root /etc/kubernetes/pki/*.key
# Fix certificate permissions
sudo chmod 644 /etc/kubernetes/pki/*.crt
sudo chown root:root /etc/kubernetes/pki/*.crt
# Verify
ls -la /etc/kubernetes/pki/
CIS Benchmark requires:
- Private keys (
.key):600(owner read/write only) - Certificates (
.crt):644(owner read/write, others read) - Owner:
root:rootfor all files
Q5: You're choosing a host OS for a new production cluster. What are the security advantages of Talos Linux over a standard Ubuntu Server installation?
| Feature | Talos Linux | Ubuntu Server |
|---|---|---|
| SSH access | None — API only | Yes |
| Package manager | None | apt |
| Root filesystem | Read-only, immutable | Read-write |
| Updates | Atomic, API-driven | Manual apt upgrade |
| Shell access | None | Full bash |
| Attack surface | Minimal | Large |
Key advantages:
- No SSH — eliminates the most common attack vector
- No shell — even with a container escape, no interactive shell on the host
- No package manager — attacker cannot install tools
- Immutable filesystem — attacker cannot persist malware
- API-managed — all management through authenticated, encrypted API calls
| Previous | Up | Next |
|---|---|---|
| System Hardening | System Hardening | IAM Roles |