Filesystem Security
Mount Options
noexec, nosuid, nodev, and ro: reducing what a filesystem allows.
Not every filesystem needs every capability. A scratch directory does not need to run programs; a data volume does not need device nodes; a config partition might not need to be writable at all. By default the kernel grants all of these everywhere, which means a writable area like /tmp becomes a convenient landing pad: an attacker drops a binary, marks it executable, and runs it. Mount options let you remove those capabilities per mount point, so the kernel itself refuses the abuse. The change is cheap, enforced by the kernel rather than by policy, and easy to reverse. This page complements Disk Encryption (which protects the bytes at rest) and Integrity Monitoring (which detects tampering).
What each option prevents
| Option | Effect | Attack it blunts |
|---|---|---|
noexec | The kernel refuses to execute any file on the mount | Running a dropped payload or shell script directly from /tmp |
nosuid | setuid and setgid bits are ignored | A planted setuid-root binary granting privilege escalation |
nodev | Device nodes on the filesystem are ignored | A crafted device node (for example a copy of /dev/mem or a disk) used to bypass permissions |
ro | Mount is read-only | Any modification, including persistence and log tampering |
These options are independent and can be combined. noexec alone is not a complete barrier (an attacker can sometimes invoke the interpreter directly, as in bash script.sh), which is why you combine it with nosuid and nodev and treat it as one layer among several.
Sensible options per mount point
| Mount point | Suggested options | Reasoning |
|---|---|---|
/tmp | nodev,nosuid,noexec | Purely scratch space; nothing legitimate runs from here |
/var/tmp | nodev,nosuid,noexec | Persistent scratch; same reasoning as /tmp |
/home | nodev,nosuid | Users store data, not devices or setuid binaries. Omit noexec if users legitimately run scripts from their home directory |
/var | nodev,nosuid | Logs and spool data, not devices |
| Removable media (USB) | nodev,nosuid,noexec | Untrusted media should never introduce devices or executables |
Be cautious with noexec on /home and /tmp: some package managers and language toolchains build in temporary directories. Test before enforcing on a system with real workloads.
Applying options via /etc/fstab
On a stock Ubuntu system /tmp often lives on the root filesystem, so first give it a dedicated entry (here backed by tmpfs, an in-memory filesystem).
# Inspect current mounts and their options
findmnt /tmp
Expected output:
TARGET SOURCE FSTYPE OPTIONS
/tmp /dev/sda1[/tmp] ext4 rw,relatime
Add or edit a line in /etc/fstab, then remount. A tmpfs /tmp line looks like this:
# Append a hardened tmpfs mount for /tmp
echo 'tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec,size=1G 0 0' | sudo tee -a /etc/fstab
# Validate fstab syntax WITHOUT rebooting (critical: a bad line can break boot)
sudo findmnt --verify
# Apply the new options
sudo mount -o remount /tmp
Expected output:
Success, no errors or warnings detected
TARGET SOURCE FSTYPE OPTIONS
/tmp tmpfs tmpfs rw,nosuid,nodev,noexec,size=1048576k
Confirm the options took effect and that execution is now blocked.
# Prove noexec is enforced
cp /bin/true /tmp/probe && chmod +x /tmp/probe && /tmp/probe; echo "exit=$?"
Expected output:
bash: /tmp/probe: Permission denied
exit=126
systemd mount units
systemd can manage mounts too, and on Ubuntu a tmp.mount unit is the modern way to harden /tmp. The unit name must match the mount path (/tmp becomes tmp.mount).
# Use the shipped example unit and enable it
sudo cp /usr/share/systemd/tmp.mount /etc/systemd/system/tmp.mount
sudo systemctl daemon-reload
sudo systemctl enable --now tmp.mount
systemctl show tmp.mount -p Options
Expected output:
Options=mode=1777,strictatime,nosuid,nodev,noexec,size=50%%
Prefer fstab for simple static mounts and systemd units when you need ordering dependencies or want the mount tied to other units. Do not configure the same mount in both places.
Practical Guidance
- Give
/tmpand/var/tmptheir own mounts withnodev,nosuid,noexec; a shared scratch area is the classic payload drop zone. - Always run
sudo findmnt --verifyafter editing/etc/fstab, and test on a snapshot: a malformed line can leave the host unbootable. - Apply
nodev,nosuidbroadly (/home,/var) but introducenoexeccautiously, since some toolchains build in temporary directories. - Force removable media to mount
nodev,nosuid,noexecso untrusted USB drives cannot introduce devices or executables. - Verify enforcement rather than trusting the config: copy a binary in and confirm it is denied, as shown above.
- Remember mount options are one layer only; pair them with encryption and integrity monitoring for data at rest.