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, securityMount & filesystem hardening
Last reviewed
Split high-risk directories onto their own mounts and strip them with nodev, nosuid, noexec so a writable corner of the disk can't become a launch pad.
The threat: write a payload, then run it
Attackers almost always need a place to write a payload and then run it. World-writable directories like /tmp and /dev/shm, or churn-heavy trees like /var, are the obvious candidates: a dropped malware binary, an exploit stage, a downloaded toolkit, or a fileless payload in shared memory. Two mount-level weaknesses make this trivial:
- Execution from writable storage: if any unprivileged user can drop a file under
/tmpand execute it, a single compromised service becomes arbitrary code execution. - setuid/device abuse: a
setuidbinary or a device node placed on a user-writable mount can be leveraged for privilege escalation or to access raw devices and bypass filesystem permissions.
When everything lives on a single / partition, none of these directories can carry independent restrictions: the whole disk shares one set of mount options, and there is no boundary to enforce.
Why harden it
Mount options are a cheap, kernel-enforced control: the kernel, not an application, decides what is allowed. Three flags neutralise the core abuse cases:
noexec: the kernel refuses to execute any program stored on the mount, removing the attacker's staging ground.nosuid: thesetuid/setgidbits are ignored, so a planted setuid-root binary runs with the caller's privileges, not root's.nodev: device nodes on the mount are not honoured, blocking a rogue/dev/sda-style node used to read raw disk.
But a flag can only be applied to a mount that exists. That is why hardening pairs the options with separate partitions (or tmpfs/systemd mount units) for the sensitive trees: /tmp, /var, /var/tmp, /var/log, /var/log/audit, /home, /dev/shm, /boot, /srv, /opt, /usr. A separate /var/log also stops a log flood from filling /, and a dedicated audit partition protects the evidence trail.
Defense principles applied
- Attack-surface reduction: disable unused filesystem drivers (
cramfs,freevxfs,jffs2,hfs,udf,usb-storage) so an attacker can't auto-mount an exotic or removable filesystem to sidestep your options. - Least privilege:
nosuidandnodevdeny the two classic escalation primitives on data partitions, which never legitimately need them. - Defense in depth: mount options layer on top of file permissions and MAC; even if permissions are wrong,
noexecstill blocks the execute step. - Containment: separate partitions stop one directory from exhausting space everywhere and isolate the audit trail from tampering and overflow.
The dpkg/noexec caveat
noexec on /var and /tmp has a real cost on Debian/Ubuntu. APT and dpkg stage and run maintainer scripts (preinst, postinst) from temporary locations, and some operations execute helpers from under /var or /tmp: under a strict noexec these can fail mid-upgrade, leaving packages half-configured. The same applies to build tools, certain pip/dnf plugins, app self-updaters, and sandboxed browsers that execute from /dev/shm. This is not a reason to skip the control: redirect tooling with $TMPDIR/APT::ExtractTemplates::TempDir to an exec-capable scratch area, grant a scoped temporary exception during maintenance, or use dpkg hooks that remount briefly, never silently drop noexec from /tmp system-wide. Test upgrades in staging before enforcing.
What Pavois audits: the effective mount table
Pavois reads the effective mount table, the kernel's live view via /proc/self/mountinfo exposed through InSpec's mount('<path>') resource, not /etc/fstab. That is the whole point: it sees a /tmp provided at runtime by the tmp.mount systemd unit, a /dev/shm absent from fstab, a partition silently remounted without a flag, or an fstab line that never took effect, cases a file-based scanner (OVAL/oscap) would wrongly pass. Concretely, the Mounts rules check:
noexec,nosuid,nodevon/tmp,/var,/var/tmp,/var/log,/var/log/audit,/home,/dev/shm,/boot,/boot/efi,/srv,/opt, asserting each resolved option set contains the flag.- Separate-partition rules (
be_mounted) confirming/tmp,/var,/var/tmp,/var/log,/var/log/audit,/home,/dev/shm,/boot,/srv,/opt,/usrride on their own filesystem rather than on/.
Each rule carries its norm mappings (CIS, ANSSI BP-028 R28, NIST) and a findmnt-based verification step.
How do you verify and operate?
- See the effective options:
findmnt /tmporfindmnt -o TARGET,OPTIONSshows what the kernel actually enforces;/proc/self/mountinfois the raw live truth. Do not trust/etc/fstabalone. - Apply without a reboot:
mount -o remount,noexec,nosuid,nodev /tmponce the fstab line (or systemd unit) is in place, then confirm withfindmnt. /tmpis often a systemd unit, not fstab:systemctl enable --now tmp.mount(or atmp.mountdrop-in) provides/tmpastmpfs; edit the unit, not just fstab.- Persist it: the
nodev,nosuid,noexecoptions go in the/etc/fstabentry (or the systemd mount unit) so they survive a reboot; Pavois reading the live mount catches the gap when they don't.
Pitfalls
noexeccan break package upgrades and build tools (see thedpkgcaveat): scope an exec-capable$TMPDIRrather than dropping the flag.- A flag in
fstabthat was never remounted is not effective: only the live mount counts. /tmpas a systemdtmp.mountignores the fstab line: harden the unit.- Some apps execute from
/dev/shm(Chrome, some runtimes): test before enforcingnoexecthere.
FAQ
Why does Pavois read the live mount instead of /etc/fstab? Because the kernel enforces the live options; an fstab entry not yet remounted, or a /tmp provided by tmp.mount, makes fstab a false source. Pavois reads /proc/self/mountinfo.
noexec broke an apt upgrade. What now? Redirect APT/dpkg temp execution to an exec-capable area ($TMPDIR, APT::ExtractTemplates::TempDir) or remount briefly during maintenance; never drop noexec from /tmp permanently.
Do I need separate partitions, or just options? Both: a flag can only apply to a mount that exists, so isolate the sensitive trees and then strip them with nodev,nosuid,noexec.