Handbook sections▾
Foundations
Understanding the threats to a Linux hostWhy we hardenThe defense principles behind every ruleEffective configuration: the truth no file holdsThe standards Pavois maps toSOCLE control ID modelEvidence & exportsHow the A-E grade is computedWhat a PASS proves: the qualified verdictTrust model for the evidence bundleIs SOCLE just your own norm? (governance & the circularity question)What Pavois covers, and what it doesn'tDomains
Hardening SSHHardening PAMMandatory Access Control (SELinux / AppArmor)Hardening the host firewallHardening sudoFile Permissions & OwnershipMount & filesystem hardeningHardening kernel modulesHardening the kernel & network with sysctlAudit logging with auditdHardening logging with journald and rsyslogHardening systemd servicesPackage hygieneHardening the bootloader (GRUB)Synchronizing timeLogin banners and MOTDControlling cron and at accessHardening the GNOME desktop (dconf)Tooling
Undo a hardening run: restore pointsOperating Pavois: privileges, air-gap, timing, exceptionsRun Pavois in CI (GitHub Actions)Feature status: delivered, partial, roadmapGovernance: licence, versioning, provenance, securityHardening the kernel & network with sysctl
Last reviewed
The kernel's runtime tunables decide how the host routes packets and exposes its own memory. Tighten them so the network stack and the kernel itself stop helping an attacker, and make the change persist a reboot.
The threat: a kernel tuned for connectivity, not safety
A Linux kernel ships with runtime tunables (the sysctl keys under /proc/sys) whose defaults favour connectivity and debuggability over security, so the host actively helps an attacker. With IP forwarding on, a compromised server becomes a router that pivots traffic into networks it should never bridge. Accepting ICMP redirects lets any host on the segment rewrite your routing table and man-in-the-middle your traffic. Source-routed packets let a remote sender dictate the return path and bypass filtering. Without reverse-path filtering, spoofed source addresses sail straight in. Locally, an unrandomized address space (ASLR off) makes memory-corruption exploits reliable; leaked kernel pointers in dmesg//proc hand attackers the addresses to defeat KASLR; and an open ptrace scope lets one process read another's memory, harvesting credentials and tokens.
Why harden sysctl
These are one-line settings with outsized impact: each weak default is a primitive an attacker chains into a pivot, a route hijack, a spoofed flood or a reliable exploit. On a typical server (not a router) hardening them costs almost nothing operationally yet removes whole exploitation classes before any service-level control matters. It is the cheapest defense-in-depth layer you own, and the set below maps to CIS and ANSSI BP-028: R8 (memory and ASLR), R9 (kernel), R11 (Yama/ptrace), R12 (IPv4), R13 (IPv6), R14 (filesystem) and R23 (kexec).
Runtime vs persistent: the distinction that decides the grade
sysctl has two faces, and the difference is exactly the qualified-verdict axis:
| Action | Command | Persistence |
|---|---|---|
| Read the live value | sysctl -n net.ipv4.ip_forward |
n/a |
| Set at runtime | sysctl -w net.ipv4.ip_forward=0 |
lost at reboot |
| Make persistent | a file in /etc/sysctl.d/ then sysctl --system |
survives reboot |
For hardening you always write a file in /etc/sysctl.d/; -w is for testing only. Precedence: at the same filename the directory wins (/etc > /run > /usr/lib); across different names the highest lexical name wins, so a 99- prefix is read last and overrides the distribution defaults; sysctl --system reads /etc/sysctl.conf last of all. A value can be live but not persistent (set with -w, lost on reboot) or written but not applied (file edited, sysctl --system never run): only re-reading /proc/sys proves the running state.
What Pavois audits: the effective /proc/sys value
Pavois reads the effective, running value, not what a file claims. Each control uses InSpec's kernel_parameter resource, which reads the live /proc/sys tree, so it sees the kernel's resolved state after every /etc/sysctl.conf, /etc/sysctl.d/*.conf drop-in, kernel command-line argument and runtime sysctl -w has been applied, so a file edited but never applied (sysctl --system not run) is never read as compliant. (SSG's sysctl checks also read /proc/sys, so both catch runtime drift here; the edge is the consistent evidence model, not sysctl itself.) The domain carries dozens of checks: forwarding off, reverse-path filtering on, ICMP redirects ignored and not sent, source routing refused, martian logging, broadcast-echo and bogus-error ignoring, SYN cookies, ASLR = 2, kptr_restrict = 2, dmesg_restrict, and yama.ptrace_scope >= 1, each mapped to CIS, ANSSI BP-028 and more so one audit reports against whichever standard you select.
The hardening parameters
| Parameter | Hardened value | Closes |
|---|---|---|
net.ipv4.ip_forward |
0 | host used as a router or pivot |
net.ipv4.conf.all.accept_redirects |
0 | ICMP-redirect route hijack / MITM |
net.ipv4.conf.all.send_redirects |
0 | leaking routes to the segment |
net.ipv4.conf.all.accept_source_route |
0 | attacker-dictated return path |
net.ipv4.conf.all.rp_filter |
1 | source-address spoofing |
net.ipv4.conf.all.log_martians |
1 | silent impossible-address traffic |
net.ipv4.tcp_syncookies |
1 | SYN-flood denial of service |
net.ipv6.conf.all.accept_ra |
0 | rogue IPv6 router advertisements |
kernel.randomize_va_space |
2 | reliable memory-corruption exploits |
kernel.kptr_restrict |
2 | KASLR defeat via leaked pointers |
kernel.dmesg_restrict |
1 | kernel-log information leak |
kernel.yama.ptrace_scope |
1 | one process reading another's memory |
kernel.unprivileged_bpf_disabled |
1 | unprivileged eBPF kernel surface |
kernel.kexec_load_disabled |
1 | hot-loading a replacement kernel |
fs.protected_symlinks |
1 | symlink TOCTOU attacks |
fs.suid_dumpable |
0 | core dumps of setuid programs |
How do you verify and operate sysctl?
Capture a baseline first (defaults vary by distribution), then read the live values back with sysctl net.ipv4.conf.all.rp_filter kernel.kptr_restrict. For concrete proof, kptr_restrict = 2 zeroes the symbol addresses in head -2 /proc/kallsyms, even for root. Apply a hardening file with sysctl --system. Mind the container limit: kernel.* and fs.* are read-only in an unprivileged container (permission denied); only net.* and a namespaced IPC subset are writable, so full kernel hardening happens on the host or a VM, which containers then inherit.
Exceptions and pitfalls
ip_forward = 0breaks routing. Routers, NAT gateways, Kubernetes nodes and Docker hosts needip_forward = 1. Only harden it on a plain application server.rp_filter = 1(strict) breaks asymmetric routing and multi-homed hosts (use2, loose). The kernel appliesmax(conf.all, conf.<iface>), soall = 0does not disable filtering if an interface is higher, anddefaultonly affects interfaces created later. CIS and ANSSI want1; systemd/Debian often keep2on purpose.accept_ra = 0assumes statically configured IPv6; keep it on where prefixes arrive via Router Advertisement.- Disabling IPv6 can stop
sshdor Postfix bound to::1(setAddressFamily inet/inet_protocols = ipv4). - Yama must be loaded for
kernel.yama.ptrace_scopeto exist (cat /sys/kernel/security/lsm). - NetworkManager / systemd-networkd set per-interface values after
systemd-sysctl: fix a per-interface setting in the network manager, not by stacking sysctl files. - Test on a VM or via an out-of-band console: a bad network setting applied remotely can lock you out.
FAQ
Why does Pavois read /proc/sys instead of the sysctl files? Because only the live value is what the kernel enforces. A file present but never applied (no sysctl --system) changes nothing, and Pavois audits the effective state.
A value is correct now but the page fails after a reboot. Why? It was set with sysctl -w (runtime only). Persist it in /etc/sysctl.d/ so it survives a reboot. This is the runtime-versus-persistent distinction the qualified verdict captures.
Can I harden the kernel from inside a container? Not for kernel.* and fs.* (read-only); only net.* and namespaced IPC. Harden the host or the VM, and the container inherits it.