Add '{partition-layout.sh}'
Separate drive partitions for /tmp /home /var /var/tmp /var/log /var/log/audit with restrictive mount options
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
#!/usr/bin/env bash
|
||||
###############################################################################
|
||||
# NIST 800-53 / CIS Partition Layout – Ubuntu 24.04 (Proxmox VM Template)
|
||||
#
|
||||
# PURPOSE:
|
||||
# Migrates a default single-partition Ubuntu install into a proper
|
||||
# LVM-based layout with separate volumes for /tmp, /home, /var,
|
||||
# /var/tmp, /var/log, and /var/log/audit, each with restrictive
|
||||
# mount options.
|
||||
#
|
||||
# PREREQUISITES:
|
||||
# - A second virtual disk attached to the VM (e.g., /dev/sdb)
|
||||
# Add it in Proxmox: Hardware → Add → Hard Disk
|
||||
# - Run BEFORE the hardening script
|
||||
# - Run from a root shell (sudo -i)
|
||||
#
|
||||
# STRATEGY:
|
||||
# Uses LVM on the second disk so volumes can be resized later.
|
||||
# After this script, the original root disk holds only / and /boot.
|
||||
#
|
||||
# Usage:
|
||||
# chmod +x partition-layout.sh
|
||||
# sudo ./partition-layout.sh /dev/sdb # <-- your second disk
|
||||
###############################################################################
|
||||
set -euo pipefail
|
||||
|
||||
LOG="/var/log/partition-layout-$(date +%Y%m%d-%H%M%S).log"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
banner() { printf '\n\e[1;36m>>> %s\e[0m\n' "$1"; }
|
||||
|
||||
if [[ $EUID -ne 0 ]]; then echo "Run as root." >&2; exit 1; fi
|
||||
|
||||
DISK="${1:-}"
|
||||
if [[ -z "$DISK" ]]; then
|
||||
echo "Usage: $0 /dev/sdX"
|
||||
echo " Supply the SECOND disk device (the one you added in Proxmox)."
|
||||
echo ""
|
||||
echo "Available disks:"
|
||||
lsblk -d -o NAME,SIZE,TYPE,MOUNTPOINTS | grep disk
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -b "$DISK" ]]; then
|
||||
echo "ERROR: $DISK is not a valid block device." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Safety check — abort if the disk has existing partitions with data
|
||||
if lsblk -n "$DISK" | grep -q 'part'; then
|
||||
echo "WARNING: $DISK already has partitions:"
|
||||
lsblk "$DISK"
|
||||
read -rp "This will DESTROY all data on $DISK. Continue? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || exit 0
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# CONFIGURATION – Adjust sizes to your needs
|
||||
# These are minimums; LVM lets you grow them later with lvextend + resize2fs
|
||||
###############################################################################
|
||||
VG_NAME="vg_nist"
|
||||
|
||||
# Size allocations (adjust to your total disk size)
|
||||
# For a 50 GB second disk, this uses ~38 GB and leaves ~12 GB free in the VG
|
||||
declare -A LV_SIZES=(
|
||||
[lv_home]="8G"
|
||||
[lv_tmp]="2G"
|
||||
[lv_var]="10G"
|
||||
[lv_var_tmp]="2G"
|
||||
[lv_var_log]="8G"
|
||||
[lv_var_log_audit]="4G"
|
||||
)
|
||||
|
||||
# Mount options per NIST 800-53 / CIS
|
||||
declare -A MOUNT_OPTS=(
|
||||
[/home]="defaults,nosuid,nodev"
|
||||
[/tmp]="defaults,nosuid,nodev,noexec"
|
||||
[/var]="defaults,nosuid"
|
||||
[/var/tmp]="defaults,nosuid,nodev,noexec"
|
||||
[/var/log]="defaults,nosuid,nodev,noexec"
|
||||
[/var/log/audit]="defaults,nosuid,nodev,noexec"
|
||||
)
|
||||
|
||||
# Map LV names to mount points
|
||||
declare -A LV_MOUNTS=(
|
||||
[lv_home]="/home"
|
||||
[lv_tmp]="/tmp"
|
||||
[lv_var]="/var"
|
||||
[lv_var_tmp]="/var/tmp"
|
||||
[lv_var_log]="/var/log"
|
||||
[lv_var_log_audit]="/var/log/audit"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# STEP 1: Install LVM tools if missing
|
||||
###############################################################################
|
||||
banner "Installing LVM2"
|
||||
apt-get update -y && apt-get install -y lvm2
|
||||
|
||||
###############################################################################
|
||||
# STEP 2: Partition the disk and create the physical volume + volume group
|
||||
###############################################################################
|
||||
banner "Preparing $DISK for LVM"
|
||||
|
||||
wipefs -a "$DISK"
|
||||
parted -s "$DISK" mklabel gpt
|
||||
parted -s "$DISK" mkpart primary 1MiB 100%
|
||||
parted -s "$DISK" set 1 lvm on
|
||||
|
||||
# Refresh partition table
|
||||
partprobe "$DISK"
|
||||
sleep 2
|
||||
|
||||
# Identify the partition (sdb1 or vdb1 etc.)
|
||||
PART="${DISK}1"
|
||||
# Handle NVMe naming (nvme0n1p1)
|
||||
if [[ "$DISK" == *nvme* ]] || [[ "$DISK" == *loop* ]]; then
|
||||
PART="${DISK}p1"
|
||||
fi
|
||||
|
||||
pvcreate -f "$PART"
|
||||
vgcreate "$VG_NAME" "$PART"
|
||||
|
||||
echo "Volume group created:"
|
||||
vgdisplay "$VG_NAME" | grep -E 'VG Name|VG Size|Free'
|
||||
|
||||
###############################################################################
|
||||
# STEP 3: Create logical volumes
|
||||
###############################################################################
|
||||
banner "Creating logical volumes"
|
||||
|
||||
# Order matters: /var must be populated before /var/log and /var/log/audit
|
||||
ORDERED_LVS=(lv_var lv_var_log lv_var_log_audit lv_home lv_tmp lv_var_tmp)
|
||||
|
||||
for lv in "${ORDERED_LVS[@]}"; do
|
||||
lvcreate -L "${LV_SIZES[$lv]}" -n "$lv" "$VG_NAME" -y
|
||||
mkfs.ext4 -L "$lv" "/dev/${VG_NAME}/${lv}"
|
||||
echo " Created $lv (${LV_SIZES[$lv]})"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Remaining free space in VG (available for future growth):"
|
||||
vgdisplay "$VG_NAME" | grep 'Free'
|
||||
|
||||
###############################################################################
|
||||
# STEP 4: Migrate data from existing directories to new volumes
|
||||
#
|
||||
# We process them in dependency order: /var first, then /var/log,
|
||||
# then /var/log/audit, so nested mounts copy correctly.
|
||||
###############################################################################
|
||||
banner "Migrating data to new volumes"
|
||||
|
||||
# Migration order: deepest-nesting last when copying, first when mounting
|
||||
MIGRATE_ORDER=(lv_var lv_var_log lv_var_log_audit lv_home lv_tmp lv_var_tmp)
|
||||
|
||||
for lv in "${MIGRATE_ORDER[@]}"; do
|
||||
mount_point="${LV_MOUNTS[$lv]}"
|
||||
dev="/dev/${VG_NAME}/${lv}"
|
||||
temp_mount="/mnt/migrate_${lv}"
|
||||
|
||||
echo "Migrating ${mount_point} ..."
|
||||
|
||||
mkdir -p "$temp_mount"
|
||||
mount "$dev" "$temp_mount"
|
||||
|
||||
# Copy existing data preserving permissions
|
||||
if [[ -d "$mount_point" ]]; then
|
||||
rsync -aAXq "${mount_point}/" "$temp_mount/" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
umount "$temp_mount"
|
||||
rmdir "$temp_mount"
|
||||
|
||||
echo " ✓ ${mount_point} data copied to ${dev}"
|
||||
done
|
||||
|
||||
###############################################################################
|
||||
# STEP 5: Build fstab entries
|
||||
###############################################################################
|
||||
banner "Updating /etc/fstab"
|
||||
|
||||
cp /etc/fstab /etc/fstab.bak.$(date +%s)
|
||||
|
||||
# Remove any existing entries for these mount points (avoid duplicates)
|
||||
for lv in "${ORDERED_LVS[@]}"; do
|
||||
mp="${LV_MOUNTS[$lv]}"
|
||||
# Escape slashes for sed
|
||||
mp_escaped=$(echo "$mp" | sed 's|/|\\/|g')
|
||||
sed -i "\| ${mp_escaped} |d" /etc/fstab
|
||||
done
|
||||
|
||||
# Also remove existing tmpfs /tmp entry if the hardening script added one
|
||||
sed -i '/tmpfs.*\/tmp/d' /etc/fstab
|
||||
|
||||
# Append new entries
|
||||
{
|
||||
echo ""
|
||||
echo "# ===== NIST 800-53 Separate Partitions (LVM on $DISK) ====="
|
||||
for lv in "${ORDERED_LVS[@]}"; do
|
||||
mp="${LV_MOUNTS[$lv]}"
|
||||
opts="${MOUNT_OPTS[$mp]}"
|
||||
echo "/dev/${VG_NAME}/${lv} ${mp} ext4 ${opts} 0 2"
|
||||
done
|
||||
echo ""
|
||||
echo "# /dev/shm hardening"
|
||||
echo "tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0"
|
||||
} >> /etc/fstab
|
||||
|
||||
echo "New fstab:"
|
||||
cat /etc/fstab
|
||||
|
||||
###############################################################################
|
||||
# STEP 6: Mount everything and verify
|
||||
###############################################################################
|
||||
banner "Mounting all new volumes"
|
||||
|
||||
# Must mount in order: /var before /var/log before /var/log/audit
|
||||
MOUNT_ORDER=(/var /var/log /var/log/audit /home /tmp /var/tmp)
|
||||
|
||||
for mp in "${MOUNT_ORDER[@]}"; do
|
||||
mount "$mp"
|
||||
echo " ✓ Mounted $mp"
|
||||
done
|
||||
|
||||
# Set sticky bit on /tmp and /var/tmp
|
||||
chmod 1777 /tmp /var/tmp
|
||||
|
||||
# Ensure /var/log/audit exists with correct ownership
|
||||
mkdir -p /var/log/audit
|
||||
chmod 700 /var/log/audit
|
||||
|
||||
# Remount /dev/shm
|
||||
mount -o remount /dev/shm 2>/dev/null || true
|
||||
|
||||
###############################################################################
|
||||
# STEP 7: Restart critical services that use /var/log and /tmp
|
||||
###############################################################################
|
||||
banner "Restarting services"
|
||||
|
||||
systemctl restart rsyslog 2>/dev/null || true
|
||||
systemctl restart systemd-journald 2>/dev/null || true
|
||||
systemctl restart auditd 2>/dev/null || true
|
||||
|
||||
###############################################################################
|
||||
# VERIFICATION
|
||||
###############################################################################
|
||||
banner "Verification"
|
||||
|
||||
echo ""
|
||||
echo "━━━ Mount Points ━━━"
|
||||
df -h /home /tmp /var /var/tmp /var/log /var/log/audit 2>/dev/null | \
|
||||
awk 'NR==1 || /vg_nist/'
|
||||
echo ""
|
||||
echo "━━━ Mount Options ━━━"
|
||||
for mp in "${MOUNT_ORDER[@]}"; do
|
||||
opts=$(findmnt -n -o OPTIONS "$mp" 2>/dev/null || echo "NOT MOUNTED")
|
||||
printf " %-20s %s\n" "$mp" "$opts"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "━━━ LVM Free Space (for future growth) ━━━"
|
||||
vgdisplay "$VG_NAME" | grep -E 'Free|VG Size'
|
||||
|
||||
cat <<'DONE'
|
||||
|
||||
╔══════════════════════════════════════════════════════════════════╗
|
||||
║ Partition Layout Complete ║
|
||||
╠══════════════════════════════════════════════════════════════════╣
|
||||
║ ║
|
||||
║ Separate mount points created: ║
|
||||
║ /home – nosuid, nodev ║
|
||||
║ /tmp – nosuid, nodev, noexec ║
|
||||
║ /var – nosuid ║
|
||||
║ /var/tmp – nosuid, nodev, noexec ║
|
||||
║ /var/log – nosuid, nodev, noexec ║
|
||||
║ /var/log/audit – nosuid, nodev, noexec ║
|
||||
║ /dev/shm – nosuid, nodev, noexec ║
|
||||
║ ║
|
||||
║ To grow a volume later: ║
|
||||
║ lvextend -L +5G /dev/vg_nist/lv_var_log ║
|
||||
║ resize2fs /dev/vg_nist/lv_var_log ║
|
||||
║ ║
|
||||
║ NEXT: Reboot to confirm everything mounts cleanly, ║
|
||||
║ then run the hardening script. ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
DONE
|
||||
|
||||
echo "Full log: $LOG"
|
||||
Reference in New Issue
Block a user