System Hardening (15%)
Principle of Least Privilege
- Ensure that people or bots only have access to what is needed, and nothing else.
Limit access to nodes
Managing Local Users and Groups
- Commands to be aware of:
idwholastgroupsuseradduserdelusermodgroupdel - Files to be aware of:
/etc/passwd/etc/shadow/etc/group - Disable logins for users and set their login shell to
/bin/nologin - Remove users from groups they do not need to belong to
Securing SSH
- Set the following in
sshd_config
PermitRootLogin no
PasswordAuthentication no
Using sudo
- The
/etc/sudoersfile controls and configures the behavior of thesudocommand. Each entry follows a structured syntax. Below is a breakdown of the fields and their meanings:
# Example Lines
# ----------------------------------
# User/Group Host=Command(s)
admin ALL=(ALL) NOPASSWD: ALL
%developers ALL=(ALL) ALL
john ALL=(ALL:ALL) /usr/bin/apt-get
# Field Breakdown
admin ALL=(ALL) NOPASSWD: ALL
| | | | |
| | | | +---> Command(s): Commands the user/group can execute.
| | | +------------> Options: Modifiers like `NOPASSWD` (no password required).
| | +--------------------> Runas: User/Group the command can be run as.
| +------------------------> Host: On which machine this rule applies (`ALL` for any).
+-----------------------------------------> User/Group: The user or group this rule applies to.
# Examples Explained
1. Allow `admin` to execute any command without a password:
admin ALL=(ALL) NOPASSWD: ALL
Remove Packages Packages
- This one is self-explanatory. Don’t have unnecessary software installed on your nodes.
Restrict Kernel Modules
- Kernel modules are ways of extending the kernel to enable it to understand new hardware. They are like device drivers.
modprobeallows you to load a kernel modulelsmodallows you to view all loaded modules- You can blacklist modules by adding a new entry to
/etc/modprobe.d/blacklist.conf- The entry should be in the format
blacklist <module name> - Example:
echo "blacklist sctp" >> /etc/modprobe.d/blacklist.conf
- The entry should be in the format
- You may need to reboot the system after disabling kernel modules or blacklisting them
Disable Open Ports
- Use
netstat -tunlpor to list listening ports on a system - Stop the service associated with the open port or disable access with a firewall
- Common firewalls you can use are
iptablesorufw- Run
ufw statusto list the current status of the UFW firewall - Allow all traffic outbound:
ufw default allow outgoing - Deny all incoming:
ufw default deny incoming - Allow SSH from 172.16.154.24:
ufw allow from 172.16.154.24 to any port 22 proto tcp
- Run
- Common firewalls you can use are
Tracing Syscalls
- There are several ways to trace syscalls in Linux.
strace
straceis included with most Linux distributions.- To use
strace, simply add it before the binary that you are running:strace touch /tmp/test - You can also attach
straceto a running process like this:strace -p <PID>
AquaSec Tracee
traceeis an open source tool created by AquaSec- Uses eBPF (extended Berkely Packet Filter) to trace syscalls on a system. eBPF runs programs directly within the kernel space without loading any kernel modules. As a result, tools that use eBPF are more efficient and typically use less resources.
traceecan be run by using the binaries or as a container
Restricting Access to syscalls with seccomp
-
seccompcan be used to restrict a process’ access to syscalls. It allows access to the most commonly used syscalls, while restricting access to syscalls that can be considered dangerous. -
To see if
seccompis enabled:grep -i seccomp /boot/config-$(uname -r) -
seccompcan operate in 1 of 3 modes:mode 0: disabledmode 1: strict (blocks nearly all syscalls, except for 4)mode 2: selectively filters syscalls- To see which mode the process is currently running in:
grep -i seccomp /proc/1/statuswhere ‘1’ is the PID of the process
-
seccompprofiles- Kubernetes provides a default
seccompprofile, that can be either restrictive or permissive, depending on your configuration - You can create custom profiles to fine-tune
seccompand which syscalls it blocks or allows within a containers - Example
seccompprofile formode 1:{ "defaultAction": "SCMP_ACT_ERRNO", "archMap": [ { "architecture": "SCMP_ARCH_X86_64", "subArchitectures": [] } ], "syscalls": [ { "names": ["read", "write", "exit", "sigreturn"], "action": "SCMP_ACT_ALLOW" } ] }
- Kubernetes provides a default
-
To apply a
seccompprofile to a pod:apiVersion: v1 kind: Pod metadata: name: audit-pod labels: app: audit-pod spec: securityContext: seccompProfile: type: Localhost localhostProfile: profiles/audit.json #this path is relative to default seccomp profile location (/var/lib/kubelet/seccomp) containers: - name: test-container image: hashicorp/http-echo:1.0 args: - "-text=just made some syscalls!" securityContext: allowPrivilegeEscalation: false
Restrict access to file systems
AppArmor
-
AppArmor can be used to limit a containers’ access to resources on the host. Why do we need apparmor if we have traditional discretionary access controls (file system permissions, etc.)? With discretionary access control, a running process will inherit the permissions of the user who started it. Likely more permissions than the process needs. AppArmor is a mandatory access control implementation that allows one to implement fine-grained controls over what a process can access or do on a system.
-
AppArmor runs as a daemon on Linux systems. You can check it’s status using
systemctl:systemctl status apparmor- If
apparmor-utilsis installed, you can also useaa-status
- If
-
To use AppArmor, the kernel module must also be loaded. The check status:
cat /sys/module/apparmor/parameters/enabledY = loaded -
AppArmor profiles define what a process can and cannot do and are stored in
/etc/apparmor.d/. Profiles need to be copied to every worker node and loaded. -
Every profile needs to be loaded into AppArmor before it can take effect
- To view loaded profiles, run
aa-status
- To view loaded profiles, run
-
To load a profile:
apparmor_parser -r -W /path/to/profile- If
apparmor-utilsis installed, you can also useaa-enforceto load a profile
- If
-
Profiles are loaded in ‘enforce’ mode by default. To change the mode to ‘complain’:
apparmor_parser -C /path/to/profile- If
apparmor-utilsis installed, you can also useaa-complainto change the mode
- If
-
To view loaded apparmor profiles:
kubeadmin@kube-controlplane:~$ sudo cat /sys/kernel/security/apparmor/profiles cri-containerd.apparmor.d (enforce) wpcom (unconfined) wike (unconfined) vpnns (unconfined) vivaldi-bin (unconfined) virtiofsd (unconfined) rsyslogd (enforce) vdens (unconfined) uwsgi-core (unconfined) /usr/sbin/chronyd (enforce) /usr/lib/snapd/snap-confine (enforce) /usr/lib/snapd/snap-confine//mount-namespace-capture-helper (enforce) tcpdump (enforce) man_groff (enforce) man_filter (enforce) ....or:
root@controlplane00:/etc/apparmor.d# aa-status apparmor module is loaded. 33 profiles are loaded. 12 profiles are in enforce mode. /home/rtn/tools/test.sh /usr/bin/man /usr/lib/NetworkManager/nm-dhcp-client.action /usr/lib/NetworkManager/nm-dhcp-helper /usr/lib/connman/scripts/dhclient-script /usr/sbin/chronyd /{,usr/}sbin/dhclient lsb_release man_filter man_groff nvidia_modprobe nvidia_modprobe//kmod 21 profiles are in complain mode. avahi-daemon dnsmasq dnsmasq//libvirt_leaseshelper identd klogd mdnsd nmbd nscd php-fpm ping samba-bgqd samba-dcerpcd samba-rpcd samba-rpcd-classic samba-rpcd-spoolss smbd smbldap-useradd smbldap-useradd///etc/init.d/nscd syslog-ng syslogd traceroute 0 profiles are in kill mode. 0 profiles are in unconfined mode. 4 processes have profiles defined. 2 processes are in enforce mode. /usr/sbin/chronyd (704) /usr/sbin/chronyd (708) 2 processes are in complain mode. /usr/sbin/avahi-daemon (587) avahi-daemon /usr/sbin/avahi-daemon (613) avahi-daemon 0 processes are unconfined but have a profile defined. 0 processes are in mixed mode. 0 processes are in kill mode. -
AppArmor defines profile modes that determine how the profile behaves:
- Modes:
- Enforced: Action is taken and the application is allowed/blocked from performing defined actions. Events are logged in syslog.
- Complain: Events are logged but no action is taken
- Unconfined: application can perform any task and no event is logged
- Modes:
-
AppArmor Tools
- Can be used to generate apparmor profiles
- To install:
apt install -y apparmor-utils - Run
aa-genprofto generate a profile:aa-genprof ./my-application
-
Before applying an AppArmor profile to a pod, you must ensure the container runtime supports AppArmor. You must also ensure AppArmor is installed on the worker node and that all necessary profiles are loaded.
-
To apply an AppArmor profile to a pod, you must add the following security profile (K8s 1.30+):
securityContext: appArmorProfile: type: <profile_type> localhostProfile: <profile_name>- <profile_type> can be one of 3 values:
Unconfined,RuntimeDefault, orLocalhostUnconfinedmeans the container is not restricted by AppArmorRuntimeDefaultmeans the container will use the default AppArmor profileLocalhostmeans the container will use a custom profile
- <profile_type> can be one of 3 values:
Deep Dive into AppArmor Profiles
AppArmor profiles define security rules for specific applications, specifying what they can and cannot do. These profiles reside in /etc/apparmor.d/ and are loaded into the kernel to enforce security policies.
- Each profile follows these structure:
profile <profile_name> <executable_path> {
<rules>
}
- Example profile, a profile for nano:
profile nano /usr/bin/nano {
# Allow reading any file
file,
# Deny writing to system directories
deny /etc/* rw,
}
- Types of AppArmor rules:
- File Access Rules:
/home/user/data.txt r # Read-only access /etc/passwd rw # Read & write access /tmp/ rw # Full access to /tmp - Network Access Rules:
network inet tcp, # Allow TCP connections network inet udp, # Allow UDP connections network inet dgram, # Allow datagram connections - Capability Rules:
deny capability sys_admin, # Deny sys_admin capability deny capability sys_ptrace, # Deny sys_ptrace capability - File Access Rules:
Linux Capabilities in Pods
- For the purpose of performing permission checks, traditional UNIX implementations distinguish two categories of processes: privileged processes (whose effective user ID is 0, referred to as superuser or root), and unprivileged processes (whose effective UID is nonzero). Privileged processes bypass all kernel permission checks, while unprivileged processes are subject to full permission checking based on the process’s credentials (usually: effective UID, effective GID, and supplementary group list).
- Starting with Linux 2.2, Linux divides the privileges traditionally associated with superuser into distinct units, known as capabilities, which can be independently enabled and disabled. Capabilities are a per-thread attribute.
- Capabilities control what a process can do
- Some common capabilities
CAP_SYS_ADMINCAP_NET_ADMINCAP_NET_RAW
- To view the capabilities of a process:
getcap- Check the capabilities of a binary -getcap <path to bin>getpcaps- Check the capabilities of a process -getpcaps <pid>