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 includekernel.kptr_restrict,kernel.dmesg_restrict,kernel.unprivileged_bpf_disabled,net.ipv4.conf.all.rp_filter, andnet.ipv4.tcp_syncookies./etc/modprobe.d/*.conf,blacklist/install ... /bin/falseto 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 bysysctl.
Firewall#
/etc/nftables.conf, nftables ruleset (modern default)./etc/iptables/rules.v4/rules.v6, legacy iptables persistence (iptables-persistentpackage 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 runupdate-ca-certificates/update-ca-trust./etc/ca-certificates.conf, enable/disable installed CAs.
Sandboxing#
/etc/systemd/system/<unit>.d/sandbox.conf, drop-in to addProtectSystem=,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/debsumsconfiguration./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/RHELdnf-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.
sysctl |
Effect |
|---|---|
|
Hide kernel pointers from /proc |
|
Restrict |
|
Block unprivileged eBPF |
|
Limit |
|
Reverse-path filtering |
|
SYN-flood protection |
|
Drop ICMP redirects |
|
Drop IPv6 router advertisements |
|
Block symlink-following attacks |
|
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 sysctlman 5 modprobe.dPermissions 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.
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 |
|
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 |
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.
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 |
Parrot |
AppArmor |
Debian-derived. Similar posture to Kali. |
Fedora |
SELinux |
Enforcing by default with the |
CentOS Stream |
SELinux |
Enforcing; shares the Fedora policy lineage. |
Rocky Linux |
SELinux |
RHEL rebuild; enforcing |
AlmaLinux |
SELinux |
RHEL rebuild; enforcing |
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 |
NixOS |
None |
AppArmor available via |
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 namespacesman 8 selinux,man 8 sestatus,man 5 selinux_configman 8 apparmor,man 8 aa-status,man 5 apparmor.dman 7 landlock,man 2 landlock_create_rulesetPermissions 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.
Tool |
Where it fits |
|---|---|
|
Native nftables CLI; the modern baseline. |
|
Legacy CLI; on most distros now backed by nftables. |
|
Debian/Ubuntu; simple |
|
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-cmdNetworking 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 sshNetworking 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.
Layer |
Tool |
|---|---|
Block device |
LUKS / dm-crypt ( |
Filesystem |
fscrypt (ext4, f2fs), eCryptfs (legacy) |
Files |
GnuPG ( |
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:
man 8 cryptsetup,man 5 crypttabman 1 update-ca-certificates/man 8 update-ca-trustman 1 gpg, https://age-encryption.org
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.
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.execfor the full hardening keyword listman 1 systemd-analyze(securitysubcommand grades a unit)man 7 capabilities,man 2 seccomp,man 7 namespacesman 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.
Tool |
Scope |
|---|---|
|
Verify Debian package file hashes |
|
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 rpmman 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 aureportman 5 journald.conf,man 1 journalctlPermissions 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 needrestartman 8 dnf-automaticPackages 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#
Permissions for users, groups, sudo, PAM, capabilities, SELinux, AppArmor, ACLs, audit
Networking for interface, route, and socket administration
Services for systemd unit anatomy
Operations for offensive / defensive doctrine
hardening, defensive hardening playbook