Why secure partitioning and mount options become urgent later, not on day one
In the first weeks after a Linux system goes live, storage feels simple. We install the OS, add a few packages, and everything “just works.” Then the system grows the way systems always grow: logs get noisier, temporary files multiply, developers ask for build space, agents drop binaries into /tmp, and one day a service crashes because the root filesystem is full. That’s the operational side.
The security side arrives just as quietly. A single writable directory that also allows execution becomes a staging area. A single setuid binary in the wrong place becomes a privilege escalation path. A single device node created where it should not exist becomes a pivot. None of these are dramatic on their own. They become dramatic when they stack.
Disk partitioning and mount options are not glamorous controls, but they are durable ones. When we separate high-risk paths and apply the right mount flags, we reduce blast radius, make failures predictable, and turn entire classes of attacks into dead ends.
Prerequisites and assumptions
Before we touch disks or mount options, we need to be explicit about the environment. These steps are production-grade and assume we are operating with change control in mind.
- Platform: Linux. Commands below are written for modern distributions using
systemdandutil-linuxtools (lsblk,findmnt,blkid). - Filesystem: We will use
ext4in examples because it is widely supported and predictable. If we are using XFS or another filesystem, the mount options logic remains the same, but formatting commands differ. - Access: We need root privileges. We will use
sudoin commands. If we are already root,sudois harmless but can be removed. - Change window: Partitioning and moving directories like
/varcan be disruptive. We should schedule a maintenance window and have console access (ILO/iDRAC/VM console) in case the system does not boot. - Backups: We must have a verified backup or snapshot. For VMs, take a snapshot. For physical systems, ensure we can restore from backup media.
- Scope: We will focus on secure partition layout and mount options. We will not discuss storage abstraction theory. We will keep the approach practical and directly implementable.
- Persistence: All mount changes will be made persistent via
/etc/fstaband verified across reboots.
The security model we are implementing
We are going to separate and harden the directories that most often become operational pressure points and security pivot points:
- /tmp and /var/tmp: common drop zones for temporary files; frequently abused for execution and persistence.
- /var: logs, caches, spool, databases; tends to grow and can fill root.
- /home: user content; should not be a place where privileged execution is easy.
Mount options we will use and what they mean
These three options are the core of this hardening pattern. We will apply them where they make sense, not blindly everywhere.
- nodev: device files on this filesystem are not interpreted as devices. This reduces the risk of device-node abuse in writable locations.
- nosuid: set-user-ID and set-group-ID bits are ignored. This blocks privilege escalation via setuid binaries placed on that filesystem.
- noexec: binaries cannot be executed from this filesystem. This disrupts many “drop and run” attack chains. It can also break legitimate workflows, so we apply it carefully.
In practice, we commonly apply nodev,nosuid,noexec to /tmp. For /home, we often apply nodev,nosuid and decide on noexec based on how the environment builds and runs software. For /var, we usually avoid noexec because some software legitimately executes helpers from under /var, but we can still use nodev and sometimes nosuid depending on the workload.
Step 1: Inspect the current disk and mount layout
Before we change anything, we need a clear picture of what exists: disks, partitions, filesystems, and current mount options. This prevents us from hardening the wrong device or duplicating mounts.
sudo lsblk -e7 -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,UUID
sudo findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS
sudo df -hT
We have now captured the current state. lsblk shows the block devices and where they are mounted, findmnt shows the effective mount options, and df shows capacity and filesystem types. We should keep this output in the change record.
Step 2: Decide the target layout
We need a layout that matches how systems fail in real life: logs grow, temp fills, and user space expands. A common, stable baseline for general-purpose servers is:
- / (root): OS and core binaries
- /var: logs, spool, caches, databases (size depends on workload)
- /tmp: separate filesystem or tmpfs with strict flags
- /home: user data (optional on servers, common on multi-user systems)
If we are building a new system, we implement this during installation. If we are hardening an existing system, we can still implement it, but we must move data safely and validate boot behavior.
Step 3: Create a new partition (only if we have unallocated space)
If we have free space on a disk, we can create a new partition for a mount point like /var or /home. We will first identify the primary disk device and confirm there is unallocated space. We will not guess.
First, we detect the disk that backs the root filesystem so we operate on the correct device.
ROOT_SRC=$(findmnt -n -o SOURCE /)
echo "Root source: $ROOT_SRC"
ROOT_DISK=$(lsblk -no PKNAME "$ROOT_SRC" 2>/dev/null | head -n1)
echo "Root disk: /dev/$ROOT_DISK"
sudo lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS "/dev/$ROOT_DISK"
We have now identified the disk that contains the root filesystem and printed its current partition map. If there is unallocated space, we can proceed to create a new partition. If there is no free space, we should stop here and plan a disk expansion or attach a new disk.
Create a partition using fdisk (MBR/GPT compatible interactive step)
Partitioning is inherently risky because it changes on-disk structures. We will use fdisk in a controlled way. This step is interactive by design; that is safer than trying to script partition creation across unknown layouts.
We will open fdisk on the target disk and create a new Linux filesystem partition. We will not modify existing partitions.
sudo fdisk "/dev/$ROOT_DISK"
Inside fdisk, we create a new partition (n), accept defaults for start sector, choose an appropriate size, then write changes (w). After writing, the kernel may need to re-read the partition table.
Now we ask the kernel to re-read the partition table and confirm the new partition exists.
sudo partprobe "/dev/$ROOT_DISK"
sudo lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS "/dev/$ROOT_DISK"
We have now refreshed the partition table and verified the new partition is visible. Next, we will format it and mount it safely.
Step 4: Format the new filesystem
Formatting creates a new filesystem and destroys any data on that partition. We will first identify the new partition device name (for example, /dev/sda3 or /dev/nvme0n1p3) and then format it as ext4.
We will detect the newest partition on the root disk that has no filesystem yet. This is a convenience check; we still confirm the device name before formatting.
NEW_PART=$(lsblk -rno NAME,TYPE,FSTYPE "/dev/$ROOT_DISK" | awk '$2=="part" && $3=="" {print $1}' | tail -n1)
echo "Candidate new partition: /dev/$NEW_PART"
sudo lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS "/dev/$ROOT_DISK"
We have now identified a candidate partition without a filesystem. We must visually confirm it is the correct one in the lsblk output. Once confirmed, we format it.
sudo mkfs.ext4 -F "/dev/$NEW_PART"
The partition now contains an ext4 filesystem. Next, we will mount it temporarily, migrate data if needed, and then make it persistent in /etc/fstab.
Step 5: Implement secure mounts for /tmp and /var/tmp using systemd tmp.mount
/tmp is one of the highest-leverage places to harden because it is world-writable and frequently used by both legitimate processes and attackers. We will mount /tmp as a dedicated tmpfs with nodev,nosuid,noexec. This is a common enterprise baseline and avoids disk wear while limiting execution.
We will use a systemd unit (tmp.mount) because it is explicit, predictable, and integrates cleanly with boot ordering.
First, we check whether /tmp is already a separate mount and whether any existing configuration might conflict.
sudo findmnt /tmp || true
sudo systemctl status tmp.mount --no-pager || true
We have now confirmed the current state. If /tmp is already mounted separately, we should review its options before changing anything. If it is not, we can proceed.
Now we create the systemd mount unit for /tmp with secure options.
sudo tee /etc/systemd/system/tmp.mount >/dev/null <<'EOF'
[Unit]
Description=Temporary Directory (/tmp)
Documentation=man:hier(7)
Before=local-fs.target
[Mount]
What=tmpfs
Where=/tmp
Type=tmpfs
Options=mode=1777,strictatime,nodev,nosuid,noexec,size=1G
[Install]
WantedBy=local-fs.target
EOF
We have now defined a dedicated tmpfs mount for /tmp with the sticky bit permissions (1777) and the hardening flags. The size=1G cap prevents runaway temporary usage from consuming all RAM; we can adjust it later based on workload.
Next, we enable and start the mount so it becomes active now and persists across reboots.
sudo systemctl daemon-reload
sudo systemctl enable --now tmp.mount
Systemd has now loaded the new unit, mounted /tmp as tmpfs, and ensured it will be mounted at boot.
Now we verify the effective mount options.
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /tmp
mount | grep -E ' on /tmp ' || true
We have confirmed that /tmp is mounted with nodev,nosuid,noexec. This materially reduces the chance that a dropped binary in /tmp becomes an execution path.
Bind-mount /var/tmp to /tmp for consistent hardening
/var/tmp is similar to /tmp but traditionally persists across reboots. In many environments, we prefer consistent behavior and hardening. A pragmatic approach is to bind-mount /var/tmp to /tmp, so it inherits the same mount flags and behavior.
First, we ensure the directory exists and is not already a separate mount.
sudo mkdir -p /var/tmp
sudo findmnt /var/tmp || true
We have ensured /var/tmp exists and checked whether it is already mounted. Now we add a persistent bind mount in /etc/fstab.
Before editing /etc/fstab, we will take a timestamped backup. This is a small step that saves big recoveries.
sudo cp -a /etc/fstab "/etc/fstab.bak.$(date +%F_%H%M%S)"
We now have a rollback point for the filesystem table.
Now we append the bind mount entry. We will use bind and also apply nodev,nosuid,noexec explicitly to ensure the effective flags remain strict.
echo '/tmp /var/tmp none bind,nodev,nosuid,noexec 0 0' | sudo tee -a /etc/fstab >/dev/null
We have now made the bind mount persistent. Next, we apply it without rebooting and verify.
sudo mount /var/tmp
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /var/tmp
/var/tmp is now backed by /tmp and inherits the hardened behavior. This reduces the number of writable execution-capable paths on the system.
Step 6: Create and mount a dedicated filesystem for /var (optional but strongly recommended)
/var is where growth happens: logs, caches, spools, and sometimes application state. When /var shares the root filesystem, a log storm can become a full-root outage. Separating /var makes failures contained and recovery calmer.
We will mount a new filesystem at /var and migrate existing data. This is a sensitive change because services write to /var constantly. We will stop services that are heavy writers, copy data with permissions preserved, and then switch mounts.
Prepare the new filesystem mount point
We will create a temporary mount point, mount the new partition there, and stage the data copy. This avoids partial moves and keeps rollback simple.
sudo mkdir -p /mnt/newvar
sudo mount "/dev/$NEW_PART" /mnt/newvar
sudo df -hT /mnt/newvar
The new filesystem is now mounted at /mnt/newvar. We have confirmed it is available and has expected capacity.
Stop high-write services before copying
To keep the copy consistent, we will stop common services that write heavily into /var. The exact list depends on the host role, but these are common on many systems. If a service is not installed, the command will simply report it.
sudo systemctl stop rsyslog 2>/dev/null || true
sudo systemctl stop systemd-journald 2>/dev/null || true
sudo systemctl stop cron 2>/dev/null || true
sudo systemctl stop crond 2>/dev/null || true
We have reduced write activity into /var. On systems with databases or application services, we should stop those as well before proceeding.
Copy /var data with ownership, permissions, ACLs, and xattrs preserved
We will use rsync in archive mode and preserve ACLs and extended attributes. This is important for services that rely on specific labels or metadata.
sudo rsync -aHAX --numeric-ids /var/ /mnt/newvar/
/var has now been copied to the new filesystem with metadata preserved. Next, we will prepare the persistent mount in /etc/fstab using the filesystem UUID, which is stable across reboots.
Persist the /var mount in /etc/fstab
We will fetch the UUID of the new partition and then add an /etc/fstab entry. We will use conservative mount options: nodev is generally safe for /var. We will be cautious with nosuid and avoid noexec for broad compatibility.
NEW_UUID=$(blkid -s UUID -o value "/dev/$NEW_PART")
echo "New filesystem UUID: $NEW_UUID"
We have captured the UUID. Now we add the mount entry. We will keep it explicit and readable.
sudo cp -a /etc/fstab "/etc/fstab.bak.$(date +%F_%H%M%S)"
echo "UUID=$NEW_UUID /var ext4 defaults,nodev 0 2" | sudo tee -a /etc/fstab >/dev/null
/etc/fstab now contains a persistent mount for /var. Next, we will switch the live system to use it.
Switch /var to the new filesystem safely
We will move the old /var out of the way as a rollback path, create a fresh mount point, mount the new filesystem, and then verify the content is present.
sudo mv /var /var.old
sudo mkdir -p /var
sudo mount /var
sudo ls -la /var | head
/var is now mounted from the new filesystem, and we have retained /var.old as a rollback copy. The directory listing confirms that expected content exists.
Now we restart the services we stopped earlier.
sudo systemctl start systemd-journald 2>/dev/null || true
sudo systemctl start rsyslog 2>/dev/null || true
sudo systemctl start cron 2>/dev/null || true
sudo systemctl start crond 2>/dev/null || true
Services are now writing to the new /var. If everything is stable after verification and a reboot, we can later remove /var.old to reclaim space.
Verify /var mount and options
We will confirm that /var is mounted from the expected UUID and that the mount options are in effect.
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /var
sudo grep -E '^[^#].+s/vars' /etc/fstab || true
sudo df -hT /var
We have verified the live mount, the persistent configuration, and the capacity. This is the point where operational stability improves: log storms fill /var, not /.
Step 7: Harden /home mount options (when /home is a separate filesystem)
On multi-user systems and many enterprise builds, /home is separate. Hardening /home reduces the chance that user-writable space becomes a privileged execution path. The safest baseline is nodev,nosuid. We will treat noexec as optional because it can break developer workflows and some automation.
First, we check whether /home is a separate mount and capture its current options.
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /home || true
If /home is not a separate mount, mount options cannot be applied independently without creating a dedicated filesystem. If it is separate, we can proceed to adjust /etc/fstab.
Now we update the /home entry in /etc/fstab to include nodev,nosuid. We will do this carefully by printing the current line first, then editing with a safe approach.
sudo cp -a /etc/fstab "/etc/fstab.bak.$(date +%F_%H%M%S)"
sudo awk 'BEGIN{found=0} $0 !~ /^#/ && $2=="/home"{found=1} END{if(found==0) exit 1}' /etc/fstab && echo "Found /home in fstab" || echo "No /home entry found in fstab"
We have confirmed whether /home is managed by fstab. If it is, we can edit the options. Because fstab formats vary, we will use a controlled manual edit step using a console editor in production change windows.
We will open /etc/fstab and add nodev,nosuid to the options field for the /home line. We will not change the device or filesystem type.
sudo editor /etc/fstab
We have now updated /etc/fstab. Next, we apply the mount changes and verify. If the system refuses to remount due to busy files, we can schedule a reboot, but we will try a remount first.
sudo mount -o remount /home 2>/dev/null || true
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /home || true
/home now reflects the hardened options if it is a separate filesystem and the remount succeeded. If it did not, a reboot will apply the new options at boot.
Step 8: Validate persistence and boot safety
The most expensive failure mode here is a system that does not boot because of a bad /etc/fstab entry. We will validate fstab syntax and then perform a controlled reboot when possible.
Validate fstab entries without rebooting
We will ask the system to mount everything defined in fstab. If there is a syntax error or a missing device, this will surface immediately.
sudo mount -a
If mount -a returns no output and exit code 0, the entries are syntactically valid and mountable in the current environment.
Now we verify the key mounts again.
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /tmp
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /var/tmp
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /var || true
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /home || true
We have confirmed the effective mount options. This is the moment where the hardening becomes real: the kernel is enforcing these constraints, not just configuration files.
Reboot verification (recommended)
When we have a maintenance window, we should reboot to confirm the system comes back cleanly and mounts are applied early in boot.
sudo systemctl reboot
After the system returns, we re-run the mount verification commands above. If everything matches, the configuration is persistent and stable.
Firewall considerations
Disk partitioning and mount options do not require firewall changes. The only firewall-related operational note is indirect: if we separate /var and logging becomes more reliable, we should ensure remote logging ports (if used) are explicitly allowed and monitored. That is environment-specific and should be handled in the broader hardening baseline rather than tied to this storage change.
Troubleshooting
System drops into emergency mode after reboot
- Symptom: Boot stops with messages about failing to mount a filesystem; console shows “You are in emergency mode.”
- Likely causes: Wrong UUID in
/etc/fstab, wrong filesystem type, typo in mount options, device not present at boot. - Fix: From the emergency shell, remount root read-write, correct
/etc/fstab, then reboot.
mount -o remount,rw /
cat /etc/fstab
blkid
editor /etc/fstab
systemctl reboot
We have now made the root filesystem writable, inspected the failing configuration, confirmed actual UUIDs with blkid, and corrected fstab.
/tmp hardening breaks an application
- Symptom: An installer or agent fails with “Permission denied” when executing from
/tmp, or scripts fail when unpacked into/tmp. - Likely causes:
noexecprevents execution from/tmp. - Fix: Adjust the workflow to execute from a trusted path (preferred), or selectively relax
noexecif business requirements demand it.
First, we confirm the mount options.
sudo findmnt -no OPTIONS /tmp
If noexec is present and the workload truly requires execution from /tmp, we can change the systemd unit options and reload. We should treat this as an exception and document it.
sudo editor /etc/systemd/system/tmp.mount
sudo systemctl daemon-reload
sudo systemctl restart tmp.mount
sudo findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /tmp
We have now applied the updated policy for /tmp. The verification confirms the effective options.
/var migration results in missing files or service failures
- Symptom: Services fail to start; logs mention missing directories under
/varor permission issues. - Likely causes: Incomplete copy, services wrote new files during copy, missing ACLs/xattrs, SELinux contexts not preserved (on SELinux systems).
- Fix: Stop relevant services, re-run
rsync, and restore contexts if applicable.
sudo rsync -aHAX --numeric-ids /var.old/ /var/
We have now re-synchronized the data from the rollback copy into the active /var. If SELinux is enabled, we should also restore contexts.
getenforce 2>/dev/null || true
sudo restorecon -RFv /var 2>/dev/null || true
We have checked SELinux mode and restored contexts where applicable.
Common mistakes
Mounting by device name instead of UUID
- Symptom: After adding a disk or changing boot order, the system mounts the wrong partition or fails to mount.
- Fix: Use
UUID=in/etc/fstaband confirm withblkid.
sudo blkid
sudo grep -E '^[^#]' /etc/fstab
We have now listed stable identifiers and confirmed what fstab is using.
Applying noexec to /var without validating workloads
- Symptom: Package managers, agents, or application updaters fail unexpectedly; errors reference execution from under
/var. - Fix: Remove
noexecfrom/varmount options and remount, then re-test.
sudo editor /etc/fstab
sudo mount -o remount /var
sudo findmnt -no OPTIONS /var
We have now corrected the mount policy and confirmed the effective options.
Forgetting the sticky bit on /tmp
- Symptom: Users or processes can delete other users’ temporary files; unexpected application failures.
- Fix: Ensure
/tmppermissions are1777.
sudo chmod 1777 /tmp
sudo stat -c '%a %n' /tmp
We have enforced the sticky bit and verified the permissions.
How do we at NIILAA look at this
This setup is not impressive because it is complex. It is impressive because it is controlled. Every component is intentional. Every configuration has a reason. This is how infrastructure should scale — quietly, predictably, and without drama.
At NIILAA, we help organizations design partitioning standards, implement hardened mount policies, validate application compatibility, and operationalize these controls across fleets with consistent verification and rollback paths. Whether we are working on a single critical server or an enterprise baseline, we focus on changes that stay correct after the next update, the next incident, and the next growth spike.
Website: https://www.niilaa.com
Email: [email protected]
LinkedIn: https://www.linkedin.com/company/niilaa
Facebook: https://www.facebook.com/niilaa.llc