CKS — Minimize Host OS Footprint

8 minute read

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 curl and gcc available 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:root for 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:

  1. No SSH — eliminates the most common attack vector
  2. No shell — even with a container escape, no interactive shell on the host
  3. No package manager — attacker cannot install tools
  4. Immutable filesystem — attacker cannot persist malware
  5. API-managed — all management through authenticated, encrypted API calls

Previous Up Next
System Hardening System Hardening IAM Roles

Updated: