Hardening#

Host hardening is the operator’s defensive posture on the platform. Linux ships with a deep stack of primitives, including discretionary permissions, MAC (SELinux / AppArmor), namespaces, capabilities, seccomp, kernel crypto, and the audit framework. The operator’s job is to know which knobs are turned, which are not, and which the next intruder will trip when they land.

This page is the defender / blue-team view: what to harden, what to audit, what to log. It assumes the user / group / sudo / MAC fundamentals in Permissions; the controls below sit on top of that base. For doctrine (red, blue, threat intel, IR), see Operations.

Files#

The configuration files that drive kernel hardening, firewalls, crypto, SSH, sandboxing, integrity checking, and update policy. These are the persistent posture of the host; the runtime tools enforce what these files declare, so audit and change them rather than relying on transient sysctl or firewall-cmd invocations.

Kernel and sysctl hardening#

  • /etc/sysctl.conf, legacy single-file kernel tunables.

  • /etc/sysctl.d/*.conf, drop-in fragments (preferred). Common hardening keys include kernel.kptr_restrict, kernel.dmesg_restrict, kernel.unprivileged_bpf_disabled, net.ipv4.conf.all.rp_filter, and net.ipv4.tcp_syncookies.

  • /etc/modprobe.d/*.conf, blacklist / install ... /bin/false to disable risky modules (dccp, sctp, rds, firewire).

  • /etc/security/limits.conf, per-user resource caps (also covered in Permissions).

  • /proc/sys/kernel/, live kernel knobs read/written by sysctl.

Firewall#

  • /etc/nftables.conf, nftables ruleset (modern default).

  • /etc/iptables/rules.v4 / rules.v6, legacy iptables persistence (iptables-persistent package on Debian/Ubuntu).

  • /etc/ufw/, UFW (Uncomplicated Firewall) rules and profiles.

  • /etc/firewalld/, firewalld zones, services, and direct rules (Fedora / RHEL default).

SSH hardening#

  • /etc/ssh/sshd_config, daemon policy (PermitRootLogin no, PasswordAuthentication no, KbdInteractiveAuthentication no, AllowUsers, AllowGroups, MaxAuthTries, ClientAliveInterval).

  • /etc/ssh/sshd_config.d/*.conf, drop-in fragments.

  • /etc/ssh/ssh_config / ~/.ssh/config, client policy.

  • /etc/ssh/moduli, DH moduli (regenerate to drop weak primes).

  • ~/.ssh/authorized_keys, public keys (mode 600).

  • /etc/hosts.allow / /etc/hosts.deny, TCP wrappers (legacy, deprecated on most distros).

Encryption at rest#

  • /etc/crypttab, LUKS / dm-crypt volumes unlocked at boot.

  • /etc/fstab, mount options that affect security (nosuid, nodev, noexec, ro).

  • /etc/dm-verity/, dm-verity integrity targets (where used).

Encryption in transit / PKI#

  • /etc/ssl/certs/, system trust store (symlinks).

  • /etc/ssl/private/, private keys (root-only, mode 700 dir).

  • /usr/local/share/ca-certificates/ (Debian/Ubuntu) or /etc/pki/ca-trust/source/anchors/ (Fedora/RHEL), drop custom CAs here, then run update-ca-certificates / update-ca-trust.

  • /etc/ca-certificates.conf, enable/disable installed CAs.

Sandboxing#

  • /etc/systemd/system/<unit>.d/sandbox.conf, drop-in to add ProtectSystem=, PrivateTmp=, NoNewPrivileges=, CapabilityBoundingSet=, SystemCallFilter=.

  • /etc/firejail/, firejail profiles.

  • /etc/bubblewrap/, bwrap profiles (used by Flatpak).

  • /etc/apparmor.d/, AppArmor profiles (see Permissions).

Integrity#

  • /etc/aide/aide.conf, AIDE rule set.

  • /var/lib/aide/aide.db, AIDE baseline database (store off-host).

  • /etc/ima/, IMA / EVM policy.

  • /etc/dpkg/dpkg.cfg.d/ (Debian), --verify / debsums configuration.

  • /var/lib/dpkg/info/*.md5sums, per-package file hashes.

Audit#

  • /etc/audit/auditd.conf, daemon config (also in Permissions).

  • /etc/audit/rules.d/*.rules, drop-in audit rules.

  • /var/log/audit/audit.log, raw audit events.

Updates#

  • /etc/apt/apt.conf.d/50unattended-upgrades, Debian/Ubuntu unattended security updates.

  • /etc/apt/apt.conf.d/20auto-upgrades, enable / schedule.

  • /etc/dnf/automatic.conf, Fedora/RHEL dnf-automatic.

  • /etc/needrestart/needrestart.conf, detect daemons running with outdated libraries after upgrades.

Hardening#

Tighten kernel and runtime defaults that ship permissive for compatibility. Most distributions favor “works out of the box” over “secure out of the box,” so the operator’s first hardening pass is to flip the sysctls, sysrq, and module-loading defaults that an attacker would otherwise enjoy.

Common hardening knobs#

sysctl

Effect

kernel.kptr_restrict=2

Hide kernel pointers from /proc

kernel.dmesg_restrict=1

Restrict dmesg to root

kernel.unprivileged_bpf_disabled=1

Block unprivileged eBPF

kernel.yama.ptrace_scope=1

Limit ptrace to children

net.ipv4.conf.all.rp_filter=1

Reverse-path filtering

net.ipv4.tcp_syncookies=1

SYN-flood protection

net.ipv4.conf.all.accept_redirects=0

Drop ICMP redirects

net.ipv6.conf.all.accept_ra=0

Drop IPv6 router advertisements

fs.protected_symlinks=1

Block symlink-following attacks

fs.protected_hardlinks=1

Block hardlink-following attacks

Example:

$ sudo install -m 0644 /dev/stdin /etc/sysctl.d/99-hardening.conf <<'EOF'
$ kernel.kptr_restrict=2
$ kernel.dmesg_restrict=1
$ kernel.unprivileged_bpf_disabled=1
$ kernel.yama.ptrace_scope=1
$ net.ipv4.tcp_syncookies=1
$ net.ipv4.conf.all.rp_filter=1
$ fs.protected_symlinks=1
$ fs.protected_hardlinks=1
$ EOF
$ sudo sysctl --system

References:

  • man 5 sysctl.conf, man 8 sysctl

  • man 5 modprobe.d

  • Permissions for users, sudo, PAM, MAC

LSM#

The Linux Security Modules framework hooks every security-relevant kernel operation behind an interface that one or more modules consume. The major modules ship with the kernel; distributions pick one as the default major MAC layer and leave the rest opt-in. The operator’s first question on contact with a defended host is which module is loaded and in what mode, because that single answer constrains every later post-foothold move.

LSM modules#

Module

Scope

SELinux

Label-based mandatory access control. Type enforcement, RBAC, and MLS. Every file, process, and socket carries a security context.

AppArmor

Path-based MAC profiles per binary. Easier to author than SELinux, less granular.

Yama

ptrace restriction (kernel.yama.ptrace_scope). Loaded almost everywhere.

Landlock

Unprivileged opt-in filesystem sandbox (kernel 5.13+). Used by browsers and per-app sandboxers, not whole-system policy.

Lockdown

Restricts what root can do to the running kernel (5.4+). Engaged automatically under Secure Boot.

IMA / EVM

File measurement and integrity attestation against a kernel-held hash list. See the Integrity section below.

BPF LSM

eBPF programs as LSM hooks (5.7+). Runtime-loadable detection / enforcement; used by tools like Tetragon.

SafeSetID

Constrains setuid / setgid transitions to an allowlist. Niche; useful in container runtimes.

Smack

Simplified label-based MAC. Default on Tizen and some embedded estates.

TOMOYO

Pathname-based MAC with a learning mode. Rare outside embedded.

The major LSMs are designed to stack; the kernel loads the set named in CONFIG_LSM (and the lsm= boot parameter) in order. SELinux and AppArmor are mutually exclusive as the major MAC layer on most configurations, so picking one is a distribution-level decision rather than a per-host knob.

Default MAC layer by distribution#

Distribution

Default MAC

Notes

Debian

AppArmor

Enforcing by default since bullseye (11). Yama and Lockdown also loaded.

Ubuntu

AppArmor

Enforcing since 7.10. Snap confinement relies on it; Landlock available in recent LTS kernels.

Kali

AppArmor

Debian-derived. Mostly complain mode on offensive tooling.

Tails

AppArmor

Debian-derived. Profiles confine the Tor Browser path.

Whonix

AppArmor

Debian-derived. Layered with sandbox-app-launcher and firejail.

Parrot

AppArmor

Debian-derived. Similar posture to Kali.

Fedora

SELinux

Enforcing by default with the targeted policy. Yama and Lockdown loaded.

CentOS Stream

SELinux

Enforcing; shares the Fedora policy lineage.

Rocky Linux

SELinux

RHEL rebuild; enforcing targeted policy.

AlmaLinux

SELinux

RHEL rebuild; enforcing targeted policy.

Oracle Linux

SELinux

RHEL-compatible; enforcing by default.

openSUSE

AppArmor

SELinux available as an alternative, not enforcing out of the box.

SLES

AppArmor

Enterprise. SELinux supported but AppArmor is the default.

Arch

None

Both SELinux and AppArmor available as opt-in packages.

Gentoo

None

SELinux and AppArmor both available; hardened profile ships SELinux policy.

Alpine

None

Hardened kernel and musl-side defaults instead of a MAC layer.

NixOS

None

AppArmor available via security.apparmor flake option.

Android

SELinux

Always enforcing on shipped builds (5.0+). Landlock and seccomp also confine the app sandbox.

ChromeOS

SELinux

Restricted policy. Verified boot and dm-verity carry most of the integrity story.

Qubes OS

None at host

Xen-based VM isolation is the primary control. Per-template MAC varies with the template distro.

Inspect what is actually loaded on the host. Useful before tuning, auditing, or pivoting on a defended box.

$ cat /sys/kernel/security/lsm
$ ls /sys/kernel/security/

For SELinux hosts.

$ getenforce
$ sestatus
$ semanage boolean -l | head
$ ls -Z /etc/passwd

For AppArmor hosts.

$ sudo aa-status
$ sudo aa-enforce /etc/apparmor.d/usr.bin.example
$ sudo aa-complain /etc/apparmor.d/usr.bin.example

For Landlock support.

$ grep -E 'CONFIG_SECURITY_LANDLOCK|landlock' /boot/config-$(uname -r)
$ ls /sys/kernel/security/landlock 2>/dev/null

References:

  • man 7 capabilities, man 7 namespaces

  • man 8 selinux, man 8 sestatus, man 5 selinux_config

  • man 8 apparmor, man 8 aa-status, man 5 apparmor.d

  • man 7 landlock, man 2 landlock_create_ruleset

  • Permissions for SELinux and AppArmor file layouts and tool details

Firewall#

Most modern distros default to nftables; iptables is the legacy syntax that still works through the iptables-nft shim. UFW (Debian / Ubuntu) and firewalld (RHEL family) are higher-level wrappers that emit nft rules under the hood, friendlier than writing them by hand.

Firewall front-ends#

Tool

Where it fits

nft

Native nftables CLI; the modern baseline.

iptables

Legacy CLI; on most distros now backed by nftables.

ufw

Debian/Ubuntu; simple allow/deny per port.

firewall-cmd

Fedora/RHEL; zones + services.

Example:

$ sudo nft list ruleset
$ sudo nft -f /etc/nftables.conf
$ sudo systemctl enable --now nftables

$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing
$ sudo ufw allow OpenSSH
$ sudo ufw enable
$ sudo ufw status verbose

$ sudo firewall-cmd --get-default-zone
$ sudo firewall-cmd --permanent --add-service=ssh
$ sudo firewall-cmd --reload

References:

  • man 8 nft, man 8 ufw, man 1 firewall-cmd

  • Networking for routes, interfaces, packet path

SSH Hardening#

The single most attacked service on a public Linux host. Disable password auth, root login, and weak algorithms; require keys, restrict source addresses where possible, and pair sshd with fail2ban or equivalent so brute-force attempts get rate-limited at the firewall.

Example /etc/ssh/sshd_config.d/00-hardening.conf:

$ PermitRootLogin no
$ PasswordAuthentication no
$ KbdInteractiveAuthentication no
$ PubkeyAuthentication yes
$ AllowUsers operator
$ MaxAuthTries 3
$ LoginGraceTime 20
$ ClientAliveInterval 300
$ ClientAliveCountMax 2
$ X11Forwarding no
$ AllowAgentForwarding no
$ AllowTcpForwarding no
$ PermitUserEnvironment no

Validate and reload:

$ sudo sshd -t
$ sudo systemctl reload ssh
$ sudo systemctl reload sshd

Drop weak DH moduli:

$ awk '$5 >= 3071' /etc/ssh/moduli | sudo tee /etc/ssh/moduli.strong
$ sudo mv /etc/ssh/moduli.strong /etc/ssh/moduli

References:

  • man 5 sshd_config, man 1 ssh

  • Networking for listening sockets, ports

Encryption#

Encrypt block devices at rest with LUKS, files in transit with TLS or SSH, and keep a managed CA trust store under /etc/ssl/certs/. The same triad covers laptops, servers, and ephemeral cloud instances; only the lifecycle of the keys differs.

Crypto building blocks#

Layer

Tool

Block device

LUKS / dm-crypt (cryptsetup)

Filesystem

fscrypt (ext4, f2fs), eCryptfs (legacy)

Files

GnuPG (gpg), age

Transport

OpenSSL / GnuTLS / OpenSSH / WireGuard

Trust store

ca-certificates / p11-kit

Example:

$ sudo cryptsetup luksFormat /dev/sdX1
$ sudo cryptsetup open /dev/sdX1 secret
$ sudo mkfs.ext4 /dev/mapper/secret
$ sudo cryptsetup close secret

$ sudo cp corp-root.crt /usr/local/share/ca-certificates/
$ sudo update-ca-certificates

$ age-keygen -o ~/.age/key.txt
$ age -r $(grep public ~/.age/key.txt | awk '{print $4}') \
    -o secrets.tar.age secrets.tar

References:

Sandboxing#

Confine processes so that a compromise of one service does not own the host. Linux composes namespaces, cgroups, capabilities, and seccomp into a sandbox; high-level tools (containers, systemd directives, firejail, bubblewrap) wrap that machinery so operators do not have to assemble it by hand.

Sandboxing options#

Mechanism

Use it for

systemd unit opts

Hardening any systemd-managed daemon

seccomp

Filtering syscalls a process may make

namespaces

Isolating mount, net, pid, user, uts, ipc views

cgroups v2

Resource limits and freeze/kill semantics

AppArmor / SELinux

Mandatory access control over file/socket access

bubblewrap (bwrap)

Building per-app sandboxes (used by Flatpak)

firejail

Drop-in sandbox for desktop apps

Example, harden a systemd unit.

$ sudo systemctl edit nginx.service
$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx
$ systemd-analyze security nginx

References:

  • man 5 systemd.exec for the full hardening keyword list

  • man 1 systemd-analyze (security subcommand grades a unit)

  • man 7 capabilities, man 2 seccomp, man 7 namespaces

  • man 1 firejail, man 1 bwrap

Integrity#

Detect tampering of files, packages, and the boot chain before it propagates. AIDE and Tripwire baseline the filesystem; debsums and rpm -Va check installed packages against their manifests; Secure Boot, IMA, and dm-verity push the same check earlier into the boot path.

Integrity tooling#

Tool

Scope

debsums

Verify Debian package file hashes

rpm -Va

Verify RPM-installed files against package metadata

AIDE / Tripwire

Whole-system file integrity baseline + diff

IMA / EVM

Kernel-enforced measurement of files at access time

dm-verity

Read-only Merkle-tree-verified block device

Secure Boot + TPM

Verified boot chain; sealed keys

Example:

$ sudo debsums -s
$ sudo rpm -Va | grep -v '^\.\.\.\.\.\.\.\.\.'

$ sudo aideinit
$ sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
$ sudo aide --check

References:

  • man 1 debsums, man 8 rpm

  • man 1 aide, man 5 aide.conf

Audit and Logging#

The kernel audit subsystem records security-relevant events (auditd); the systemd journal records everything else. Forward both off-host to a log collector or SIEM so a compromise cannot silently rewrite local logs; on-box logs are inherently untrustworthy after root is lost.

Example:

$ sudo auditctl -l
$ sudo ausearch -k privileged-cmd
$ sudo aureport --summary

$ sudo mkdir -p /var/log/journal
$ sudo systemd-tmpfiles --create --prefix /var/log/journal
$ sudo systemctl restart systemd-journald

References:

  • man 8 auditctl, man 8 ausearch, man 8 aureport

  • man 5 journald.conf, man 1 journalctl

  • Permissions for the audit subsystem entry points

Updates#

Unpatched packages are the single largest source of host compromise. Run unattended security updates wherever the change-management policy allows, and detect daemons still running an old library after a library upgrade. needrestart and dnf needs-restarting answer the latter without an unscheduled reboot.

Example:

$ sudo apt install -y unattended-upgrades needrestart
$ sudo dpkg-reconfigure --priority=low unattended-upgrades
$ sudo unattended-upgrade --dry-run --debug
$ sudo needrestart -r l

$ sudo dnf install -y dnf-automatic
$ sudo systemctl enable --now dnf-automatic.timer

References:

  • man 8 unattended-upgrade, man 1 needrestart

  • man 8 dnf-automatic

  • Packages for package managers and repositories

Common Tasks#

Identify mandatory access control, which MAC is loaded, in what mode.

$ getenforce 2>/dev/null
$ sestatus 2>/dev/null
$ aa-status 2>/dev/null
$ cat /sys/kernel/security/lsm

Check kernel hardening flags, the cheap signals.

$ cat /proc/sys/kernel/randomize_va_space   # KASLR
$ cat /proc/sys/kernel/yama/ptrace_scope
$ cat /proc/sys/kernel/kptr_restrict
$ cat /proc/sys/kernel/dmesg_restrict

Audit subsystem present and live (auditd, journald, remote logging).

$ systemctl is-active auditd
$ sudo auditctl -s
$ sudo auditctl -l
$ journalctl -u systemd-journald -n 20

Sandbox a service, tighten one unit without rewriting it.

$ systemctl cat <unit>
$ systemd-analyze security <unit>
$ sudo systemctl edit <unit>
# then add: NoNewPrivileges=yes / ProtectSystem=strict / PrivateTmp=yes

Check disk encryption / secure boot state, evidence of data at rest protection.

$ lsblk -o NAME,FSTYPE,MOUNTPOINT
$ sudo cryptsetup status <dm-name>
$ mokutil --sb-state
$ bootctl status 2>/dev/null

Hunt for backdoor surface (listening services with credentials, world-writable in PATH, recently modified setuid).

$ sudo ss -tlnp
$ sudo find / -perm -4000 -type f -newer /etc/hostname 2>/dev/null
$ echo $PATH | tr ':' '\n' | xargs -I{} find {} -perm -o+w -type f 2>/dev/null

Patch known CVEs, the cheapest defensive win.

$ sudo apt list --upgradable 2>/dev/null | grep -i security
$ sudo unattended-upgrade --dry-run -d 2>/dev/null
$ sudo dnf updateinfo list security 2>/dev/null
$ sudo needrestart -r l 2>/dev/null

Review authentication failures (brute force, lockout, anomaly).

$ sudo journalctl _COMM=sshd -p warning --since '24 hours ago'
$ sudo lastb | head -30
$ sudo grep -i 'failed\|invalid' /var/log/auth.log /var/log/secure 2>/dev/null
$ sudo faillock --user <user> 2>/dev/null

Lock down SSH, the most common foothold.

$ sudo grep -E '^(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication|AllowUsers)' /etc/ssh/sshd_config
$ sudo sshd -T | sort
$ sudo systemctl reload sshd
$ ss -tlnp 'sport = :ssh'

Verify file integrity, detect tampering of system binaries.

$ sudo debsums -c 2>/dev/null
$ sudo rpm -Va 2>/dev/null | grep -v '^\.\.\.\.\.\.\.\.'
$ sudo aide --check 2>/dev/null
$ sha256sum /usr/bin/{bash,ls,ps} | tee /var/log/binhash.snap

Rotate keys and credentials after compromise or staffing change.

$ sudo passwd -e <user>
$ sudo find /home -name authorized_keys -exec ls -la {} \;
$ sudo journalctl _COMM=sshd | grep 'Accepted publickey'
$ sudo grep -RIn 'BEGIN .* PRIVATE KEY' /etc /opt /home 2>/dev/null

Apply a quick hardening pass (safe defaults, low-risk wins).

$ sudo sysctl -w kernel.dmesg_restrict=1 kernel.kptr_restrict=2
$ echo 'kernel.dmesg_restrict=1' | sudo tee /etc/sysctl.d/99-hardening.conf
$ sudo systemctl mask <unused-unit>
$ sudo ufw default deny incoming; sudo ufw enable

References#