CIS Hardening of a Debian Linux Server Part 8: Level 2 Initial Setup
This is the ninth entry in the series documenting the Debian 13 server CIS hardening project.
This pass covers Level 2 recommendations that were deferred in the Level 1 pass.
1.1.1 Configure Filesystem Kernel Modules #
This part covers 1.1.1.6 through 1.1.1.8 of the benchmark. The remainder of the section was handled in the Level 1 pass.
For each of these modules, if a dependent functionality is required later on, such as containers, the module must be re-enabled.
1.1.1.6 Ensure Overlay Kernel Module Is Not Available #
Disabling this module can severely disrupt containerized workloads, since applications such as Docker and Kubernetes rely on it.
#!/usr/bin/env bash
{
l_mod_name="overlayfs" l_mod_type="fs"
while IFS= read -r l_mod_path; do
if [ -d "$l_mod_path/${l_mod_name//-/\/}" ] && \
[ -n "$(ls -A "$l_mod_path/${l_mod_name//-/\/}")" ]; then
printf '%s\n' "$l_mod_name exists in $l_mod_path"
fi
done < <(readlink -e /usr/lib/modules/**/kernel/$l_mod_type \
|| readlink -e /lib/modules/**/kernel/$l_mod_type)
}
overlayfs exists in /usr/lib/modules/6.12.94+deb13-cloud-amd64/kernel/fs
overlayfs exists in /usr/lib/modules/6.12.95+deb13-cloud-amd64/kernel/fs
The module is present for both installed kernels, so remediation is required.
# lsmod | grep 'overlay'
# modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+overlay\b'
Neither command returns output. The module is not currently loaded, but it is not yet disabled through configuration.
The unload commands are run regardless, as a precaution (both commands do approximately the same thing):
# modprobe -r overlay 2>/dev/null
# rmmod overlay 2>/dev/null
The module is then blacklisted and its install path pointed to /bin/false:
# printf '%s\n' "" "install overlay /bin/false" >> /etc/modprobe.d/60-overlay.conf
# printf '%s\n' "" "blacklist overlay" >> /etc/modprobe.d/60-overlay.conf
Rechecking confirms the change:
# modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+overlay\b'
blacklist overlay
install overlay /bin/false
1.1.1.7 Ensure Squashfs Kernel Module Is Not Available #
Disabling this module causes Snap packages to fail, since Snap relies on SquashFS as its compressed filesystem format.
The audit script is identical to the one used for overlayfs, substituting
squashfs for the module name:
squashfs exists in /usr/lib/modules/6.12.94+deb13-cloud-amd64/kernel/fs
squashfs exists in /usr/lib/modules/6.12.95+deb13-cloud-amd64/kernel/fs
Remediation is required.
# lsmod | grep 'squashfs'
No output. The module is not loaded.
# modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+squashfs\b'
No output. The module is not yet disabled through configuration.
Since Snap depends on this module, its presence on the system is checked before proceeding:
# dpkg -l snapd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-============-============-=================================
un snapd <none> <none> (no description available)
The un status indicates snapd is not installed.
# which command
# command -v snap
Neither command returns a result, confirming Snap is absent. Remediation can proceed.
# modprobe -r squashfs 2>/dev/null
# rmmod squashfs 2>/dev/null
# printf '%s\n' "" "install squashfs /bin/false" >> /etc/modprobe.d/60-squashfs.conf
# printf '%s\n' "" "blacklist squashfs" >> /etc/modprobe.d/60-squashfs.conf
# modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+squashfs\b'
blacklist squashfs
install squashfs /bin/false
1.1.1.8 Ensure Udf Kernel Module Is Not Available #
Microsoft Azure requires this module, and it should not be disabled on systems running there. That does not apply here, since this system runs on Hetzner Cloud rather than Azure.
The same audit script applies, substituting udf for the module name:
udf exists in /usr/lib/modules/6.12.94+deb13-cloud-amd64/kernel/fs
udf exists in /usr/lib/modules/6.12.95+deb13-cloud-amd64/kernel/fs
Remediation is required.
# lsmod | grep 'udf'
No output. The module is not loaded.
# modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+udf\b'
No output. The module is not yet disabled through configuration. The unload commands are run regardless:
# modprobe -r udf 2>/dev/null
# rmmod udf 2>/dev/null
# printf '%s\n' "" "install udf /bin/false" >> /etc/modprobe.d/60-udf.conf
# printf '%s\n' "" "blacklist udf" >> /etc/modprobe.d/60-udf.conf
# modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+udf\b'
blacklist udf
install udf /bin/false
1.1.2 Configure Filesystem Partitions #
This section covers 1.1.2.3 through 1.1.2.7 of the benchmark. Recommendations 1.1.2.1 and 1.1.2.2 were handled in the Level 1 pass.
An initial audit of /home shows the gap this section addresses:
#!/usr/bin/env bash
{
l_valid_shells="^($(awk -F\/ '$NF != "nologin" {print}' /etc/shells | sed -rn '/^\//{s,/,\\\\/,g;p}' | paste -s -d '|' - ))$"
awk -v pat="$l_valid_shells" -F: '($1!~/^(root|halt|sync|shutdown|nfsnobody)$/ && ($3>='"$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"' || $3 != 65534) && $(NF) ~ pat) {print $1 " - " $6}' /etc/passwd
}
sharaf - /home/sharaf
# findmnt -kn /home
No output. /home is not on a separate partition, which is expected, as the
system was originally installed with a single root partition.
Resolving this requires repartitioning a live, already-installed system, a procedure not covered in step-by-step detail by the benchmark itself. A companion guide to this series, published on Hetzner’s community platform: Post-Install Repartitioning for CIS Hardening Compliance, walks through it in full. What follows is that procedure executed against this server.
The procedure starts with disabling automatic root partition growth via touch /etc/growroot-disabled, and a snapshot of the server is taken as a safety net.
The Hetzner rescue system is then enabled and the server rebooted into it, since
the root partition cannot be resized while mounted and in active use.
The root filesystem is shrunk and checked from the rescue system:
root@rescue ~ # e2fsck -f -y /dev/sda1
e2fsck 1.47.0 (5-Feb-2023)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/sda1: 47085/2436864 files (0.2% non-contiguous), 602222/9937403 blocks
root@rescue ~ # resize2fs /dev/sda1 8G
resize2fs 1.47.0 (5-Feb-2023)
Resizing the filesystem on /dev/sda1 to 2097152 (4k) blocks.
The filesystem on /dev/sda1 is now 2097152 (4k) blocks long.
root@rescue ~ # parted /dev/sda
GNU Parted 3.5
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: QEMU QEMU HARDDISK (scsi)
Disk /dev/sda: 41.0GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
14 1049kB 2097kB 1049kB bios_grub
15 2097kB 258MB 256MB fat32 boot, esp
1 258MB 41.0GB 40.7GB ext4
(parted) resizepart 1 10GiB
Warning: Shrinking a partition can cause data loss, are you sure you want to continue?
Yes/No? yes
(parted) quit
Information: You may need to update /etc/fstab.
root@rescue ~ # resize2fs /dev/sda1
resize2fs 1.47.0 (5-Feb-2023)
Resizing the filesystem on /dev/sda1 to 2558464 (4k) blocks.
The filesystem on /dev/sda1 is now 2558464 (4k) blocks long.
root@rescue ~ # e2fsck -f -y /dev/sda1
e2fsck 1.47.0 (5-Feb-2023)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/sda1: 47085/633264 files (0.3% non-contiguous), 487828/2558464 blocks
The remaining space is used to create one partition per target mount point:
root@rescue ~ # parted -a optimal /dev/sda mkpart home ext4 10GiB 20GiB
Information: You may need to update /etc/fstab.
root@rescue ~ # parted -a optimal /dev/sda mkpart var ext4 20GiB 24GiB
Information: You may need to update /etc/fstab.
root@rescue ~ # parted -a optimal /dev/sda mkpart vartmp ext4 24GiB 26GiB
Information: You may need to update /etc/fstab.
root@rescue ~ # parted -a optimal /dev/sda mkpart varlog ext4 26GiB 32GiB
Information: You may need to update /etc/fstab.
root@rescue ~ # parted -a optimal /dev/sda mkpart varlogaudit ext4 32GiB 36GiB
Information: You may need to update /etc/fstab.
root@rescue ~ # lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 3.8G 1 loop
sda 8:0 0 38.1G 0 disk
├─sda1 8:1 0 9.8G 0 part
├─sda2 8:2 0 10G 0 part
├─sda3 8:3 0 4G 0 part
├─sda4 8:4 0 2G 0 part
├─sda5 8:5 0 6G 0 part
├─sda6 8:6 0 4G 0 part
├─sda14 8:14 0 1M 0 part
└─sda15 8:15 0 244M 0 part
sr0 11:0 1 1024M 0 rom
root@rescue ~ # mkfs.ext4 /dev/sda2
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 2621440 4k blocks and 655360 inodes
Filesystem UUID: ca933296-6391-4495-a400-b8ce9e4c79f3
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
root@rescue ~ # mkfs.ext4 /dev/sda3
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 1048576 4k blocks and 262144 inodes
Filesystem UUID: e4840e1e-c3d4-4b8c-8406-96cb8ad68ca0
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
root@rescue ~ # mkfs.ext4 /dev/sda4
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 524288 4k blocks and 131072 inodes
Filesystem UUID: deeeefa0-c179-4bff-85c6-15632670806a
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
root@rescue ~ # mkfs.ext4 /dev/sda5
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 1572864 4k blocks and 393216 inodes
Filesystem UUID: af407528-4cd2-44c9-9c3d-890aa626ef66
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
root@rescue ~ # mkfs.ext4 /dev/sda6
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 1048576 4k blocks and 262144 inodes
Filesystem UUID: d1fc4ca8-9bf4-485f-8111-90ae55a31a39
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
Data is then migrated from the root filesystem to each new partition using rsync:
root@rescue ~ # mkdir -p /mnt/old_root /mnt/new_home /mnt/new_var /mnt/new_vartmp /mnt/new_varlog /mnt/new_varaudit
root@rescue ~ # mount /dev/sda1 /mnt/old_root
root@rescue ~ # mount /dev/sda2 /mnt/new_home
root@rescue ~ # mount /dev/sda3 /mnt/new_var
root@rescue ~ # mount /dev/sda4 /mnt/new_vartmp
root@rescue ~ # mount /dev/sda5 /mnt/new_varlog
root@rescue ~ # mount /dev/sda6 /mnt/new_varaudit
root@rescue ~ # rsync -aHAX /mnt/old_root/home/ /mnt/new_home/
root@rescue ~ # rsync -aHAX /mnt/old_root/var/tmp/ /mnt/new_vartmp/
root@rescue ~ # rsync -aHAX --exclude='tmp/' --exclude='log/' /mnt/old_root/var/ /mnt/new_var/
root@rescue ~ # rsync -aHAX /mnt/old_root/var/log/ /mnt/new_varlog/
The migrated data is verified on each new partition before continuing:
root@rescue ~ # ls -la /mnt/new_home/
total 24K
drwxr-xr-x 4 root root 4.0K Jun 27 15:49 .
drwxr-xr-x 1 root root 160 Jul 19 01:19 ..
drwx------ 2 root root 16K Jul 19 01:15 lost+found
drwx------ 3 1000 1000 4.0K Jul 18 05:06 sharaf
root@rescue ~ # ls -la /mnt/new_vartmp/
total 24K
drwxrwxrwt 4 root root 4.0K Jul 19 00:50 .
drwxr-xr-x 1 root root 160 Jul 19 01:19 ..
drwxrwxrwt 2 root root 4.0K Jun 30 00:18 cloud-init
drwx------ 2 root root 16K Jul 19 01:15 lost+found
root@rescue ~ # ls -la /mnt/new_var/
total 52K
drwxr-xr-x 10 root root 4.0K May 12 15:23 .
drwxr-xr-x 1 root root 160 Jul 19 01:19 ..
drwxr-xr-x 2 root root 4.0K Jul 15 02:00 backups
drwxr-xr-x 9 root root 4.0K Jun 30 23:01 cache
drwxr-xr-x 26 root root 4.0K Jul 14 11:17 lib
drwxr-xr-x 2 root root 4.0K Jan 2 2026 local
lrwxrwxrwx 1 root root 9 May 12 15:19 lock -> /run/lock
drwx------ 2 root root 16K Jul 19 01:15 lost+found
drwxrwsr-x 2 root mail 4.0K May 12 15:19 mail
drwxr-xr-x 2 root root 4.0K May 12 15:19 opt
lrwxrwxrwx 1 root root 4 May 12 15:19 run -> /run
drwxr-xr-x 4 root root 4.0K Jul 12 00:34 spool
-rw-r--r-- 1 root root 208 May 12 15:19 .updated
root@rescue ~ # ls -la /mnt/new_varlog/
total 14M
drwxr-xr-x 10 root root 4.0K Jul 19 00:50 .
drwxr-xr-x 1 root root 160 Jul 19 01:19 ..
drwxr-sr-x 2 988 adm 4.0K Jul 18 04:37 aide
-rw-r----- 1 root root 1.5K Jul 12 08:36 alternatives.log
-rw-r----- 1 root root 815 Jun 27 15:41 alternatives.log.1
drwxr-xr-x 2 root root 4.0K Apr 10 2025 apparmor
drwxr-xr-x 2 root root 4.0K Jul 14 11:17 apt
-rw-r----- 1 root adm 7.4M Jul 19 00:50 auth.log
-rw-rw---- 1 root utmp 0 Jul 1 02:28 btmp
-rw-rw---- 1 root utmp 0 May 12 15:19 btmp.1
-rw-r----- 1 root adm 196K Jun 30 00:18 cloud-init.log
-rw-r----- 1 root adm 7.3K Jun 30 00:18 cloud-init-output.log
-rw-r----- 1 root adm 21K Jul 19 00:17 cron.log
-rw-r----- 1 root root 33K Jul 14 11:17 dpkg.log
-rw-r----- 1 root root 59K Jun 30 23:01 dpkg.log.1
drwxr-sr-x+ 4 root systemd-journal 4.0K Jun 27 13:38 journal
-rw-r----- 1 root adm 1.1M Jul 18 23:13 kern.log
-rw-rw-r-- 1 root utmp 286K Jul 19 00:48 lastlog
drwx------ 2 root root 16K Jul 19 01:15 lost+found
-rw-r----- 1 root root 446K Jul 16 04:00 lynis.log
-rw-r----- 1 root root 57K Jul 16 04:00 lynis-report.dat
drwx------ 2 root root 4.0K Jun 27 13:38 private
lrwxrwxrwx 1 root root 39 May 12 15:19 README -> ../../usr/share/doc/systemd/README.logs
drwxr-xr-x 3 root root 4.0K Jun 27 15:40 runit
-rw-r----- 1 root root 5.8K Jul 19 00:49 sudo.log
-rw-r----- 1 root adm 1.8M Jul 19 00:50 syslog
-rw-r----- 1 root adm 890K Jul 18 23:13 ufw.log
-rw-r----- 1 root adm 156K Jul 12 01:00 ufw.log.1
drwxr-xr-x 2 root root 4.0K Jul 7 02:08 unattended-upgrades
-rw-r----- 1 root adm 80 Jul 13 03:42 user.log
-rw-r----- 1 root adm 1.2M Jul 19 00:50 warnings.log
-rw-rw-r-- 1 root utmp 48K Jul 19 00:48 wtmp
-rw-r--r-- 1 root root 20K Jul 19 00:50 wtmp.db
/var/log/audit did not yet exist on this system, so nothing was migrated to
it. The original data is then removed from the root filesystem to reclaim space,
and each new partition is unmounted, leaving /dev/sda1 mounted for the fstab
update that follows:
root@rescue ~ # rm -Rf /mnt/old_root/home/
root@rescue ~ # rm -Rf /mnt/old_root/var/
root@rescue ~ # mkdir -p /mnt/old_root/home
root@rescue ~ # mkdir -p /mnt/old_root/var
root@rescue ~ # mkdir -p /mnt/new_var/tmp
root@rescue ~ # mkdir -p /mnt/new_var/log
root@rescue ~ # mkdir -p /mnt/new_varlog/audit
root@rescue ~ # umount /mnt/new_home
root@rescue ~ # umount /mnt/new_var
root@rescue ~ # umount /mnt/new_vartmp
root@rescue ~ # umount /mnt/new_varlog
root@rescue ~ # umount /mnt/new_varaudit
The UUIDs of the new partitions are collected and added to /etc/fstab on the
mounted root filesystem, using 2 as the fsck pass value to enable checks:
root@rescue ~ # blkid | grep -E "sda[2-6]"
/dev/sda4: UUID="deeeefa0-c179-4bff-85c6-15632670806a" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="vartmp" PARTUUID="c46818d6-7db7-4188-995e-b03e445cc9dc"
/dev/sda2: UUID="ca933296-6391-4495-a400-b8ce9e4c79f3" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="home" PARTUUID="7943b6fe-0457-4610-b45d-e7dd95995b4e"
/dev/sda5: UUID="af407528-4cd2-44c9-9c3d-890aa626ef66" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="varlog" PARTUUID="a820080f-5f39-486c-b85d-273a3dd4bf83"
/dev/sda3: UUID="e4840e1e-c3d4-4b8c-8406-96cb8ad68ca0" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="var" PARTUUID="84915022-b77b-40cf-8bd3-5c7b0b819176"
/dev/sda6: UUID="d1fc4ca8-9bf4-485f-8111-90ae55a31a39" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="varlogaudit" PARTUUID="bd705270-db64-4eff-a53e-d722e85e78b9"
root@rescue ~ # vim /mnt/old_root/etc/fstab
root@rescue ~ # cat /mnt/old_root/etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# systemd generates mount units based on this file, see systemd.mount(5).
# Please run 'systemctl daemon-reload' after making changes here.
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/sda3 during installation
UUID=8ba91a8e-417d-4e0c-96a7-1f1468d56d79 / ext4 errors=remount-ro 0 1
# /boot/efi was on /dev/sda2 during installation
UUID=8E15-D0E4 /boot/efi vfat umask=0077 0 1
/dev/sr0 /media/cdrom0 udf,iso9660 user,noauto 0 0
tmpfs /tmp tmpfs rw,nosuid,nodev,noexec,size=1959128k,nr_inodes=1048576,inode64 0 0
tmpfs /dev/shm tmpfs rw,nosuid,nodev,noexec,inode64 0 0
UUID=ca933296-6391-4495-a400-b8ce9e4c79f3 /home ext4 defaults,rw,nosuid,nodev,relatime 0 2
UUID=e4840e1e-c3d4-4b8c-8406-96cb8ad68ca0 /var ext4 defaults,rw,nosuid,nodev,relatime 0 2
UUID=deeeefa0-c179-4bff-85c6-15632670806a /var/tmp ext4 defaults,rw,nosuid,nodev,noexec,relatime 0 2
UUID=af407528-4cd2-44c9-9c3d-890aa626ef66 /var/log ext4 defaults,rw,nosuid,nodev,noexec,relatime 0 2
UUID=d1fc4ca8-9bf4-485f-8111-90ae55a31a39 /var/log/audit ext4 defaults,rw,nosuid,nodev,noexec,relatime 0 2
Running findmnt against the updated file confirms it is free of parse errors:
root@rescue ~ # findmnt --verify --fstab --tab-file /mnt/old_root/etc/fstab
/boot/efi
[E] unreachable on boot required target: No such file or directory
/media/cdrom0
[W] unreachable target: No such file or directory
[W] cannot detect on-disk filesystem type (No medium found)
/var/log/audit
[E] unreachable on boot required target: No such file or directory
0 parse errors, 2 errors, 2 warnings
The reported errors and warnings stem from findmnt checking targets against
the rescue environment’s own filesystem, which does not have these directories,
rather than against the target system. With 0 parse errors, the file itself is
syntactically sound. The rescue root filesystem is then unmounted and the server
rebooted into the newly repartitioned disk:
root@rescue ~ # umount /mnt/old_root
root@rescue ~ # reboot
Post-reboot verification confirms each partition mounted at the intended location with the expected options:
# mount | column -t
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
udev on /dev type devtmpfs (rw,nosuid,relatime,size=1944220k,nr_inodes=486055,mode=755,inode64)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=600,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,nodev,noexec,relatime,size=391828k,mode=755,inode64)
/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,inode64)
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
pstore on /sys/fs/pstore type pstore (rw,nosuid,nodev,noexec,relatime)
bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=41,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=390)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,nosuid,nodev,relatime,pagesize=2M)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k,inode64)
debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)
tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /run/credentials/systemd-journald.service type tmpfs (ro,nosuid,nodev,noexec,relatime,nosymfollow,size=1024k,nr_inodes=1024,mode=700,inode64,noswap)
fusectl on /sys/fs/fuse/connections type fusectl (rw,nosuid,nodev,noexec,relatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec,relatime,size=1959128k,nr_inodes=1048576,inode64)
/dev/sda2 on /home type ext4 (rw,nosuid,nodev,relatime)
/dev/sda3 on /var type ext4 (rw,nosuid,nodev,relatime)
/dev/sda15 on /boot/efi type vfat (rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro)
/dev/sda5 on /var/log type ext4 (rw,nosuid,nodev,noexec,relatime)
/dev/sda4 on /var/tmp type ext4 (rw,nosuid,nodev,noexec,relatime)
/dev/sda6 on /var/log/audit type ext4 (rw,nosuid,nodev,noexec,relatime)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /run/credentials/serial-getty@ttyS0.service type tmpfs (ro,nosuid,nodev,noexec,relatime,nosymfollow,size=1024k,nr_inodes=1024,mode=700,inode64,noswap)
tmpfs on /run/credentials/getty@tty1.service type tmpfs (ro,nosuid,nodev,noexec,relatime,nosymfollow,size=1024k,nr_inodes=1024,mode=700,inode64,noswap)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=391824k,nr_inodes=97956,mode=700,uid=1000,gid=1000,inode64)
# df -h
Filesystem Size Used Avail Use% Mounted on
udev 1.9G 0 1.9G 0% /dev
tmpfs 383M 592K 383M 1% /run
/dev/sda1 9.6G 1.2G 8.0G 13% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 1.0M 0 1.0M 0% /run/credentials/systemd-journald.service
tmpfs 1.9G 0 1.9G 0% /tmp
/dev/sda2 9.8G 312K 9.3G 1% /home
/dev/sda3 3.9G 324M 3.4G 9% /var
/dev/sda15 241M 290K 240M 1% /boot/efi
/dev/sda5 5.9G 158M 5.4G 3% /var/log
/dev/sda4 2.0G 44K 1.8G 1% /var/tmp
/dev/sda6 3.9G 24K 3.7G 1% /var/log/audit
tmpfs 1.0M 0 1.0M 0% /run/credentials/serial-getty@ttyS0.service
tmpfs 1.0M 0 1.0M 0% /run/credentials/getty@tty1.service
tmpfs 383M 8.0K 383M 1% /run/user/1000
1.1.2.3 Configure /home #
# findmnt -kn /home
/home /dev/sda2 ext4 rw,nosuid,nodev,relatime
/home is now on its own partition.
# findmnt -kn /home | grep -v nodev
# findmnt -kn /home | grep -v nosuid
No output for either command. Both the nodev and nosuid options are set.
1.1.2.4 Configure /var #
# findmnt -kn /var
/var /dev/sda3 ext4 rw,nosuid,nodev,relatime
# findmnt -kn /var | grep -v nodev
# findmnt -kn /var | grep -v nosuid
No output for either command. Both options are set.
1.1.2.5 Configure /var/tmp #
# findmnt -kn /var/tmp
/var/tmp /dev/sda4 ext4 rw,nosuid,nodev,noexec,relatime
# findmnt -kn /var/tmp | grep -v nodev
# findmnt -kn /var/tmp | grep -v nosuid
# findmnt -kn /var/tmp | grep -v noexec
No output for any of the three commands. nodev, nosuid, and noexec are all set.
1.1.2.6 Configure /var/log #
# findmnt -kn /var/log
/var/log /dev/sda5 ext4 rw,nosuid,nodev,noexec,relatime
# findmnt -kn /var/log | grep -v nodev
# findmnt -kn /var/log | grep -v nosuid
# findmnt -kn /var/log | grep -v noexec
No output for any of the three commands. All three options are set.
1.1.2.7 Configure /var/log/audit #
# findmnt -kn /var/log/audit
/var/log/audit /dev/sda6 ext4 rw,nosuid,nodev,noexec,relatime
# findmnt -kn /var/log/audit | grep -v nodev
# findmnt -kn /var/log/audit | grep -v nosuid
# findmnt -kn /var/log/audit | grep -v noexec
No output for any of the three commands. All three options are set.
1.2.1.2 Ensure Weak Dependencies Are Configured #
# /usr/bin/apt-config dump | /usr/bin/grep "APT::Install-"
APT::Install-Recommends "false";
APT::Install-Suggests "0";
Install-Recommends is set to false rather than 0. Although the two are
likely functionally equivalent, the value is changed directly rather than
relying on that assumption.
# grep "APT::Install-Recommends" /etc/apt/apt.conf.d/*
/etc/apt/apt.conf.d/00InstallRecommends:APT::Install-Recommends "false";
false is changed to 0 in /etc/apt/apt.conf.d/00InstallRecommends, and the
audit is run again:
# /usr/bin/apt-config dump | /usr/bin/grep "APT::Install-"
APT::Install-Recommends "0";
APT::Install-Suggests "0";
Both values now read 0.
1.3.1.3 Ensure All AppArmor Profiles Are Enforcing #
# apparmor_status | grep profiles
105 profiles are loaded.
6 profiles are in enforce mode.
23 profiles are in complain mode.
0 profiles are in prompt mode.
0 profiles are in kill mode.
76 profiles are in unconfined mode.
0 processes have profiles defined.
# apparmor_status | grep processes
0 processes have profiles defined.
0 processes are in enforce mode.
0 processes are in complain mode.
0 processes are in prompt mode.
0 processes are in kill mode.
0 processes are unconfined but have a profile defined.
0 processes are in mixed mode.
The first check fails, since not every profile is in enforce mode, though all are loaded. The second passes, since no running process is unconfined.
Before enforcing, the loaded profiles are reviewed to determine whether doing so is safe:
# aa-status
apparmor module is loaded.
105 profiles are loaded.
6 profiles are in enforce mode.
/usr/bin/man
lsb_release
man_filter
man_groff
nvidia_modprobe
nvidia_modprobe//kmod
23 profiles are in complain mode.
Xorg
plasmashell
plasmashell//QtWebEngineProcess
sbuild
sbuild-abort
sbuild-adduser
sbuild-apt
sbuild-checkpackages
sbuild-clean
sbuild-createchroot
sbuild-destroychroot
sbuild-distupgrade
sbuild-hold
sbuild-shell
sbuild-unhold
sbuild-update
sbuild-upgrade
transmission-cli
transmission-daemon
transmission-gtk
transmission-qt
unix-chkpwd
unprivileged_userns
0 profiles are in prompt mode.
0 profiles are in kill mode.
76 profiles are in unconfined mode.
1password
Discord
MongoDB Compass
QtWebEngineProcess
balena-etcher
brave
buildah
busybox
cam
ch-checkns
ch-run
chrome
chromium
crun
devhelp
element-desktop
epiphany
evolution
firefox
flatpak
foliate
geary
github-desktop
goldendict
ipa_verify
kchmviewer
keybase
lc-compliance
libcamerify
linux-sandbox
loupe
lxc-attach
lxc-create
lxc-destroy
lxc-execute
lxc-stop
lxc-unshare
lxc-usernsexec
mmdebstrap
msedge
nautilus
notepadqq
obsidian
opam
opera
pageedit
polypane
privacybrowser
qcam
qmapshack
qutebrowser
rootlesskit
rpm
rssguard
runc
scide
signal-desktop
slack
slirp4netns
steam
stress-ng
surfshark
systemd-coredump
toybox
trinity
tup
tuxedo-control-center
userbindmount
uwsgi-core
vdens
virtiofsd
vivaldi-bin
vpnns
vscode
wike
wpcom
0 processes have profiles defined.
0 processes are in enforce mode.
0 processes are in complain mode.
0 processes are in prompt mode.
0 processes are in kill mode.
0 processes are unconfined but have a profile defined.
0 processes are in mixed mode.
These profiles match the defaults shipped by AppArmor’s own packages rather
than anything custom. The benchmark notes that switching a profile to enforce
mode removes its unconfined status, which can interrupt an application’s normal
operation, and that further tuning may be required for anything still in active
use. Since none of the loaded profiles have been customized, all of them are
set to enforce, with journalctl -k | grep -i apparmor used afterward to catch
anything that breaks as a result:
# aa-enforce /etc/apparmor.d/*
Setting /etc/apparmor.d/1password to enforce mode.
Profile for /etc/apparmor.d/abi not found, skipping
Profile for /etc/apparmor.d/abstractions not found, skipping
Setting /etc/apparmor.d/balena-etcher to enforce mode.
Setting /etc/apparmor.d/brave to enforce mode.
Setting /etc/apparmor.d/buildah to enforce mode.
Setting /etc/apparmor.d/busybox to enforce mode.
Setting /etc/apparmor.d/cam to enforce mode.
Setting /etc/apparmor.d/ch-checkns to enforce mode.
Setting /etc/apparmor.d/chrome to enforce mode.
Setting /etc/apparmor.d/chromium to enforce mode.
Setting /etc/apparmor.d/ch-run to enforce mode.
Setting /etc/apparmor.d/code to enforce mode.
Setting /etc/apparmor.d/crun to enforce mode.
Setting /etc/apparmor.d/devhelp to enforce mode.
Profile for /etc/apparmor.d/disable not found, skipping
Setting /etc/apparmor.d/Discord to enforce mode.
Setting /etc/apparmor.d/element-desktop to enforce mode.
Setting /etc/apparmor.d/epiphany to enforce mode.
Setting /etc/apparmor.d/evolution to enforce mode.
Setting /etc/apparmor.d/firefox to enforce mode.
Setting /etc/apparmor.d/flatpak to enforce mode.
Setting /etc/apparmor.d/foliate to enforce mode.
Profile for /etc/apparmor.d/force-complain not found, skipping
Setting /etc/apparmor.d/geary to enforce mode.
Setting /etc/apparmor.d/github-desktop to enforce mode.
Setting /etc/apparmor.d/goldendict to enforce mode.
Setting /etc/apparmor.d/ipa_verify to enforce mode.
Setting /etc/apparmor.d/kchmviewer to enforce mode.
Setting /etc/apparmor.d/keybase to enforce mode.
Setting /etc/apparmor.d/lc-compliance to enforce mode.
Setting /etc/apparmor.d/libcamerify to enforce mode.
Setting /etc/apparmor.d/linux-sandbox to enforce mode.
Profile for /etc/apparmor.d/local not found, skipping
Setting /etc/apparmor.d/loupe to enforce mode.
Setting /etc/apparmor.d/lsb_release to enforce mode.
Setting /etc/apparmor.d/lxc-attach to enforce mode.
Setting /etc/apparmor.d/lxc-create to enforce mode.
Setting /etc/apparmor.d/lxc-destroy to enforce mode.
Setting /etc/apparmor.d/lxc-execute to enforce mode.
Setting /etc/apparmor.d/lxc-stop to enforce mode.
Setting /etc/apparmor.d/lxc-unshare to enforce mode.
Setting /etc/apparmor.d/lxc-usernsexec to enforce mode.
Setting /etc/apparmor.d/mmdebstrap to enforce mode.
Setting /etc/apparmor.d/MongoDB_Compass to enforce mode.
Setting /etc/apparmor.d/msedge to enforce mode.
Setting /etc/apparmor.d/nautilus to enforce mode.
Setting /etc/apparmor.d/notepadqq to enforce mode.
Setting /etc/apparmor.d/nvidia_modprobe to enforce mode.
Setting /etc/apparmor.d/obsidian to enforce mode.
Setting /etc/apparmor.d/opam to enforce mode.
Setting /etc/apparmor.d/opera to enforce mode.
Setting /etc/apparmor.d/pageedit to enforce mode.
Setting /etc/apparmor.d/plasmashell to enforce mode.
Setting /etc/apparmor.d/polypane to enforce mode.
Setting /etc/apparmor.d/privacybrowser to enforce mode.
Setting /etc/apparmor.d/qcam to enforce mode.
Setting /etc/apparmor.d/qmapshack to enforce mode.
Setting /etc/apparmor.d/QtWebEngineProcess to enforce mode.
Setting /etc/apparmor.d/qutebrowser to enforce mode.
Setting /etc/apparmor.d/rootlesskit to enforce mode.
Setting /etc/apparmor.d/rpm to enforce mode.
Setting /etc/apparmor.d/rssguard to enforce mode.
Setting /etc/apparmor.d/runc to enforce mode.
Setting /etc/apparmor.d/sbuild to enforce mode.
Setting /etc/apparmor.d/sbuild-abort to enforce mode.
Setting /etc/apparmor.d/sbuild-adduser to enforce mode.
Setting /etc/apparmor.d/sbuild-apt to enforce mode.
Setting /etc/apparmor.d/sbuild-checkpackages to enforce mode.
Setting /etc/apparmor.d/sbuild-clean to enforce mode.
Setting /etc/apparmor.d/sbuild-createchroot to enforce mode.
Setting /etc/apparmor.d/sbuild-destroychroot to enforce mode.
Setting /etc/apparmor.d/sbuild-distupgrade to enforce mode.
Setting /etc/apparmor.d/sbuild-hold to enforce mode.
Setting /etc/apparmor.d/sbuild-shell to enforce mode.
Setting /etc/apparmor.d/sbuild-unhold to enforce mode.
Setting /etc/apparmor.d/sbuild-update to enforce mode.
Setting /etc/apparmor.d/sbuild-upgrade to enforce mode.
Setting /etc/apparmor.d/scide to enforce mode.
Setting /etc/apparmor.d/signal-desktop to enforce mode.
Setting /etc/apparmor.d/slack to enforce mode.
Setting /etc/apparmor.d/slirp4netns to enforce mode.
Setting /etc/apparmor.d/steam to enforce mode.
Setting /etc/apparmor.d/stress-ng to enforce mode.
Setting /etc/apparmor.d/surfshark to enforce mode.
Setting /etc/apparmor.d/systemd-coredump to enforce mode.
Setting /etc/apparmor.d/toybox to enforce mode.
Setting /etc/apparmor.d/transmission to enforce mode.
Setting /etc/apparmor.d/trinity to enforce mode.
Profile for /etc/apparmor.d/tunables not found, skipping
Setting /etc/apparmor.d/tup to enforce mode.
Setting /etc/apparmor.d/tuxedo-control-center to enforce mode.
Setting /etc/apparmor.d/unix-chkpwd to enforce mode.
Setting /etc/apparmor.d/unprivileged_userns to enforce mode.
Setting /etc/apparmor.d/userbindmount to enforce mode.
Setting /etc/apparmor.d/usr.bin.man to enforce mode.
Setting /etc/apparmor.d/uwsgi-core to enforce mode.
Setting /etc/apparmor.d/vdens to enforce mode.
Setting /etc/apparmor.d/virtiofsd to enforce mode.
Setting /etc/apparmor.d/vivaldi-bin to enforce mode.
Setting /etc/apparmor.d/vpnns to enforce mode.
Setting /etc/apparmor.d/wike to enforce mode.
Setting /etc/apparmor.d/wpcom to enforce mode.
Setting /etc/apparmor.d/Xorg to enforce mode.
The audit is run again:
# apparmor_status | grep profiles
105 profiles are loaded.
105 profiles are in enforce mode.
0 profiles are in complain mode.
0 profiles are in prompt mode.
0 profiles are in kill mode.
0 profiles are in unconfined mode.
0 processes have profiles defined.
# apparmor_status | grep processes
0 processes have profiles defined.
0 processes are in enforce mode.
0 processes are in complain mode.
0 processes are in prompt mode.
0 processes are in kill mode.
0 processes are unconfined but have a profile defined.
0 processes are in mixed mode.
All 105 profiles are now in enforce mode.
1.5.2 Ensure fs.protected_symlinks Is Configured #
# sysctl fs.protected_symlinks
fs.protected_symlinks = 1
The runtime value is already correct.
#!/usr/bin/env bash
{
l_parameter_name="fs.protected_symlinks"
l_grep="${l_parameter_name//./(\\.|\\/)}" a_output=() a_files=()
l_systemdsysctl="$(readlink -e /lib/systemd/systemd-sysctl \
|| readlink -e /usr/lib/systemd/systemd-sysctl)"
l_ufw_file="$([ -f /etc/default/ufw ] && \
awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
[ -f "$(readlink -e "$l_ufw_file")" ] && \
a_files+=("$l_ufw_file"); a_files+=("/etc/sysctl.conf")
while IFS= read -r l_fname; do
l_file="$(readlink -e "${l_fname//# /}")"
[ -n "$l_file" ] && ! grep -Psiq -- '(^|\h+)'"$l_file"'\b' \
<<< "${a_files[*]}" && a_files+=("$l_file")
done < <("$l_systemdsysctl" --cat-config | tac | \
grep -Psio -- '^\h*#\h*\/[^#\n\r\h]+\.conf\b')
for l_file in "${a_files[@]}"; do
l_opt="$(grep -Psoi '^\h*'"$l_grep"'\h*=\h*\H+\b' "$l_file" | tail -n 1)"
l_option_value="$(cut -d= -f2 <<< "$l_opt" | xargs)"
[ -n "$l_option_value" ] && \
a_output+=(" - \"$l_parameter_name = $l_option_value\" is set in:" \
" \"$l_file\"")
done
[ "${#a_output[@]}" -gt "0" ] && printf '%s\n' "" "${a_output[@]}" ""
}
- "fs.protected_symlinks = 1" is set in:
"/usr/lib/sysctl.d/50-default.conf"
The parameter is set persistently at boot as well as at runtime.
A Lynis rescan is then performed:
================================================================================
Lynis security scan details:
Scan mode:
Normal [▆] Forensics [ ] Integration [ ] Pentest [ ]
Lynis modules:
- Compliance status [?]
- Security audit [V]
- Vulnerability scan [V]
Details:
Hardening index : 78 [############### ]
Tests performed : 278
Plugins enabled : 2
Software components:
- Firewall [V]
- Intrusion software [X]
- Malware scanner [X]
Files:
- Test and debug information : /var/log/lynis.log
- Report data : /var/log/lynis-report.dat
================================================================================
The hardening index rose from 74 to 78 following this pass.