Skip to content

OS and Kernel Hardening

Overview

Kernel and OS hardening focuses on reducing the attack surface of the node operating system itself. Every unnecessary package, service, or kernel feature is a potential entry point for attackers. The principle is straightforward: if you don't need it, remove it or disable it.

This section covers the host-level security measures that complement container-level controls like AppArmor, seccomp, and capabilities.

CKS Exam Context

While the exam is primarily focused on Kubernetes-level security, you may encounter questions that require SSH-ing into a node to:

  • Disable unnecessary services
  • Check or modify kernel parameters
  • Verify installed packages
  • Inspect the host OS configuration

Kernel Security Layers

Minimizing Host OS Attack Surface

Principle: Minimal Base OS

A Kubernetes node should run the minimum software necessary to function as a cluster member. Purpose-built container OSes include:

OSDescription
Ubuntu Minimal / ServerReduced package set, widely used
Amazon Linux 2AWS-optimized, minimal by default
Flatcar Container LinuxImmutable OS designed for containers
BottlerocketAWS container OS, read-only root
Talos LinuxKubernetes-focused, API-managed, no SSH
Google Container-Optimized OSGKE nodes, minimal and auto-updated

Reducing Installed Packages

bash
# List all installed packages (Debian/Ubuntu)
dpkg -l | wc -l

# List packages with descriptions
dpkg -l

# Remove unnecessary packages
sudo apt remove --purge <package-name>

# Remove unused dependencies
sudo apt autoremove

# List installed packages (RHEL/CentOS)
rpm -qa | wc -l

# Remove a package
sudo yum remove <package-name>
# or
sudo dnf remove <package-name>

What to Remove

Common packages that should not be on a Kubernetes node:

  • Desktop environments (GNOME, KDE, X11)
  • Web browsers (firefox, chromium)
  • Office suites (libreoffice)
  • Development tools (gcc, make) -- unless needed for kernel modules
  • FTP servers (vsftpd, proftpd)
  • Mail servers (postfix, sendmail)
  • Unused language runtimes (PHP, Ruby, Perl)
  • Legacy remote access (telnet, rsh)

Checking for Unnecessary Software

bash
# Find packages that provide network services
dpkg -l | grep -E '(apache|nginx|mysql|postgres|ftp|telnet|mail|samba)'

# Find setuid binaries (potential privilege escalation)
find / -perm -4000 -type f 2>/dev/null

# Find setgid binaries
find / -perm -2000 -type f 2>/dev/null

# Find world-writable files
find / -perm -0002 -type f 2>/dev/null

# Find world-writable directories
find / -perm -0002 -type d 2>/dev/null

Disabling Unnecessary Services

Every running service is a potential attack vector. Disable services that are not required for Kubernetes node operation.

Listing and Disabling Services

bash
# 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

# Disable and stop a service
sudo systemctl disable --now <service-name>

# Check if a service is running
systemctl status <service-name>

# Mask a service (prevent it from being started at all)
sudo systemctl mask <service-name>

Services to Keep on a Kubernetes Node

ServicePurposeRequired?
kubeletKubernetes node agentYes
containerd / cri-oContainer runtimeYes
systemd-resolvedDNS resolutionYes
systemd-networkd / NetworkManagerNetwork managementYes
sshdRemote access (for management)Usually yes
chrony / ntpTime synchronizationYes
auditdAudit loggingRecommended

Services to Disable on a Kubernetes Node

bash
# Disable common unnecessary services
sudo systemctl disable --now cups.service        # Print service
sudo systemctl disable --now avahi-daemon.service # mDNS/DNS-SD
sudo systemctl disable --now bluetooth.service    # Bluetooth
sudo systemctl disable --now apache2.service      # Web server
sudo systemctl disable --now nginx.service        # Web server
sudo systemctl disable --now postfix.service      # Mail
sudo systemctl disable --now smbd.service         # Samba/SMB
sudo systemctl disable --now nmbd.service         # NetBIOS
sudo systemctl disable --now snapd.service        # Snap packages
sudo systemctl disable --now ModemManager.service # Modem

sysctl Security Parameters

sysctl controls kernel parameters at runtime. Several parameters are critical for node security.

Network Security Parameters

bash
# Prevent IP forwarding (unless node is a router/gateway)
# Note: Kubernetes REQUIRES ip_forward to be enabled for pod networking
sudo sysctl -w net.ipv4.ip_forward=1  # Keep enabled for Kubernetes

# Disable source routing (prevent IP spoofing)
sudo sysctl -w net.ipv4.conf.all.accept_source_route=0
sudo sysctl -w net.ipv4.conf.default.accept_source_route=0

# Enable reverse path filtering (prevent IP spoofing)
sudo sysctl -w net.ipv4.conf.all.rp_filter=1
sudo sysctl -w net.ipv4.conf.default.rp_filter=1

# Disable ICMP redirects (prevent routing manipulation)
sudo sysctl -w net.ipv4.conf.all.accept_redirects=0
sudo sysctl -w net.ipv4.conf.default.accept_redirects=0
sudo sysctl -w net.ipv4.conf.all.send_redirects=0

# Ignore ICMP broadcast requests (prevent Smurf attacks)
sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1

# Enable SYN cookies (prevent SYN flood attacks)
sudo sysctl -w net.ipv4.tcp_syncookies=1

# Log suspicious packets (martians)
sudo sysctl -w net.ipv4.conf.all.log_martians=1

Kernel Security Parameters

bash
# Restrict kernel pointer leaks (prevent KASLR bypass)
sudo sysctl -w kernel.kptr_restrict=2

# Restrict dmesg access to root
sudo sysctl -w kernel.dmesg_restrict=1

# Restrict perf_event access
sudo sysctl -w kernel.perf_event_paranoid=3

# Disable SysRq key (prevent system manipulation)
sudo sysctl -w kernel.sysrq=0

# Restrict ptrace scope (prevent process tracing)
# 0 = no restrictions, 1 = only child processes, 2 = admin only, 3 = disabled
sudo sysctl -w kernel.yama.ptrace_scope=2

# Enable ASLR (Address Space Layout Randomization)
sudo sysctl -w kernel.randomize_va_space=2

# Restrict core dumps
sudo sysctl -w fs.suid_dumpable=0

# Restrict unprivileged user namespaces (prevent container escape vectors)
sudo sysctl -w kernel.unprivileged_userns_clone=0

Making sysctl Changes Persistent

bash
# Add to /etc/sysctl.d/99-kubernetes-hardening.conf
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-hardening.conf
# Network hardening
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.log_martians = 1

# Kernel hardening
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.perf_event_paranoid = 3
kernel.sysrq = 0
kernel.yama.ptrace_scope = 2
kernel.randomize_va_space = 2
fs.suid_dumpable = 0
EOF

# Apply all sysctl settings
sudo sysctl --system

Kubernetes Pod sysctl Settings

Kubernetes allows pods to set certain "safe" sysctl parameters:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: sysctl-pod
spec:
  securityContext:
    sysctls:
    - name: net.ipv4.ip_local_port_range
      value: "1024 65535"
    - name: net.ipv4.tcp_syncookies
      value: "1"
  containers:
  - name: app
    image: nginx:1.27

Safe vs Unsafe Sysctls

Kubernetes categorizes sysctls as:

  • Safe sysctls: Namespaced and can be set per-pod without affecting the host. Allowed by default.
    • kernel.shm_rmid_forced
    • net.ipv4.ip_local_port_range
    • net.ipv4.tcp_syncookies
    • net.ipv4.ping_group_range
    • net.ipv4.ip_unprivileged_port_start
  • Unsafe sysctls: Could affect other pods or the host. Must be explicitly allowed by the kubelet flag --allowed-unsafe-sysctls.

/proc and /sys Filesystem Restrictions

/proc Filesystem

The /proc filesystem exposes kernel and process information. It is a critical target for attackers because it reveals:

  • Process memory maps (/proc/<pid>/maps)
  • Kernel configuration (/proc/config.gz)
  • System information (/proc/cpuinfo, /proc/meminfo)
  • Kernel symbols (/proc/kallsyms)
  • Process credentials (/proc/<pid>/status)

Default Container /proc Masking

Kubernetes and container runtimes mask sensitive /proc paths by default:

Masked PathWhy It's Sensitive
/proc/acpiHardware information
/proc/kcorePhysical memory access
/proc/keysKernel keyring
/proc/latency_statsPerformance data
/proc/timer_listTiming information (side-channel attacks)
/proc/timer_statsTimer statistics
/proc/sched_debugScheduler debug info
/proc/scsiSCSI device info
/proc/sysMade read-only (prevents sysctl changes from containers)

/sys Filesystem

The /sys filesystem provides access to kernel subsystems, devices, and drivers:

bash
# By default, /sys is mounted read-only in containers
# This prevents containers from:
# - Modifying device configurations
# - Changing kernel parameters
# - Accessing hardware directly

# Privileged containers get read-write /sys access -- another reason to avoid privileged mode

Kernel Module Restrictions

Why Kernel Modules Are Dangerous

Kernel modules run with full kernel privileges. If an attacker can load a kernel module, they can:

  • Execute arbitrary code in kernel space
  • Bypass all security controls
  • Install rootkits
  • Access any memory or device

Restricting Module Loading

bash
# Disable module loading entirely (irreversible until reboot)
echo 1 | sudo tee /proc/sys/kernel/modules_disabled

# Blacklist specific dangerous modules
cat <<EOF | sudo tee /etc/modprobe.d/kubernetes-hardening.conf
# Disable uncommon network protocols
install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install squashfs /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 (if not needed)
install usb-storage /bin/true
EOF

Caution

Setting kernel.modules_disabled=1 prevents any module loading, including legitimate ones needed by Kubernetes networking. Only use this after ensuring all required modules are already loaded.

SSH Hardening

If SSH is enabled on nodes (for maintenance), it should be hardened:

bash
# /etc/ssh/sshd_config recommended settings

# Disable root login
PermitRootLogin no

# Use key-based authentication only
PasswordAuthentication no
PubkeyAuthentication yes

# Disable empty passwords
PermitEmptyPasswords no

# Set maximum authentication attempts
MaxAuthTries 3

# Set login grace time
LoginGraceTime 30

# Disable X11 forwarding
X11Forwarding no

# Disable agent forwarding
AllowAgentForwarding no

# Use SSH protocol 2 only (default in modern versions)
Protocol 2

# Restrict SSH to specific users/groups
AllowUsers kubeadmin
# Or: AllowGroups kubernetes-admins

# Set idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
bash
# Apply SSH changes
sudo systemctl restart sshd

# Verify SSH configuration
sudo sshd -T | grep -E '(permitrootlogin|passwordauthentication|maxauthtries)'

File System Hardening

Important File Permissions

bash
# Secure critical Kubernetes files
sudo chmod 600 /etc/kubernetes/admin.conf
sudo chmod 600 /etc/kubernetes/scheduler.conf
sudo chmod 600 /etc/kubernetes/controller-manager.conf
sudo chmod 644 /etc/kubernetes/kubelet.conf

# Secure etcd data directory
sudo chmod 700 /var/lib/etcd

# Secure PKI directory
sudo chmod 700 /etc/kubernetes/pki
sudo chmod 600 /etc/kubernetes/pki/*.key

# Check permissions
ls -la /etc/kubernetes/
ls -la /etc/kubernetes/pki/

Mount Options

bash
# Check current mounts
mount | grep -E '(nosuid|noexec|nodev)'

# Recommended mount options for /tmp
# In /etc/fstab:
# tmpfs /tmp tmpfs defaults,nosuid,nodev,noexec 0 0

# nosuid - Ignore setuid/setgid bits
# nodev  - Do not interpret device files
# noexec - Do not allow execution of binaries

Hardening Checklist

Node Hardening Checklist

Packages:

  • [ ] Remove unnecessary packages (desktop, dev tools, servers)
  • [ ] Keep only packages required for Kubernetes node operation
  • [ ] Enable automatic security updates

Services:

  • [ ] Disable unnecessary services (cups, avahi, bluetooth, etc.)
  • [ ] Mask services that should never start
  • [ ] Verify only required services are running

Kernel:

  • [ ] Apply recommended sysctl parameters
  • [ ] Restrict kernel module loading
  • [ ] Enable ASLR (kernel.randomize_va_space=2)
  • [ ] Restrict ptrace (kernel.yama.ptrace_scope=2)
  • [ ] Restrict dmesg access (kernel.dmesg_restrict=1)

Network:

  • [ ] Disable ICMP redirects
  • [ ] Enable SYN cookies
  • [ ] Enable reverse path filtering
  • [ ] Disable source routing

Access:

  • [ ] Harden SSH configuration
  • [ ] Disable root SSH login
  • [ ] Use key-based authentication
  • [ ] Set proper file permissions on Kubernetes configs

Filesystem:

  • [ ] Mount /tmp with nosuid,nodev,noexec
  • [ ] Secure Kubernetes PKI files
  • [ ] Secure etcd data directory

Quick Reference

Exam Speed Reference

bash
# Check running services
systemctl list-units --type=service --state=running

# Disable a service
sudo systemctl disable --now <service>

# Check sysctl value
sysctl <parameter>

# Set sysctl value
sudo sysctl -w <parameter>=<value>

# Apply all sysctl configs
sudo sysctl --system

# Find setuid binaries
find / -perm -4000 -type f 2>/dev/null

# Check installed packages
dpkg -l | wc -l

# Check SSH configuration
sudo sshd -T

Key Exam Takeaways

  1. Minimize packages -- remove anything not needed for Kubernetes node operation
  2. Disable services -- every running service is an attack vector
  3. Apply sysctl hardening -- especially network and kernel security parameters
  4. Restrict /proc and /sys -- Kubernetes does this by default, but privileged containers bypass it
  5. Harden SSH -- disable root login, use keys, limit attempts
  6. The exam may require you to SSH into a node and check/fix OS-level configurations
  7. Remember: net.ipv4.ip_forward=1 must stay enabled for Kubernetes networking

Released under the MIT License.