- Ubuntu 22
- Contents
- 1 Initial Setup
- 2 Services
- 3 Network Configuration
- 1 Disable Unused Network Protocols and Devices
- 2 Network Parameters Host Only
- 3 Network Parameters Host and Router
- 4 Uncommon Network Protocols
- 5 Firewall Configuration
- 4 Logging and Auditing
- 5 Access Authentication and Authorization
- 6 System Maintenance
Commands starting with
#
may require you to start the command withsudo
instead.
Many instructions contain commands both to check and change settings. Many settings will be already set so you do not need to run the commands to fix them. There are some commands where you don't need to check, but the instructions have still been included. It is recommended to run the commands to check unless you know what the command does.
- Ensure mounting of cramfs filesystems is disabled (Automated)
Run the following script to check if the filesystem is installed
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_mname="cramfs" # set module name
# Check how module will be loaded
l_loadable="$(modprobe -n -v "$l_mname")"
if grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
l_output="$l_output\n - module: \"$l_mname\" is not loadable: \"$l_loadable\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is loadable: \"$l_loadable\""
fi
# Check is the module currently loaded
if ! lsmod | grep "$l_mname" > /dev/null 2>&1; then
l_output="$l_output\n - module: \"$l_mname\" is not loaded"
else
l_output2="$l_output2\n - module: \"$l_mname\" is loaded"
fi
# Check if the module is deny listed
if grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
l_output="$l_output\n - module: \"$l_mname\" is deny listed in: \"$(grep -Pl -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*)\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is not deny listed"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to remove the filesystem
#!/usr/bin/env bash
{
l_mname="cramfs" # set module name
if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
echo -e " - setting module: \"$l_mname\" to be not loadable"
echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
fi
if lsmod | grep "$l_mname" > /dev/null 2>&1; then
echo -e " - unloading module \"$l_mname\""
modprobe -r "$l_mname"
fi
if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
echo -e " - deny listing \"$l_mname\""
echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
fi
}
- Ensure mounting of squashfs filesystems is disabled (Automated)
Snap packages use this filesystem
Run the following script to check if the filesystem is installed
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_mname="squashfs" # set module name
# Check how module will be loaded
l_loadable="$(modprobe -n -v "$l_mname")"
if grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
l_output="$l_output\n - module: \"$l_mname\" is not loadable: \"$l_loadable\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is loadable: \"$l_loadable\""
fi
# Check is the module currently loaded
if ! lsmod | grep "$l_mname" > /dev/null 2>&1; then
l_output="$l_output\n - module: \"$l_mname\" is not loaded"
else
l_output2="$l_output2\n - module: \"$l_mname\" is loaded"
fi
# Check if the module is deny listed
if grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
l_output="$l_output\n - module: \"$l_mname\" is deny listed in: \"$(grep -Pl -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*)\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is not deny listed"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to remove the filesystem
#!/usr/bin/env bash
{
l_mname="squashfs" # set module name
if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
echo -e " - setting module: \"$l_mname\" to be not loadable"
echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
fi
if lsmod | grep "$l_mname" > /dev/null 2>&1; then
echo -e " - unloading module \"$l_mname\""
modprobe -r "$l_mname"
fi
if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
echo -e " - deny listing \"$l_mname\""
echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
fi
}
- Ensure mounting of udf filesystems is disabled (Automated)
Microsoft Azure uses this filesystem
Run the following script to check if the filesystem is installed
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_mname="udf" # set module name
# Check how module will be loaded
l_loadable="$(modprobe -n -v "$l_mname")"
if grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
l_output="$l_output\n - module: \"$l_mname\" is not loadable: \"$l_loadable\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is loadable: \"$l_loadable\""
fi
# Check is the module currently loaded
if ! lsmod | grep "$l_mname" > /dev/null 2>&1; then
l_output="$l_output\n - module: \"$l_mname\" is not loaded"
else
l_output2="$l_output2\n - module: \"$l_mname\" is loaded"
fi
# Check if the module is deny listed
if grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
l_output="$l_output\n - module: \"$l_mname\" is deny listed in: \"$(grep -Pl -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*)\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is not deny listed"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to remove the filesystem
#!/usr/bin/env bash
{
l_mname="udf" # set module name
if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
echo -e " - setting module: \"$l_mname\" to be not loadable"
echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
fi
if lsmod | grep "$l_mname" > /dev/null 2>&1; then
echo -e " - unloading module \"$l_mname\""
modprobe -r "$l_mname"
fi
if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
echo -e " - deny listing \"$l_mname\""
echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
fi
- Ensure /tmp is a separate partition (Automated)
Run the following command and verify the output shows that /tmp
is mounted.
# findmnt --kernel /tmp
TARGET SOURCE FSTYPE OPTIONS
/tmp tmpfs tmpfs rw,nosuid,nodev,noexec,inode6
Ensure that systemd will mount the /tmp
partition at boot time.
# systemctl is-enabled tmp.mount
enabled
First ensure that systemd is correctly configured to ensure that /tmp
will be mounted at
boot time.
# systemctl unmask tmp.mount
More Information
- Ensure nodev option set on
/tmp
partition (Automated)
This follows the same steps as 3. and 4.
Verify that the nodev
option is set for the /tmp
mount.
Run the following command to verify that the nodev
mount option is set.
Example:
# findmnt --kernel /tmp | grep nodev
/tmp tmpfs tmpfs rw,nosuid,nodev,noexec,relatime,seclabel
Edit the /etc/fstab
file and add nodev
to the fourth field (mounting options) for the /tmp
partition.
Example:
<device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0
Run the following command to remount /tmp
with the configured options:
# mount -o remount /tmp
- Ensure noexec option set on /tmp partition (Automated)
This follows the same steps as 2. and 4.
Verify that the noexec
option is set for the /tmp
mount.
Run the following command to verify that the noexec
mount option is set.
Example:
# findmnt --kernel /tmp | grep noexec
/tmp tmpfs tmpfs rw,nosuid,nodev,noexec,relatime,seclabel
Edit the /etc/fstab
file and add noexec
to the fourth field (mounting options) for the
/tmp
partition.
Example:
<device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0
Run the following command to remount /tmp
with the configured options:
# mount -o remount /tmp
- Ensure nosuid option set on /tmp partition (Automated)
This follows the same steps as 2. and 3.
Verify that the nosuid
option is set for the /tmp
mount.
Run the following command to verify that the nosuid
mount option is set.
Example:
# findmnt --kernel /tmp | grep nosuid
/tmp tmpfs tmpfs rw,nosuid,nodev,noexec,relatime,seclabel
Edit the /etc/fstab
file and add nosuid
to the fourth field (mounting options) for the
/tmp
partition.
Example:
<device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0
Run the following command to remount /tmp
with the configured options:
# mount -o remount /tmp
- Ensure separate partition exists for /var (Automated)
Run the following command and verify output shows /var is mounted. Example:
# findmnt --kernel /var
TARGET SOURCE FSTYPE OPTIONS
/var /dev/sdb ext4 rw,relatime,seclabel,data=ordered
For new installations, during installation create a custom partition setup and specify a
separate partition for /var
.
For systems that were previously installed, create a new partition and configure
/etc/fstab
as appropriate.
When modifying
/var
it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode.
- Ensure nodev option set on /var partition (Automated)
This follows the same steps as 3.
Verify that the nodev
option is set for the /var
mount.
Run the following command to verify that the nodev
mount option is set.
Example:
# findmnt --kernel /var
/var /dev/sdb ext4 rw,nosuid,nodev,relatime,seclabel
IF output is produced, ensure it includes the
nodev
option
IF the /var
partition exists, edit the /etc/fstab
file and add nodev
to the fourth field
(mounting options) for the /var
partition.
Example:
<device> /var <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var
with the configured options:
# mount -o remount /var
- Ensure nosuid option set on /var partition (Automated)
This follows the same steps as 3.
Verify that the nosuid
option is set for the /var
mount.
Run the following command to verify that the nosuid
mount option is set.
Example:
# findmnt --kernel /var
/var /dev/sdb ext4 rw,nosuid,nodev,relatime,seclabel
IF output is produced, ensure it includes the
nosuid
option
IF the /var
partition exists, edit the /etc/fstab
file and add nosuid
to the fourth field
(mounting options) for the /var
partition.
Example:
<device> /var <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var
with the configured options:
# mount -o remount /var
- Ensure separate partition exists for /var/tmp (Automated)
Run the following command and verify output shows /var/tmp
is mounted.
Example:
# findmnt --kernel /var/tmp
TARGET SOURCE FSTYPE OPTIONS
/var/tmp /dev/sdb ext4 rw,relatime,seclabel,data=ordered
For new installations, during installation create a custom partition setup and specify a
separate partition for /var/tmp
.
For systems that were previously installed, create a new partition and configure
/etc/fstab
as appropriate.
- Ensure noexec option set on /var/tmp partition (Automated)
Verify that the noexec
option is set for the /var/tmp
mount.
Run the following command to verify that the noexec
mount option is set.
Example:
# findmnt --kernel /var/tmp
/var/tmp /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
noexec
option
IF the /var/tmp
partition exists, edit the /etc/fstab
file and add noexec
to the fourth field
(mounting options) for the /var/tmp
partition.
Example:
<device> /var/tmp <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/tmp
with the configured options:
# mount -o remount /var/tmp
- Ensure nosuid option set on /var/tmp partition (Automated)
Verify that the nosuid
option is set for the /var/tmp
mount.
Run the following command to verify that the nosuid
mount option is set.
Example:
# findmnt --kernel /var/tmp
/var/tmp /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
nosuid
option
IF the /var/tmp
partition exists, edit the /etc/fstab
file and add nosuid
to the fourth field
(mounting options) for the /var/tmp
partition.
Example:
<device> /var/tmp <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/tmp
with the configured options:
# mount -o remount /var/tmp
- Ensure nodev option set on /var/tmp partition (Automated)
Verify that the nodev
option is set for the /var/tmp
mount.
Run the following command to verify that the nodev
mount option is set.
Example:
# findmnt --kernel /var/tmp
/var/tmp /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
nodev
option
IF the /var/tmp
partition exists, edit the /etc/fstab
file and add nodev
to the fourth field
(mounting options) for the /var/tmp
partition.
Example:
<device> /var/tmp <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/tmp
with the configured options:
# mount -o remount /var/tmp
- Ensure separate partition exists for /var/log (Automated)
Run the following command and verify output shows /var/log
is mounted.
Example:
# findmnt --kernel /var/log
TARGET SOURCE FSTYPE OPTIONS
/var/log /dev/sdb ext4 rw,relatime,seclabel,data=ordered
For new installations, during installation create a custom partition setup and specify a
separate partition for /var/log
.
For systems that were previously installed, create a new partition and configure
/etc/fstab
as appropriate.
- Ensure nodev option set on /var/log partition (Automated)
Verify that the nodev
option is set for the /var/log
mount.
Run the following command to verify that the nodev
mount option is set.
Example:
# findmnt --kernel /var/log
/var/log /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
nodev
option
IF the /var/log
partition exists, edit the /etc/fstab
file and add nodev
to the fourth field
(mounting options) for the /var/log
partition.
Example:
<device> /var/log <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/log
with the configured options:
# mount -o remount /var/log
- Ensure noexec option set on /var/log partition (Automated)
Verify that the noexec
option is set for the /var/log
mount.
Run the following command to verify that the noexec
mount option is set.
Example:
# findmnt --kernel /var/log
/var/log /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
noexec
option
IF the /var/log
partition exists, edit the /etc/fstab
file and add noexec
to the fourth field
(mounting options) for the /var/log
partition.
Example:
<device> /var/log <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/log
with the configured options:
# mount -o remount /var/log
- Ensure nosuid option set on /var/log partition (Automated)
Verify that the nosuid
option is set for the /var/log
mount.
Run the following command to verify that the nosuid
mount option is set.
Example:
# findmnt --kernel /var/log
/var/log /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
nosuid
option
IF the /var/log
partition exists, edit the /etc/fstab
file and add nosuid
to the fourth field
(mounting options) for the /var/log
partition.
Example:
<device> /var/log <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/log
with the configured options:
# mount -o remount /var/log
- Ensure separate partition exists for /var/log/audit (Automated)
Run the following command and verify output shows /var/log/audit
is mounted.
Example:
# findmnt --kernel /var/log/audit
TARGET SOURCE FSTYPE OPTIONS
/var/log/audit /dev/sdb ext4 rw,relatime,seclabel,data=ordered
For new installations, during installation create a custom partition setup and specify a
separate partition for /var/log
.
For systems that were previously installed, create a new partition and configure
/etc/fstab
as appropriate.
- Ensure noexec option set on /var/log/audit partition (Automated)
Verify that the noexec
option is set for the /var/log/audit
mount.
Run the following command to verify that the noexec
mount option is set.
Example:
# findmnt --kernel /var/log/audit
/var/log/audit /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
noexec
option
IF the /var/log/audit
partition exists, edit the /etc/fstab
file and add noexec
to the fourth field
(mounting options) for the /var/log/audit
partition.
Example:
<device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/log/audit
with the configured options:
# mount -o remount /var/log/audit
- Ensure nodev option set on /var/log/audit partition (Automated)
Verify that the nodev
option is set for the /var/log/audit
mount.
Run the following command to verify that the nodev
mount option is set.
Example:
# findmnt --kernel /var/log/audit
/var/log/audit /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
nodev
option
IF the /var/log/audit
partition exists, edit the /etc/fstab
file and add nodev
to the fourth field
(mounting options) for the /var/log/audit
partition.
Example:
<device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/log/audit
with the configured options:
# mount -o remount /var/log/audit
- Ensure nosuid option set on /var/log/audit partition (Automated)
Verify that the nosuid
option is set for the /var/log/audit
mount.
Run the following command to verify that the nosuid
mount option is set.
Example:
# findmnt --kernel /var/log/audit
/var/log/audit /dev/sdb ext4 rw,nosuid,nodev,noexec,relatime,seclabel
IF output is produced, ensure it includes the
nosuid
option
IF the /var/log/audit
partition exists, edit the /etc/fstab
file and add nosuid
to the fourth field
(mounting options) for the /var/log/audit
partition.
Example:
<device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /var/log/audit
with the configured options:
# mount -o remount /var/log/audit
- Ensure separate partition exists for /home (Automated)
Run the following command and verify output shows /home
is mounted:
# findmnt --kernel /home
TARGET SOURCE FSTYPE OPTIONS
/home /dev/sdb ext4 rw,relatime,seclabel
For new installations, during installation create a custom partition setup and specify a
separate partition for /home
.
For systems that were previously installed, create a new partition and configure
/etc/fstab
as appropriate.
- Ensure nodev option set on /home partition (Automated)
Verify that the nodev
option is set for the /home
mount.
Run the following command to verify that the nodev
mount option is set.
Example:
# findmnt --kernel /home
/home /dev/sdb ext4 rw,nosuid,nodev,relatime,seclabel
IF output is produced, ensure it includes the
nodev
option
IF the /home
partition exists, edit the /etc/fstab
file and add nodev
to the fourth field
(mounting options) for the /home
partition.
Example:
<device> /home <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /home
with the configured options:
# mount -o remount /home
- Ensure nosuid option set on /home partition (Automated)
Verify that the nosuid
option is set for the /home
mount.
Run the following command to verify that the nosuid
mount option is set.
Example:
# findmnt --kernel /home
/home /dev/sdb ext4 rw,nosuid,nodev,relatime,seclabel
IF output is produced, ensure it includes the
nosuid
option
IF the /home
partition exists, edit the /etc/fstab
file and add nosuid
to the fourth field
(mounting options) for the /home
partition.
Example:
<device> /home <fstype> defaults,rw,nosuid,nodev,relatime 0 0
Run the following command to remount /home
with the configured options:
# mount -o remount /home
- Ensure nodev option set on /dev/shm partition (Automated)
Verify that the nodev
option is set for the /dev/shm
mount.
Run the following command to verify that the nodev
mount option is set.
Example:
# findmnt --kernel /dev/shm | grep nodev
Edit the /etc/fstab
file and add nodev to the fourth field (mounting options) for the
/dev/shm
partition.
Run the following command to remount /dev/shm
using the updated options from
/etc/fstab
:
# mount -o remount /dev/shm
- Ensure noexec option set on /dev/shm partition (Automated)
Verify that the noexec
option is set for the /dev/shm
mount.
Run the following command to verify that the noexec
mount option is set.
Example:
# findmnt --kernel /dev/shm | grep noexec
/dev/shm tmpfs tmpfs rw,nosuid,nodev,noexec,relatime,seclabel
Edit the /etc/fstab
file and add noexec
to the fourth field (mounting options) for the
/dev/shm
partition.
Example:
<device> /dev/shm <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0
Run the following command to remount /dev/shm
with the configured options:
# mount -o remount /dev/shm
- Ensure nosuid option set on /dev/shm partition (Automated)
Verify that the nosuid
option is set for the /dev/shm
mount.
Run the following command to verify that the nosuid
mount option is set.
Example:
# findmnt --kernel /dev/shm | grep nosuid
Edit the /etc/fstab
file and add nosuid
to the fourth field (mounting options) for the
/dev/shm
partition.
Run the following command to remount /dev/shm
using the updated options from
/etc/fstab
:
# mount -o remount /dev/shm
As a preference autofs
should not be installed unless other packages depend on it.
Run the following command to verify autofs
is not installed:
# systemctl is-enabled autofs
Failed to get unit file state for autofs.service: No such file or directory
Run the following command to verify autofs
is not enabled if installed:
# systemctl is-enabled autofs
disabled
If there are no other packages that depends on autofs
, remove the package with:
# apt purge autofs
OR if there are dependencies on the autofs
package:
Run the following commands to mask autofs
:
# systemctl stop autofs
# systemctl mask autofs
Run the following script to verify usb-storage
is disabled:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_mname="usb-storage" # set module name
# Check how module will be loaded
l_loadable="$(modprobe -n -v "$l_mname")"
if grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
l_output="$l_output\n - module: \"$l_mname\" is not loadable:
\"$l_loadable\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is loadable:
\"$l_loadable\""
fi
# Check is the module currently loaded
if ! lsmod | grep "$l_mname" > /dev/null 2>&1; then
l_output="$l_output\n - module: \"$l_mname\" is not loaded"
else
l_output2="$l_output2\n - module: \"$l_mname\" is loaded"
fi
# Check if the module is deny listed
if grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
l_output="$l_output\n - module: \"$l_mname\" is deny listed in:
\"$(grep -Pl -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*)\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is not deny listed"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit
failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to disable usb-storage
:
#!/usr/bin/env bash
{
l_mname="usb-storage" # set module name
if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
echo -e " - setting module: \"$l_mname\" to be not loadable"
echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
fi
if lsmod | grep "$l_mname" > /dev/null 2>&1; then
echo -e " - unloading module \"$l_mname\""
modprobe -r "$l_mname"
fi
if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc modprobe.d/*; then
echo -e " - deny listing \"$l_mname\""
echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
fi
}
- Ensure package manager repositories are configured (Manual)
Run the following command and verify package repositories are configured correctly:
# apt-cache policy
Configure your package manager repositories according to site policy.
- Ensure GPG keys are configured (Manual)
Verify GPG keys are configured correctly for your package manager:
# apt-key list
Update your package manager GPG keys in accordance with site policy.
- Ensure AIDE is installed (Automated)
Run the following commands to verify AIDE is installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' aide aide-common
aide install ok installed installed
aide-common install ok installed installed
Install AIDE using the appropriate package manager or manual installation:
# apt install aide aide-common
Configure AIDE as appropriate for your environment. Consult the AIDE documentation for options.
Run the following commands to initialize AIDE:
# aideinit
# mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
- Ensure filesystem integrity is regularly checked (Automated)
Run the following commands to verify a cron job scheduled to run the aide check.
# grep -Prs '^([^#\n\r]+\h+)?(\/usr\/s?bin\/|^\h*)aide(\.wrapper)?\h+(--check|([^#\n\r]+\h+)?\$AIDEARGS)\b' /etc/cron.* /etc/crontab /var/spool/cron/
If cron will be used to schedule and run aide check:
Run the following command:
# crontab -u root -e
Add the following line to the crontab:
0 5 * * * /usr/bin/aide.wrapper --config /etc/aide/aide.conf --check
- Ensure bootloader password is set (Automated)
# grep "^set superusers" /boot/grub/grub.cfg
set superusers="<username>"
# grep "^password" /boot/grub/grub.cfg
password_pbkdf2 <username> <encrypted-password>
Create an encrypted password with grub-mkpasswd-pbkdf2
:
# grub-mkpasswd-pbkdf2
Enter password: <password>
Reenter password: <password>
PBKDF2 hash of your password is <encrypted-password>
Add the following into a custom /etc/grub.d
configuration file:
cat <<EOF
set superusers="<username>"
password_pbkdf2 <username> <encrypted-password>
EOF
Run the following command to update the grub2 configuration:
# update-grub
- Ensure permissions on bootloader config are configured (Automated)
Run the following command and verify Uid
and Gid
are both 0/root
and Access is 0400
or more restrictive.
# stat /boot/grub/grub.cfg
Access: (0400/-r--------) Uid: ( 0/ root) Gid: ( 0/ root)
Run the following commands to set permissions on your grub configuration:
# chown root:root /boot/grub/grub.cfg
# chmod u-wx,go-rwx /boot/grub/grub.cfg
- Ensure authentication required for single user mode (Automated)
Perform the following to determine if a password is set for the root user:
# grep -Eq '^root:\$[0-9]' /etc/shadow || echo "root is locked"
No result should be returned.
Run the following command and follow the prompts to set a password for the root user:
# passwd root
- Ensure address space layout randomization (ASLR) is enabled (Automated)
Run the following script to verify kernel.randomize_va_space is set to 2:
#!/usr/bin/env bash
{
krp="" pafile="" fafile=""
kpname="kernel.randomize_va_space"
kpvalue="2"
searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf"
krp="$(sysctl "$kpname" | awk -F= '{print $2}' | xargs)"
pafile="$(grep -Psl -- "^\h*$kpname\h*=\h*$kpvalue\b\h*(#.*)?$" $searchloc)"
fafile="$(grep -s -- "^\s*$kpname" $searchloc | grep -Pv --"\h*=\h*$kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$krp" = "$kpvalue" ] && [ -n "$pafile" ] && [ -z "$fafile" ]; then
echo -e "\nPASS:\n\"$kpname\" is set to \"$kpvalue\" in the running configuration and in \"$pafile\""
else
echo -e "\nFAIL: "
[ "$krp" != "$kpvalue" ] && echo -e "\"$kpname\" is set to \"$krp\" in the running configuration\n"
[ -n "$fafile" ] && echo -e "\n\"$kpname\" is set incorrectly in \"$fafile\""
[ -z "$pafile" ] && echo -e "\n\"$kpname = $kpvalue\" is not set in a kernel parameter configuration file\n"
fi
}
Set the following parameter in /etc/sysctl.conf
or a /etc/sysctl.d/*
file:
Example:
# printf "kernel.randomize_va_space = 2" >> /etc/sysctl.d/60-kernel_sysctl.conf
Run the following command to set the active kernel parameter:
# sysctl -w kernel.randomize_va_space=2
- Ensure prelink is not installed (Automated)
Verify prelink
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' prelink
prelink unknown ok not-installed not-installed
Run the following command to restore binaries to normal:
# prelink -ua
Uninstall prelink using the appropriate package manager or manual installation:
# apt purge prelink
- Ensure Automatic Error Reporting is not enabled (Automated)
Run the following command to verify that the Apport Error Reporting Service is not enabled:
# dpkg-query -s apport > /dev/null 2>&1 && grep -Psi --'^\h*enabled\h*=\h*[^0]\b' /etc/default/apport
Nothing should be returned.
Run the following command to verify that the apport service is not active:
# systemctl is-active apport.service | grep '^active'
Nothing should be returned.
Edit /etc/default/apport
and add or edit the enabled parameter to equal 0:
enabled=0
Run the following commands to stop and disable the apport service
# systemctl stop apport.service
# systemctl --now disable apport.service
- Ensure core dumps are restricted (Automated)
Run the following commands and verify output matches:
# grep -Es '^(\*|\s).*hard.*core.*(\s+#.*)?$' /etc/security/limits.conf /etc/security/limits.d/*
* hard core 0
# sysctl fs.suid_dumpable
fs.suid_dumpable = 0
# grep "fs.suid_dumpable" /etc/sysctl.conf /etc/sysctl.d/*
fs.suid_dumpable = 0
Run the following command to check if systemd-coredump is installed:
# systemctl is-enabled coredump.service
if enabled
, masked
, or disabled
is returned systemd-coredump is installed
Add the following line to /etc/security/limits.conf
or a /etc/security/limits.d/*
file:
* hard core 0
Set the following parameter in /etc/sysctl.conf
or a /etc/sysctl.d/*
file:
fs.suid_dumpable = 0
Run the following command to set the active kernel parameter:
# sysctl -w fs.suid_dumpable=0
IF systemd-coredump is installed:
edit /etc/systemd/coredump.conf
and add/modify the following lines:
Storage=none
ProcessSizeMax=0
Run the command:
systemctl daemon-reload
- Ensure AppArmor is installed (Automated)
Verify that AppArmor is installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' apparmor
apparmor install ok installed installed
Install AppArmor.
# apt install apparmor
- Ensure AppArmor is enabled in the bootloader configuration (Automated)
Run the following commands to verify that all linux
lines have the apparmor=1
and security=apparmor
parameters set:
# grep "^\s*linux" /boot/grub/grub.cfg | grep -v "apparmor=1"
Nothing should be returned
# grep "^\s*linux" /boot/grub/grub.cfg | grep -v "security=apparmor"
Nothing should be returned
Edit /etc/default/grub
and add the apparmor=1
and security=apparmor
parameters to the GRUB_CMDLINE_LINUX=
line
GRUB_CMDLINE_LINUX="apparmor=1 security=apparmor"
Run the following command to update the grub2
configuration:
# update-grub
- Ensure all AppArmor Profiles are in enforce or complain mode (Automated)
Run the following command and verify that profiles are loaded, and are in either enforce or complain mode:
# apparmor_status | grep profiles
37 profiles are loaded.
35 profiles are in enforce mode.
2 profiles are in complain mode.
4 processes have profiles defined.
Run the following command and verify no processes are unconfined:
# apparmor_status | grep processes
4 processes have profiles defined.
4 processes are in enforce mode.
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
Run the following command to set all profiles to enforce mode
# aa-enforce /etc/apparmor.d/*
OR
Run the following command to set all profiles to complain mode:
# aa-complain /etc/apparmor.d/*
- Ensure all AppArmor Profiles are enforcing (Automated)
Run the following commands and verify that profiles are loaded and are not in complain mode:
# apparmor_status | grep profiles
34 profiles are loaded.
34 profiles are in enforce mode.
0 profiles are in complain mode.
2 processes have profiles defined.
Run the following command and verify that no processes are unconfined:
apparmor_status | grep processes
2 processes have profiles defined.
2 processes are in enforce mode.
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
Run the following command to set all profiles to enforce mode:
# aa-enforce /etc/apparmor.d/*
- Ensure message of the day is configured properly (Automated)
Run the following command and verify no results are returned:
# grep -Eis "(\\\v|\\\r|\\\m|\\\s|$(grep '^ID=' /etc/os-release | cut -d= -f2 | sed -e 's/"//g'))" /etc/motd
Edit the /etc/motd
file with the appropriate contents according to your site policy,
remove any instances of \m
, \r
, \s
, \v
or references to the OS platform
OR if the motd is not used, this file can be removed.
Run the following command to remove the motd file:
# rm /etc/motd
- Ensure local login warning banner is configured properly (Automated)
Run the following command and verify that the contents match site policy:
# cat /etc/issue
Run the following command and verify no results are returned:
# grep -E -i "(\\\v|\\\r|\\\m|\\\s|$(grep '^ID=' /etc/os-release | cut -d= -f2 | sed -e 's/"//g'))" /etc/issue
Edit the /etc/issue
file with the appropriate contents according to your site policy, remove any instances of \m
, \r
, \s
, \v
or references to the OS platform
# echo "Authorized uses only. All activity may be monitored and reported." > /etc/issue
- Ensure remote login warning banner is configured properly (Automated)
Run the following command and verify that the contents match site policy:
# cat /etc/issue.net
Run the following command and verify no results are returned:
# grep -E -i "(\\\v|\\\r|\\\m|\\\s|$(grep '^ID=' /etc/os-release | cut -d= -f2 | sed -e 's/"//g'))" /etc/issue.net
Edit the /etc/issue.net
file with the appropriate contents according to your site policy, remove any instances of \m
, \r
, \s
, \v
or references to the OS platform
# echo "Authorized uses only. All activity may be monitored and reported." > /etc/issue.net
- Ensure permissions on /etc/motd are configured (Automated)
Run the following command and verify: Uid
and Gid
are both 0/root
and Access
is 644
, or the file doesn't exist.
# stat -L /etc/motd
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
OR
stat: cannot stat '/etc/motd': No such file or directory
Run the following commands to set permissions on /etc/motd
:
# chown root:root $(readlink -e /etc/motd)
# chmod u-x,go-wx $(readlink -e /etc/motd)
OR run the following command to remove the /etc/motd
file:
# rm /etc/motd
- Ensure permissions on /etc/issue are configured (Automated)
Run the following command and verify Uid
and Gid
are both 0/root
and Access
is 644
:
# stat -L /etc/issue
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Run the following commands to set permissions on /etc/issue
:
# chown root:root $(readlink -e /etc/issue)
# chmod u-x,go-wx $(readlink -e /etc/issue)
- Ensure permissions on /etc/issue.net are configured (Automated)
Run the following command and verify Uid
and Gid
are both 0/root
and Access
is 644
:
# stat -L /etc/issue.net
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Run the following commands to set permissions on /etc/issue.net
:
# chown root:root $(readlink -e /etc/issue.net)
# chmod u-x,go-wx $(readlink -e /etc/issue.net)
- Ensure GNOME Display Manager is removed (Automated)
Removing the GNOME Display manager will remove the Graphical User Interface (GUI) from the system. You probably don't need to do this unless directly specified that you don't need a GUI
Run the following command and verify gdm3
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' gdm3
gdm3 unknown ok not-installed not-installed
Run the following command to uninstall gdm3
:
# apt purge gdm3
- Ensure GDM login banner is configured (Automated)
Run the following script to verify that the text banner on the login screen is enabled and set:
#!/usr/bin/env bash
{
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n - Package: \"$l_pn\" exists on the system\n - checking configuration"
done
if [ -n "$l_pkgoutput" ]; then
l_output="" l_output2=""
echo -e "$l_pkgoutput"
# Look for existing settings and set variables if they exist
l_gdmfile="$(grep -Prils '^\h*banner-message-enable\b' /etc/dconf/db/*.d)"
if [ -n "$l_gdmfile" ]; then
# Set profile name based on dconf db directory ({PROFILE_NAME}.d)
l_gdmprofile="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_gdmfile")"
# Check if banner message is enabled
if grep -Pisq '^\h*banner-message-enable=true\b' "$l_gdmfile"; then
l_output="$l_output\n - The \"banner-message-enable\" option is enabled in \"$l_gdmfile\""
else
l_output2="$l_output2\n - The \"banner-message-enable\" option is not enabled"
fi
l_lsbt="$(grep -Pios '^\h*banner-message-text=.*$' "$l_gdmfile")"
if [ -n "$l_lsbt" ]; then
l_output="$l_output\n - The \"banner-message-text\" option is set in \"$l_gdmfile\"\n - banner-message-text is set to:\n - \"$l_lsbt\""
else
l_output2="$l_output2\n - The \"banner-message-text\" option is not set"
fi
if grep -Pq "^\h*system-db:$l_gdmprofile" /etc/dconf/profile/"$l_gdmprofile"; then
l_output="$l_output\n - The \"$l_gdmprofile\" profile exists"
else
l_output2="$l_output2\n - The \"$l_gdmprofile\" profile doesn't exist"
fi
if [ -f "/etc/dconf/db/$l_gdmprofile" ]; then
l_output="$l_output\n - The \"$l_gdmprofile\" profile exists in the dconf database"
else
l_output2="$l_output2\n - The \"$l_gdmprofile\" profile doesn't exist in the dconf database"
fi
else
l_output2="$l_output2\n - The \"banner-message-enable\" option isn't configured"
fi
else
echo -e "\n\n - GNOME Desktop Manager isn't installed\n - Recommendation is Not Applicable\n- Audit result:\n *** PASS ***\n"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to verify that the banner message is enabled and set:
#!/usr/bin/env bash
{
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n - Package: \"$l_pn\" exists
on the system\n - checking configuration"
done
if [ -n "$l_pkgoutput" ]; then
l_gdmprofile="gdm" # Set this to desired profile name IaW Local site policy
l_bmessage="'Authorized uses only. All activity may be monitored and reported'" # Set to
desired banner message
if [ ! -f "/etc/dconf/profile/$l_gdmprofile" ]; then
echo "Creating profile \"$l_gdmprofile\""
echo -e "user-db:user\nsystem-db:$l_gdmprofile\nfile�db:/usr/share/$l_gdmprofile/greeter-dconf-defaults" > /etc/dconf/profile/$l_gdmprofile
fi
if [ ! -d "/etc/dconf/db/$l_gdmprofile.d/" ]; then
echo "Creating dconf database directory \"/etc/dconf/db/$l_gdmprofile.d/\""
mkdir /etc/dconf/db/$l_gdmprofile.d/
fi
if ! grep -Piq '^\h*banner-message-enable\h*=\h*true\b' /etc/dconf/db/$l_gdmprofile.d/*; then
echo "creating gdm keyfile for machine-wide settings"
if ! grep -Piq -- '^\h*banner-message-enable\h*=\h*' /etc/dconf/db/$l_gdmprofile.d/*;then
l_kfile="/etc/dconf/db/$l_gdmprofile.d/01-banner-message"
echo -e "\n[org/gnome/login-screen]\nbanner-message-enable=true" >> "$l_kfile"
else
l_kfile="$(grep -Pil -- '^\h*banner-message-enable\h*=\h*' /etc/dconf/db/$l_gdmprofile.d/*)"
! grep -Pq '^\h*\[org\/gnome\/login-screen\]' "$l_kfile" && sed -ri '/^\s*banner�message-enable/ i\[org/gnome/login-screen]' "$l_kfile"
! grep -Pq '^\h*banner-message-enable\h*=\h*true\b' "$l_kfile" && sed -ri 's/^\s*(banner-message-enable\s*=\s*)(\S+)(\s*.*$)/\1true \3//' "$l_kfile"
# sed -ri '/^\s*\[org\/gnome\/login-screen\]/ a\\nbanner-message-enable=true' "$l_kfile"
fi
fi
if ! grep -Piq "^\h*banner-message-text=[\'\"]+\S+" "$l_kfile"; then
sed -ri "/^\s*banner-message-enable/ a\banner-message-text=$l_bmessage" "$l_kfile"
fi
dconf update
else
echo -e "\n\n - GNOME Desktop Manager isn't installed\n - Recommendation is Not Applicable\n - No remediation required\n"
fi
}
- There is no character limit for the banner message. gnome-shell autodetects longer stretches of text and enters two column mode.
- The banner message cannot be read from an external file.
OR
Run the following command to remove the gdm3 package:
# apt purge gdm3
- Ensure GDM disable-user-list option is enabled (Automated)
Run the following script and to verify that the disable-user-list
option is enabled or GNOME isn't installed:
#!/usr/bin/env bash
{
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
done
if [ -n "$l_pkgoutput" ]; then
output="" output2=""
l_gdmfile="$(grep -Pril '^\h*disable-user-list\h*=\h*true\b' /etc/dconf/db)"
if [ -n "$l_gdmfile" ]; then
output="$output\n - The \"disable-user-list\" option is enabled in \"$l_gdmfile\""
l_gdmprofile="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_gdmfile")"
if grep -Pq "^\h*system-db:$l_gdmprofile" /etc/dconf/profile/"$l_gdmprofile"; then
output="$output\n - The \"$l_gdmprofile\" exists"
else
output2="$output2\n - The \"$l_gdmprofile\" doesn't exist"
fi
if [ -f "/etc/dconf/db/$l_gdmprofile" ]; then
output="$output\n - The \"$l_gdmprofile\" profile exists in the dconf database"
else
output2="$output2\n - The \"$l_gdmprofile\" profile doesn't exist in the dconf database"
fi
else
output2="$output2\n - The \"disable-user-list\" option is not enabled"
fi
if [ -z "$output2" ]; then
echo -e "$l_pkgoutput\n- Audit result:\n *** PASS: ***\n$output\n"
else
echo -e "$l_pkgoutput\n- Audit Result:\n *** FAIL: ***\n$output2\n"
[ -n "$output" ] && echo -e "$output\n"
fi
else
echo -e "\n\n - GNOME Desktop Manager isn't installed\n -Recommendation is Not Applicable\n- Audit result:\n *** PASS ***\n"
fi
}
Run the following script to enable the disable-user-list
option:
the
l_gdm_profile
variable in the script can be changed if a different profile name is desired in accordance with local site policy.
#!/usr/bin/env bash
{
l_gdmprofile="gdm"
if [ ! -f "/etc/dconf/profile/$l_gdmprofile" ]; then
echo "Creating profile \"$l_gdmprofile\""
echo -e "user-db:user\nsystem-db:$l_gdmprofile\nfile�db:/usr/share/$l_gdmprofile/greeter-dconf-defaults" > /etc/dconf/profile/$l_gdmprofile
fi
if [ ! -d "/etc/dconf/db/$l_gdmprofile.d/" ]; then
echo "Creating dconf database directory \"/etc/dconf/db/$l_gdmprofile.d/\""
mkdir /etc/dconf/db/$l_gdmprofile.d/
fi
if ! grep -Piq '^\h*disable-user-list\h*=\h*true\b' /etc/dconf/db/$l_gdmprofile.d/*; then
echo "creating gdm keyfile for machine-wide settings"
if ! grep -Piq -- '^\h*\[org\/gnome\/login-screen\]' /etc/dconf/db/$l_gdmprofile.d/*; then
echo -e "\n[org/gnome/login-screen]\n# Do not show the user list\ndisable-user-list=true" >> /etc/dconf/db/$l_gdmprofile.d/00-login�screen
else
sed -ri '/^\s*\[org\/gnome\/login-screen\]/ a\# Do not show the user list\ndisable-user-list=true' $(grep -Pil -- '^\h*\[org\/gnome\/login�screen\]' /etc/dconf/db/$l_gdmprofile.d/*)
fi
fi
dconf update
}
- When the user profile is created or changed, the user will need to log out and log in again before the changes will be applied.
OR
Run the following command to remove the gdm3 package:
# apt purge gdm3
- Ensure GDM screen locks when the user is idle (Automated)
Run the following script to verify that the screen locks when the user is idle:
#!/usr/bin/env bash
{
# Check if GNMOE Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
l_output="" l_output2=""
l_idmv="900" # Set for max value for idle-delay in seconds
l_ldmv="5" # Set for max value for lock-delay in seconds
# Look for idle-delay to determine profile in use, needed for remaining tests
l_kfile="$(grep -Psril '^\h*idle-delay\h*=\h*uint32\h+\d+\b' /etc/dconf/db/*/)" # Determine file containing idle-delay key
if [ -n "$l_kfile" ]; then
# set profile name (This is the name of a dconf database)
l_profile="$(awk -F'/' '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")" #Set the key profile name
l_pdbdir="/etc/dconf/db/$l_profile.d" # Set the key file dconf db directory
# Confirm that idle-delay exists, includes unit32, and value is between 1 and max value for idle-delay
l_idv="$(awk -F 'uint32' '/idle-delay/{print $2}' "$l_kfile" | xargs)"
if [ -n "$l_idv" ]; then
[ "$l_idv" -gt "0" -a "$l_idv" -le "$l_idmv" ] && l_output="$l_output\n - The \"idle-delay\" option is set to \"$l_idv\" seconds in \"$l_kfile\""
[ "$l_idv" = "0" ] && l_output2="$l_output2\n - The \"idle�delay\" option is set to \"$l_idv\" (disabled) in \"$l_kfile\""
[ "$l_idv" -gt "$l_idmv" ] && l_output2="$l_output2\n - The \"idle-delay\" option is set to \"$l_idv\" seconds (greater than $l_idmv) in \"$l_kfile\""
else
l_output2="$l_output2\n - The \"idle-delay\" option is not set in \"$l_kfile\""
fi
# Confirm that lock-delay exists, includes unit32, and value is between 0 and max value for lock-delay
l_ldv="$(awk -F 'uint32' '/lock-delay/{print $2}' "$l_kfile" | xargs)"
if [ -n "$l_ldv" ]; then
[ "$l_ldv" -ge "0" -a "$l_ldv" -le "$l_ldmv" ] && l_output="$l_output\n - The \"lock-delay\" option is set to \"$l_ldv\" seconds in \"$l_kfile\""
[ "$l_ldv" -gt "$l_ldmv" ] && l_output2="$l_output2\n - The \"lock-delay\" option is set to \"$l_ldv\" seconds (greater than $l_ldmv) in \"$l_kfile\""
else
l_output2="$l_output2\n - The \"lock-delay\" option is not set in \"$l_kfile\""
fi
# Confirm that dconf profile exists
if grep -Psq "^\h*system-db:$l_profile" /etc/dconf/profile/*; then
l_output="$l_output\n - The \"$l_profile\" profile exists"
else
l_output2="$l_output2\n - The \"$l_profile\" doesn't exist"
fi
# Confirm that dconf profile database file exists
if [ -f "/etc/dconf/db/$l_profile" ]; then
l_output="$l_output\n - The \"$l_profile\" profile exists in the dconf database"
else
l_output2="$l_output2\n - The \"$l_profile\" profile doesn't exist in the dconf database"
fi
else
l_output2="$l_output2\n - The \"idle-delay\" option doesn't exist, remaining tests skipped"
fi
else
l_output="$l_output\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
# Report results. If no failures output in l_output2, we pass
[ -n "$l_pkgoutput" ] && echo -e "\n$l_pkgoutput"
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
idle-delay=uint32
Should be 900 seconds (15 minutes) or less, not 0 (disabled) and follow local site policylock-delay=uint32
should be 5 seconds or less and follow local site policy
Create or edit a file in the /etc/dconf/profile/
and verify it includes the following:
user-db:user
system-db:{NAME_OF_DCONF_DATABASE}
local is the name of a dconf database used in the examples.
Example:
# echo -e '\nuser-db:user\nsystem-db:local' >> /etc/dconf/profile/user
Create the directory /etc/dconf/db/{NAME_OF_DCONF_DATABASE}.d/
if it doesn't already exist:
Example:
# mkdir /etc/dconf/db/local.d
Create the key file /etc/dconf/db/{NAME_OF_DCONF_DATABASE}.d/{FILE_NAME}
to provide information for the {NAME_OF_DCONF_DATABASE}
database:
Example script:
#!/usr/bin/env bash
{
l_key_file="/etc/dconf/db/local.d/00-screensaver"
l_idmv="900" # Set max value for idle-delay in seconds (between 1 and 900)
l_ldmv="5" # Set max value for lock-delay in seconds (between 0 and 5)
{
echo '# Specify the dconf path'
echo '[org/gnome/desktop/session]'
echo ''
echo '# Number of seconds of inactivity before the screen goes blank'
echo '# Set to 0 seconds if you want to deactivate the screensaver.'
echo "idle-delay=uint32 $l_idmv"
echo ''
echo '# Specify the dconf path'
echo '[org/gnome/desktop/screensaver]'
echo ''
echo '# Number of seconds after the screen is blank before locking the screen'
echo "lock-delay=uint32 $l_ldmv"
} > "$l_key_file"
}
You must include the uint32 along with the integer key values as shown.
Run the following command to update the system databases:
# dconf update
Users must log out and back in again before the system-wide settings take effect.
- Ensure GDM screen locks cannot be overridden (Automated)
Run the following script to verify that the screen lock can not be overridden:
#!/usr/bin/env bash
{
# Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
l_output="" l_output2=""
# Look for idle-delay to determine profile in use, needed for remaining tests
l_kfd="/etc/dconf/db/$(grep -Psril '^\h*idle�delay\h*=\h*uint32\h+\d+\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF�1),a,".");print a[1]}').d" #set directory of key file to be locked
l_kfd2="/etc/dconf/db/$(grep -Psril '^\h*lock�delay\h*=\h*uint32\h+\d+\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF�1),a,".");print a[1]}').d" #set directory of key file to be locked
if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Prilq '\/org\/gnome\/desktop\/session\/idle-delay\b' "$l_kfd"; then
l_output="$l_output\n - \"idle-delay\" is locked in \"$(grep -Pril '\/org\/gnome\/desktop\/session\/idle-delay\b' "$l_kfd")\""
else
l_output2="$l_output2\n - \"idle-delay\" is not locked"
fi
else
l_output2="$l_output2\n - \"idle-delay\" is not set so it can not be locked"
fi
if [ -d "$l_kfd2" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Prilq '\/org\/gnome\/desktop\/screensaver\/lock-delay\b' "$l_kfd2"; then
l_output="$l_output\n - \"lock-delay\" is locked in \"$(grep -Pril '\/org\/gnome\/desktop\/screensaver\/lock-delay\b' "$l_kfd2")\""
else
l_output2="$l_output2\n - \"lock-delay\" is not locked"
fi
else
l_output2="$l_output2\n - \"lock-delay\" is not set so it can not be locked"
fi
else
l_output="$l_output\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
# Report results. If no failures output in l_output2, we pass[ -n "$l_pkgoutput" ] && echo -e "\n$l_pkgoutput"
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to ensure screen locks can not be overridden:
#!/usr/bin/env bash
{
# Check if GNMOE Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="y" && echo -e "\n -Package: \"$l_pn\" exists on the system\n - remediating configuration if needed"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
# Look for idle-delay to determine profile in use, needed for remaining tests
l_kfd="/etc/dconf/db/$(grep -Psril '^\h*idle�delay\h*=\h*uint32\h+\d+\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF�1),a,".");print a[1]}').d" #set directory of key file to be locked
# Look for lock-delay to determine profile in use, needed for remaining tests
l_kfd2="/etc/dconf/db/$(grep -Psril '^\h*lock�delay\h*=\h*uint32\h+\d+\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF�1),a,".");print a[1]}').d" #set directory of key file to be locked
if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Prilq '^\h*\/org\/gnome\/desktop\/session\/idle-delay\b' "$l_kfd"; then
echo " - \"idle-delay\" is locked in \"$(grep -Pril '^\h*\/org\/gnome\/desktop\/session\/idle-delay\b' "$l_kfd")\""
else
echo "creating entry to lock \"idle-delay\""
[ ! -d "$l_kfd"/locks ] && echo "creating directory $l_kfd/locks" && mkdir "$l_kfd"/locks
{
echo -e '\n# Lock desktop screensaver idle-delay setting'
echo '/org/gnome/desktop/session/idle-delay'
} >> "$l_kfd"/locks/00-screensaver
fi
else
echo -e " - \"idle-delay\" is not set so it can not be locked\n -Please follow Recommendation \"Ensure GDM screen locks when the user is idle\" and follow this Recommendation again"
fi
if [ -d "$l_kfd2" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Prilq '^\h*\/org\/gnome\/desktop\/screensaver\/lock�delay\b' "$l_kfd2"; then
echo " - \"lock-delay\" is locked in \"$(grep -Pril '^\h*\/org\/gnome\/desktop\/screensaver\/lock-delay\b' "$l_kfd2")\""
else
echo "creating entry to lock \"lock-delay\""
[ ! -d "$l_kfd2"/locks ] && echo "creating directory $l_kfd2/locks" && mkdir "$l_kfd2"/locks
{
ho -e '\n# Lock desktop screensaver lock-delay setting'
echo '/org/gnome/desktop/screensaver/lock-delay'
} >> "$l_kfd2"/locks/00-screensaver
fi
else
echo -e " - \"lock-delay\" is not set so it can not be locked\n -Please follow Recommendation \"Ensure GDM screen locks when the user is idle\" and follow this Recommendation again"
fi
else
echo -e " - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
}
Run the following command to update the system databases:
# dconf update
Users must log out and back in again before the system-wide settings take effect.
- Ensure GDM automatic mounting of removable media is disabled (Automated)
Run the following script to verify automatic mounting is disabled:
#!/usr/bin/env bash
{
l_pkgoutput="" l_output="" l_output2=""
# Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
echo -e "$l_pkgoutput"
# Look for existing settings and set variables if they exist
l_kfile="$(grep -Prils -- '^\h*automount\b' /etc/dconf/db/*.d)"
l_kfile2="$(grep -Prils -- '^\h*automount-open\b' /etc/dconf/db/*.d)"
# Set profile name based on dconf db directory ({PROFILE_NAME}.d)
if [ -f "$l_kfile" ]; then
l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")"
elif [ -f "$l_kfile2" ]; then
l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile2")"
fi
# If the profile name exist, continue checks
if [ -n "$l_gpname" ]; then
l_gpdir="/etc/dconf/db/$l_gpname.d"
# Check if profile file exists
if grep -Pq -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*; then
l_output="$l_output\n - dconf database profile file \"$(grep -Pl -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*)\" exists"
else
l_output2="$l_output2\n - dconf database profile isn't set"
fi
# Check if the dconf database file exists
if [ -f "/etc/dconf/db/$l_gpname" ]; then
l_output="$l_output\n - The dconf database \"$l_gpname\" exists"
else
l_output2="$l_output2\n - The dconf database \"$l_gpname\" doesn't exist"
fi
# check if the dconf database directory exists
if [ -d "$l_gpdir" ]; then
l_output="$l_output\n - The dconf directory \"$l_gpdir\" exitst"
else
l_output2="$l_output2\n - The dconf directory \"$l_gpdir\" doesn't exist"
fi
# check automount setting
if grep -Pqrs -- '^\h*automount\h*=\h*false\b' "$l_kfile"; then
l_output="$l_output\n - \"automount\" is set to false in: \"$l_kfile\""
else
l_output2="$l_output2\n - \"automount\" is not set correctly"
fi
# check automount-open setting
if grep -Pqs -- '^\h*automount-open\h*=\h*false\b' "$l_kfile2"; then
l_output="$l_output\n - \"automount-open\" is set to false in: \"$l_kfile2\""
else
l_output2="$l_output2\n - \"automount-open\" is not set correctly"
fi
else
# Setings don't exist. Nothing further to check
l_output2="$l_output2\n - neither \"automount\" or \"automount�open\" is set"
fi
else
l_output="$l_output\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to disable automatic mounting of media for all GNOME users:
#!/usr/bin/env bash
{
l_pkgoutput="" l_output="" l_output2=""
l_gpbame="local" # Set to desired dconf profile name (defaule is local)
# Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
done
echo -e "$l_packageout"
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
echo -e "$l_pkgoutput"
# Look for existing settings and set variables if they exist
l_kfile="$(grep -Prils -- '^\h*automount\b' /etc/dconf/db/*.d)"
l_kfile2="$(grep -Prils -- '^\h*automount-open\b' /etc/dconf/db/*.d)"
# Set profile name based on dconf db directory ({PROFILE_NAME}.d)
if [ -f "$l_kfile" ]; then
l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")"
echo " - updating dconf profile name to \"$l_gpname\""
elif [ -f "$l_kfile2" ]; then
l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile2")"
echo " - updating dconf profile name to \"$l_gpname\""
fi
# check for consistency (Clean up configuration if needed)
if [ -f "$l_kfile" ] && [ "$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")" != "$l_gpname" ]; then
sed -ri "/^\s*automount\s*=/s/^/# /" "$l_kfile"
l_kfile="/etc/dconf/db/$l_gpname.d/00-media-automount"
fi
if [ -f "$l_kfile2" ] && [ "$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile2")" != "$l_gpname" ]; then
sed -ri "/^\s*automount-open\s*=/s/^/# /" "$l_kfile2"
fi
[ -n "$l_kfile" ] && l_kfile="/etc/dconf/db/$l_gpname.d/00-media�automount"
# Check if profile file exists
if grep -Pq -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*; then
echo -e "\n - dconf database profile exists in: \"$(grep -Pl --"^\h*system-db:$l_gpname\b" /etc/dconf/profile/*)\""
else
[ ! -f "/etc/dconf/profile/user" ] && l_gpfile="/etc/dconf/profile/user" || l_gpfile="/etc/dconf/profile/user2"
echo -e " - creating dconf database profile"
{
echo -e "\nuser-db:user"
echo "system-db:$l_gpname"
} >> "$l_gpfile"
fi
# create dconf directory if it doesn't exists
l_gpdir="/etc/dconf/db/$l_gpname.d"
if [ -d "$l_gpdir" ]; then
echo " - The dconf database directory \"$l_gpdir\" exists"
else
echo " - creating dconf database directory \"$l_gpdir\""
mkdir "$l_gpdir"
fi
# check automount-open setting
if grep -Pqs -- '^\h*automount-open\h*=\h*false\b' "$l_kfile"; then
echo " - \"automount-open\" is set to false in: \"$l_kfile\""
else
echo " - creating \"automount-open\" entry in \"$l_kfile\""
! grep -Psq -- '\^\h*\[org\/gnome\/desktop\/media-handling\]\b' "$l_kfile" && echo '[org/gnome/desktop/media-handling]' >> "$l_kfile"
sed -ri '/^\s*\[org\/gnome\/desktop\/media-handling\]/a \\nautomount-open=false'
fi
# check automount setting
if grep -Pqs -- '^\h*automount\h*=\h*false\b' "$l_kfile"; then
echo " - \"automount\" is set to false in: \"$l_kfile\""
else
echo " - creating \"automount\" entry in \"$l_kfile\""
! grep -Psq -- '\^\h*\[org\/gnome\/desktop\/media-handling\]\b' "$l_kfile" && echo '[org/gnome/desktop/media-handling]' >> "$l_kfile"
sed -ri '/^\s*\[org\/gnome\/desktop\/media-handling\]/a \\nautomount=false'
fi
else
echo -e "\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
# update dconf database
dconf update
}
- Ensure GDM disabling automatic mounting of removable media is not overridden (Automated)
Run the following script to verify disable automatic mounting is locked:
#!/usr/bin/env bash
{
# Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
l_output="" l_output2=""
# Look for idle-delay to determine profile in use, needed for remaining tests
l_kfd="/etc/dconf/db/$(grep -Psril '^\h*automount\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
l_kfd2="/etc/dconf/db/$(grep -Psril '^\h*automount-open\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Piq '^\h*\/org/gnome\/desktop\/media-handling\/automount\b' "$l_kfd"; then
l_output="$l_output\n - \"automount\" is locked in \"$(grep -Pil '^\h*\/org/gnome\/desktop\/media-handling\/automount\b' "$l_kfd")\""
else
l_output2="$l_output2\n - \"automount\" is not locked"
fi
else
l_output2="$l_output2\n - \"automount\" is not set so it can not be locked"
fi
if [ -d "$l_kfd2" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Piq '^\h*\/org/gnome\/desktop\/media-handling\/automount�open\b' "$l_kfd2"; then
l_output="$l_output\n - \"lautomount-open\" is locked in \"$(grep -Pril '^\h*\/org/gnome\/desktop\/media-handling\/automount-open\b' "$l_kfd2")\""
else
l_output2="$l_output2\n - \"automount-open\" is not locked"
fi
else
l_output2="$l_output2\n - \"automount-open\" is not set so it can not be locked"
fi
else
l_output="$l_output\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
# Report results. If no failures output in l_output2, we pass[ -n "$l_pkgoutput" ] && echo -e "\n$l_pkgoutput"
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to lock disable automatic mounting of media for all GNOME users:
#!/usr/bin/env bash
{
# Check if GNMOE Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space seporated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="y" && echo -e "\n -Package: \"$l_pn\" exists on the system\n - remediating configuration if needed"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
# Look for automount to determine profile in use, needed for remaining tests
l_kfd="/etc/dconf/db/$(grep -Psril '^\h*automount\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
# Look for automount-open to determine profile in use, needed for remaining tests
l_kfd2="/etc/dconf/db/$(grep -Psril '^\h*automount-open\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Priq '^\h*\/org/gnome\/desktop\/media�handling\/automount\b' "$l_kfd"; then
echo " - \"automount\" is locked in \"$(grep -Pril '^\h*\/org/gnome\/desktop\/media-handling\/automount\b' "$l_kfd")\""
else
echo " - creating entry to lock \"automount\""
[ ! -d "$l_kfd"/locks ] && echo "creating directory $l_kfd/locks" && mkdir "$l_kfd"/locks
{
echo -e '\n# Lock desktop media-handling automount setting'
echo '/org/gnome/desktop/media-handling/automount'
} >> "$l_kfd"/locks/00-media-automount
fi
else
echo -e " - \"automount\" is not set so it can not be locked\n -Please follow Recommendation \"Ensure GDM automatic mounting of removable media is disabled\" and follow this Recommendation again"
fi
if [ -d "$l_kfd2" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Priq '^\h*\/org/gnome\/desktop\/media-handling\/automount�open\b' "$l_kfd2"; then
echo " - \"automount-open\" is locked in \"$(grep -Pril '^\h*\/org/gnome\/desktop\/media-handling\/automount-open\b' "$l_kfd2")\""
else
echo " - creating entry to lock \"automount-open\""
[ ! -d "$l_kfd2"/locks ] && echo "creating directory $l_kfd2/locks" && mkdir "$l_kfd2"/locks
{
echo -e '\n# Lock desktop media-handling automount-open setting'
echo '/org/gnome/desktop/media-handling/automount-open'
} >> "$l_kfd2"/locks/00-media-automount
fi
else
echo -e " - \"automount-open\" is not set so it can not be locked\n - Please follow Recommendation \"Ensure GDM automatic mounting of removable media is disabled\" and follow this Recommendation again"
fi
# update dconf database
dconf update
else
echo -e " - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
}
- Ensure GDM autorun-never is enabled (Automated)
Run the following script to verify that autorun-never
is set to true
for GDM:
#!/usr/bin/env bash
{
l_pkgoutput="" l_output="" l_output2=""
# Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space separated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
echo -e "$l_pkgoutput"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
echo -e "$l_pkgoutput"
# Look for existing settings and set variables if they exist
l_kfile="$(grep -Prils -- '^\h*autorun-never\b' /etc/dconf/db/*.d)"
# Set profile name based on dconf db directory ({PROFILE_NAME}.d)
if [ -f "$l_kfile" ]; then
l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")"
fi
# If the profile name exist, continue checks
if [ -n "$l_gpname" ]; then
l_gpdir="/etc/dconf/db/$l_gpname.d"
# Check if profile file exists
if grep -Pq -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*; then
l_output="$l_output\n - dconf database profile file \"$(grep -Pl -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*)\" exists"
else
l_output2="$l_output2\n - dconf database profile isn't set"
fi
# Check if the dconf database file exists
if [ -f "/etc/dconf/db/$l_gpname" ]; then
l_output="$l_output\n - The dconf database \"$l_gpname\" exists"
else
l_output2="$l_output2\n - The dconf database \"$l_gpname\" doesn't exist"
fi
# check if the dconf database directory exists
if [ -d "$l_gpdir" ]; then
l_output="$l_output\n - The dconf directory \"$l_gpdir\" exitst"
else
l_output2="$l_output2\n - The dconf directory \"$l_gpdir\" doesn't exist"
fi
# check autorun-never setting
if grep -Pqrs -- '^\h*autorun-never\h*=\h*true\b' "$l_kfile"; then
l_output="$l_output\n - \"autorun-never\" is set to true in: \"$l_kfile\""
else
l_output2="$l_output2\n - \"autorun-never\" is not set correctly"
fi
else
# Settings don't exist. Nothing further to check
l_output2="$l_output2\n - \"autorun-never\" is not set"
fi
else
l_output="$l_output\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set autorun-never to true for GDM users:
#!/usr/bin/env bash
{
l_pkgoutput="" l_output="" l_output2=""
l_gpname="local" # Set to desired dconf profile name (default is local)
# Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space separated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
done
echo -e "$l_pkgoutput"
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
echo -e "$l_pkgoutput"
# Look for existing settings and set variables if they exist
l_kfile="$(grep -Prils -- '^\h*autorun-never\b' /etc/dconf/db/*.d)"
# Set profile name based on dconf db directory ({PROFILE_NAME}.d)
if [ -f "$l_kfile" ]; then
l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")"
echo " - updating dconf profile name to \"$l_gpname\""
fi
[ ! -f "$l_kfile" ] && l_kfile="/etc/dconf/db/$l_gpname.d/00-media�autorun"
# Check if profile file exists
if grep -Pq -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*; then
echo -e "\n - dconf database profile exists in: \"$(grep -Pl --"^\h*system-db:$l_gpname\b" /etc/dconf/profile/*)\""
else
[ ! -f "/etc/dconf/profile/user" ] &&l_gpfile="/etc/dconf/profile/user" || l_gpfile="/etc/dconf/profile/user2"
echo -e " - creating dconf database profile"
{
echo -e "\nuser-db:user"
echo "system-db:$l_gpname"
} >> "$l_gpfile"
fi
# create dconf directory if it doesn't exists
l_gpdir="/etc/dconf/db/$l_gpname.d"
if [ -d "$l_gpdir" ]; then
echo " - The dconf database directory \"$l_gpdir\" exists"
else
echo " - creating dconf database directory \"$l_gpdir\""
mkdir "$l_gpdir"
fi
# check autorun-never setting
if grep -Pqs -- '^\h*autorun-never\h*=\h*true\b' "$l_kfile"; then
echo " - \"autorun-never\" is set to true in: \"$l_kfile\""
else
echo " - creating or updating \"autorun-never\" entry in \"$l_kfile\""
if grep -Psq -- '^\h*autorun-never' "$l_kfile"; then
sed -ri 's/(^\s*autorun-never\s*=\s*)(\S+)(\s*.*)$/\1true \3/' "$l_kfile"
else
! grep -Psq -- '\^\h*\[org\/gnome\/desktop\/media-handling\]\b' "$l_kfile" && echo '[org/gnome/desktop/media-handling]' >> "$l_kfile"
sed -ri '/^\s*\[org\/gnome\/desktop\/media-handling\]/a \\nautorun-never=true' "$l_kfile"
fi
fi
else
echo -e "\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
# update dconf database
dconf update
}
- Ensure GDM autorun-never is not overridden (Automated)
Run the following script to verify that autorun-never=true
cannot be overridden:
#!/usr/bin/env bash
{
# Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space separated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n -Package: \"$l_pn\" exists on the system\n - checking configuration"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
l_output="" l_output2=""
# Look for idle-delay to determine profile in use, needed for remaining tests
l_kfd="/etc/dconf/db/$(grep -Psril '^\h*autorun-never\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Piq '^\h*\/org/gnome\/desktop\/media-handling\/autorun�never\b' "$l_kfd"; then
l_output="$l_output\n - \"autorun-never\" is locked in \"$(grep -Pil '^\h*\/org/gnome\/desktop\/media-handling\/autorun-never\b' "$l_kfd")\""
else
l_output2="$l_output2\n - \"autorun-never\" is not locked"
fi
else
l_output2="$l_output2\n - \"autorun-never\" is not set so it can not be locked"
fi
else
l_output="$l_output\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
# Report results. If no failures output in l_output2, we pass[ -n "$l_pkgoutput" ] && echo -e "\n$l_pkgoutput"
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to ensure that autorun-never=true
cannot be overridden:
#!/usr/bin/env bash
{
# Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
# determine system's package manager
l_pkgoutput=""
if command -v dpkg-query > /dev/null 2>&1; then
l_pq="dpkg-query -W"
elif command -v rpm > /dev/null 2>&1; then
l_pq="rpm -q"
fi
# Check if GDM is installed
l_pcl="gdm gdm3" # Space separated list of packages to check
for l_pn in $l_pcl; do
$l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="y" && echo -e "\n -Package: \"$l_pn\" exists on the system\n - remediating configuration if needed"
done
# Check configuration (If applicable)
if [ -n "$l_pkgoutput" ]; then
# Look for autorun to determine profile in use, needed for remaining tests
l_kfd="/etc/dconf/db/$(grep -Psril '^\h*autorun-never\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
if grep -Priq '^\h*\/org/gnome\/desktop\/media-handling\/autorun�never\b' "$l_kfd"; then
echo " - \"autorun-never\" is locked in \"$(grep -Pril '^\h*\/org/gnome\/desktop\/media-handling\/autorun-never\b' "$l_kfd")\""
else
echo " - creating entry to lock \"autorun-never\""
[ ! -d "$l_kfd"/locks ] && echo "creating directory $l_kfd/locks" && mkdir "$l_kfd"/locks
{
echo -e '\n# Lock desktop media-handling autorun-never setting'
echo '/org/gnome/desktop/media-handling/autorun-never'
} >> "$l_kfd"/locks/00-media-autorun
fi
else
echo -e " - \"autorun-never\" is not set so it can not be locked\n -Please follow Recommendation \"Ensure GDM autorun-never is enabled\" and follow this Recommendation again"
fi
# update dconf database
dconf update
else
echo -e " - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
fi
}
- Ensure XDCMP is not enabled (Automated)
Run the following command and verify the output:
# grep -Eis '^\s*Enable\s*=\s*true' /etc/gdm3/custom.conf
Nothing should be returned
Edit the file /etc/gdm3/custom.conf
and remove the line:
Enable=true
Run the following commands:
# apt update
# apt upgrade
Only one time synchronization method should be in use on the system. Configuring multiple time synchronization methods could lead to unexpected or unreliable results
On physical systems, and virtual systems where host based time synchronization is not available.
One of the three time synchronization daemons should be available; chrony
, systemd-timesyncd
, or ntp
Run the following script to verify that a single time synchronization daemon is available on the system:
#!/usr/bin/env bash
{
output="" l_tsd="" l_sdtd="" chrony="" l_ntp=""
dpkg-query -W chrony > /dev/null 2>&1 && l_chrony="y"
dpkg-query -W ntp > /dev/null 2>&1 && l_ntp="y" || l_ntp=""
systemctl list-units --all --type=service | grep -q 'systemd�timesyncd.service' && systemctl is-enabled systemd-timesyncd.service | grep -q 'enabled' && l_sdtd="y"# ! systemctl is-enabled systemd-timesyncd.service | grep -q 'enabled' && l_nsdtd="y" || l_nsdtd=""
if [[ "$l_chrony" = "y" && "$l_ntp" != "y" && "$l_sdtd" != "y" ]]; then
l_tsd="chrony"
output="$output\n- chrony is in use on the system"
elif [[ "$l_chrony" != "y" && "$l_ntp" = "y" && "$l_sdtd" != "y" ]]; then
l_tsd="ntp"
output="$output\n- ntp is in use on the system"
elif [[ "$l_chrony" != "y" && "$l_ntp" != "y" ]]; then
if systemctl list-units --all --type=service | grep -q 'systemd�timesyncd.service' && systemctl is-enabled systemd-timesyncd.service | grep -Eq '(enabled|disabled|masked)'; then
l_tsd="sdtd"
output="$output\n- systemd-timesyncd is in use on the system"
fi
else
[[ "$l_chrony" = "y" && "$l_ntp" = "y" ]] && output="$output\n- both chrony and ntp are in use on the system"
[[ "$l_chrony" = "y" && "$l_sdtd" = "y" ]] && output="$output\n- both chrony and systemd-timesyncd are in use on the system"
[[ "$l_ntp" = "y" && "$l_sdtd" = "y" ]] && output="$output\n- both ntp and systemd-timesyncd are in use on the system"
fi
if [ -n "$l_tsd" ]; then
echo -e "\n- PASS:\n$output\n"
else
echo -e "\n- FAIL:\n$output\n"
fi
}
On physical systems, and virtual systems where host based time synchronization is not
available.
Select one of the three time synchronization daemons; chrony(1)
, systemd-timesyncd (2)
, or ntp (3)
, and following the remediation procedure for the selected daemon.
Note: enabling more than one synchronization daemon could lead to unexpected or unreliable results:
chrony
Run the following command to install chrony
:
# apt install chrony
Run the following commands to stop and mask the systemd-timesyncd
daemon:
# systemctl stop systemd-timesyncd.service
# systemctl --now mask systemd-timesyncd.service
Run the following command to remove the ntp
package:
# apt purge ntp
Subsection:
Configure chrony
should be followed
Subsections:Configure systemd-timesyncd
andConfigure ntp
should be skipped
systemd-timesync
Run the following command to remove the chrony
package:
# apt purge chrony
Run the following command to remove the ntp
package:
# apt purge ntp
Subsection:
Configure systemd-timesyncshould
be followed
Subsections:Configure chrony
andConfigure ntp
should be skipped
ntp
Run the following command to install ntp
:
# apt install ntp
Run the following commands to stop and mask the systemd-timesyncd
daemon:
# systemctl stop systemd-timesyncd.service
# systemctl --now mask systemd-timesyncd.service
Run the following command to remove the chrony
package:
# apt purge chrony
Subsection:
Configure ntp
should be followed
Subsections:Configure systemd-timesyncd
andConfigure chrony
should be skipped
- Ensure chrony is configured with authorized timeserver (Manual)
IF chrony
is in use on the system, run the following command to display the server and/or pool directive:
# grep -Pr --include=*.{sources,conf} '^\h*(server|pool)\h+\H+' /etc/chrony/
Verify that at least one pool
line and/or at least three server
lines are returned, and the timeserver on the returned lines follows local site policy
Output examples:
pool
directive:
pool time.nist.gov iburst maxsources 4 #The maxsources option is unique to the pool directive
server
directive:
server time-a-g.nist.gov iburst
server 132.163.97.3 iburst
server time-d-b.nist.gov iburst
Edit /etc/chrony/chrony.conf
or a file ending in .sources
in /etc/chrony/sources.d/
and add or edit server or pool lines as appropriate according to local site policy:
<[server|pool]> <[remote-server|remote-pool]>
Examples:
pool
directive:
pool time.nist.gov iburst maxsources 4 #The maxsources option is unique to the pool directive
server
directive:
server time-a-g.nist.gov iburst
server 132.163.97.3 iburst
server time-d-b.nist.gov iburst
Run one of the following commands to load the updated time sources into chronyd
running config:
# systemctl restart chronyd
- OR if sources are in a .sources file -
# chronyc reload sources
If pool and/or server directive(s) are set in a sources file in /etc/chrony/sources.d
, the line:
sourcedir /etc/chrony/sources.d
must be present in /etc/chrony/chrony.conf
- Ensure chrony is running as user _chrony (Automated)
IF chrony is in use on the system, run the following command to verify the chronydservice is being run as the _chrony user:
# ps -ef | awk '(/[c]hronyd/ && $1!="_chrony") { print $1 }'
Nothing should be returned
Add or edit the user line to /etc/chrony/chrony.conf
or a file ending in .conf
in /etc/chrony/conf.d/
:
user _chrony
- Ensure chrony is enabled and running (Automated)
IF chrony
is in use on the system, run the following commands:
Run the following command to verify that the chrony
service is enabled:
# systemctl is-enabled chrony.service
enabled
Run the following command to verify that the chrony
service is active:
# systemctl is-active chrony.service
active
In order to start chrony
:
Run the following command to unmask chrony.service
:
# systemctl unmask chrony.service
Run the following command to enable and start chrony.service
:
# systemctl --now enable chrony.service
- Ensure systemd-timesyncd configured with authorized timeserver (Manual)
IF systemd-timesyncd
is in use on the system, run the following command:
# find /etc/systemd -type f -name '*.conf' -exec grep -Ph '^\h*(NTP|FallbackNTP)=\H+' {} +
Verify that NPT=<space_seporated_list_of_servers>
and/or FallbackNTP=<space_seporated_list_of_servers>
is returned and that the time server(s) shown follows local site policy
Example Output:
/etc/systemd/timesyncd.conf.d/50-timesyncd.conf:NTP=time.nist.gov
/etc/systemd/timesyncd.conf.d/50-timesyncd.conf:FallbackNTP=time-a-g.nist.gov
time-b-g.nist.gov time-c-g.nist.gov
Edit or create a file in /etc/systemd/timesyncd.conf.d
ending in .conf
and add the NTP=
and/or FallbackNTP=
lines to the [Time]
section:
Example:
[Time]
NTP=time.nist.gov # Uses the generic name for NIST's time servers
-AND/OR�FallbackNTP=time-a-g.nist.gov time-b-g.nist.gov time-c-g.nist.gov # Space
separated list of NIST time servers
Servers added to these line(s) should follow local site policy. NIST servers are for example. The
timesyncd.conf.d
directory may need to be created
Example script: The following example script will create the systemd-timesyncd
drop-in configuration snippet:
#!/usr/bin/env bash
ntp_ts="time.nist.gov"
ntp_fb="time-a-g.nist.gov time-b-g.nist.gov time-c-g.nist.gov"
disfile="/etc/systemd/timesyncd.conf.d/50-timesyncd.conf"
if ! find /etc/systemd -type f -name '*.conf' -exec grep -Ph '^\h*NTP=\H+' {} +; then
[ ! -d /etc/systemd/timesyncd.conf.d ] && mkdir /etc/systemd/timesyncd.conf.d
! grep -Pqs '^\h*\[Time\]' "$disfile" && echo "[Time]" >> "$disfile"
echo "NTP=$ntp_ts" >> "$disfile"
fi
if ! find /etc/systemd -type f -name '*.conf' -exec grep -Ph '^\h*FallbackNTP=\H+' {} +; then
[ ! -d /etc/systemd/timesyncd.conf.d ] && mkdir /etc/systemd/timesyncd.conf.d
! grep -Pqs '^\h*\[Time\]' "$disfile" && echo "[Time]" >> "$disfile"
echo "FallbackNTP=$ntp_fb" >> "$disfile"
fi
Run the following command to reload the systemd-timesyncd
configuration:
# systemctl try-reload-or-restart systemd-timesyncd
- Ensure systemd-timesyncd is enabled and running (Automated)
IF systemd-timesyncd
is in use on the system, run the following commands:
Run the following command to verify that the systemd-timesyncd
service is enabled:
# systemctl is-enabled systemd-timesyncd.service
enabled
Run the following command to verify that the systemd-timesyncd
service is active:
# systemctl is-active systemd-timesyncd.service
active
In order to start systemd-timesync
:
Run the following command to unmask systemd-timesyncd.service
:
# systemctl unmask systemd-timesyncd.service
Run the following command to enable and start systemd-timesyncd.service
:
# systemctl --now enable systemd-timesyncd.service
- Ensure ntp access control is configured (Automated)
IF ntp
is in use on the system, run the following command to verify the restrict
lines:
# grep -P -- '^\h*restrict\h+((-4\h+)?|-6\h+)default\h+(?:[^#\n\r]+\h+)*(?!(?:\2|\3|\4|\5))(\h*\bkod\b\h*|\h*\bnomodify\b\h*|\h*\bnotrap\b\h*|\h*\bnopeer\b\h*|\h*\bnoquery\b\h*)\h+(?:[^#\n\r]+\h+)*(?!(?:\1|\3|\4|\5))(\h*\bkod\b\h*|\h*\bnomodify\b\h*|\h*\bnotrap\b\h*|\h*\bnopeer\b\h*|\h*\bnoquery\b\h*)\h+(?:[^#\n\r]+\h+)*(?!(?:\1|\2|\4|\5))(\h*\bkod\b\h*|\h*\bnomodify\b\h*|\h*\bnotrap\b\h*|\h*\bnopeer\b\h*|\h*\bnoquery\b\h*)\h+(?:[^#\n\r]+\h+)*(?!(?:\1|\2|\3|\5))(\h*\bkod\b\h*|\h*\bnomodify\b\h*|\h*\bnotrap\b\h*|\h*\bnopeer\b\h*|\h*\bnoquery\b\h*)\h+(?:[^#\n\r]+\h+)*(?!(?:\1|\2|\3|\4))(\h*\bkod\b\h*|\h*\bnomodify\b\h*|\h*\bnotrap\b\h*|\h*\bnopeer\b\h*|\h*\bnoquery\b\h*)\h*(?:\h+\H+\h*)*(?:\h+#.*)?$' /etc/ntp.conf
Output should be similar to:
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
Verify that the output includes two lines, and both lines include: default
, kod
, nomodify
, notrap
, nopeer
and noquery
.
Add or edit restrict lines in /etc/ntp.conf
to match the following:
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
- Ensure ntp is configured with authorized timeserver (Manual)
IF ntp
is in use on the system, run the following command to display the server and/or pool mode:
# grep -P -- '^\h*(server|pool)\h+\H+' /etc/ntp.conf
Verify that at least one pool
line and/or at least three server
lines are returned, and the timeserver on the returned lines follows local site policy
Output examples:
pool
mode:
pool time.nist.gov iburst maxsources 4 #The maxsources option is unique to the pool directive
server
mode:
server time-a-g.nist.gov iburst
server 132.163.97.3 iburst
server time-d-b.nist.gov iburst
Edit /etc/ntp.conf
and add or edit server
or pool
lines as appropriate according to local site policy:
<[server|pool]> <[remote-server|remote-pool]>
Examples:
pool
mode:
pool time.nist.gov iburst maxsources 4 #The maxsources option is unique to the pool directive
server
mode:
server time-a-g.nist.gov iburst
server 132.163.97.3 iburst
server time-d-b.nist.gov iburst
Run the following command to load the updated time sources into ntp
running config:
# systemctl restart ntp
- Ensure ntp is running as user ntp (Automated)
IF ntp
is in use on the system run the following command to verify the ntpd
daemon is being run as the user ntp
:
# ps -ef | awk '(/[n]tpd/ && $1!="ntp") { print $1 }'
Nothing should be returned
Run the following command to verify the RUNASUSER=
is set to ntp
in /etc/init.d/ntp
:
# grep -P -- '^\h*RUNASUSER=' /etc/init.d/ntp
RUNASUSER=ntp
Add or edit the following line in /etc/init.d/ntp
:
RUNASUSER=ntp
Run the following command to restart ntp.servocee
:
# systemctl restart ntp.service
- Ensure ntp is enabled and running (Automated)
IF ntp
is in use on the system, run the following commands:
Run the following command to verify that the ntp
service is enabled:
# systemctl is-enabled ntp.service
enabled
Run the following command to verify that the ntp
service is active:
# systemctl is-active ntp.service
active
IF ntp
is in use on the system, run the following commands:
Run the following command to unmask ntp.service
:
# systemctl unmask ntp.service
Run the following command to enable and start ntp.service
:
# systemctl --now enable ntp.service
This section describes services that are installed on systems that specifically need to run these services. If any of these services are not required, it is recommended that they be deleted from the system to reduce the potential attack surface. If a package is required as a dependency, and the service is not required, the service should be stopped and masked.
# systemctl --now mask <service_name>
If any of the following services are required DO NOT delete them
- Ensure X Window System is not installed (Automated)
Many Linux systems run applications which require a Java runtime. Some Linux Java packages have a dependency on specific X Windows xorg-x11-fonts. One workaround to avoid this dependency is to use the "headless" Java packages for your specific Java runtime, if provided by your distribution.
Verify X Windows System is not installed:
dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' xserver-xorg* | grep -Pi '\h+installed\b'
Nothing should be returned
Remove the X Windows System packages:
apt purge xserver-xorg*
- Ensure Avahi Server is not installed (Automated)
Run the following command to verify avahi-daemon
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' avahi-daemon
avahi-daemon unknown ok not-installed not-installed
Run the following commands to remove avahi-daemon
:
# systemctl stop avahi-daaemon.service
# systemctl stop avahi-daemon.socket
# apt purge avahi-daemon
- Ensure CUPS is not installed (Automated)
Run the following command to verify cups
is not Installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' cups
cups unknown ok not-installed not-installed
Run one of the following commands to remove cups
:
# apt purge cups
- Ensure DHCP Server is not installed (Automated)
Run the following commands to verify isc-dhcp-server
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' isc�dhcp-server
isc-dhcp-server unknown ok not-installed not-installed
Run the following command to remove isc-dhcp-server
:
# apt purge isc-dhcp-server
- Ensure LDAP server is not installed (Automated)
Run the following command to verify slapd
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' slapd
slapd unknown ok not-installed not-installed
Run one of the following commands to remove slapd
:
# apt purge slapd
- Ensure NFS is not installed (Automated)
Run the following command to verify nfs
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' nfs-kernel-server
nfs-kernel-server unknown ok not-installed not-installed
Run the following command to remove nfs
:
# apt purge nfs-kernel-server
- Ensure DNS Server is not installed (Automated)
Run the following command to verify DNS server
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' bind9
bind9 unknown ok not-installed not-installed
Run the following commands to disable DNS server
:
# apt purge bind9
- Ensure FTP Server is not installed (Automated)
Run the following command to verify vsftpd
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' vsftpd
vsftpd unknown ok not-installed not-installed
Run the following command to remove vsftpd
:
# apt purge vsftpd
- Ensure HTTP server is not installed (Automated)
Run the following command to verify apache
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' apache2
apache2 unknown ok not-installed not-installed
Run the following command to remove apache
:
# apt purge apache2
- Ensure IMAP and POP3 server are not installed (Automated)
Run the following command to verify dovecot-imapd
and dovecot-pop3d
are not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' dovecot-imapd dovecot-pop3d
dovecot-imapd unknown ok not-installed not-installed
dovecot-pop3d unknown ok not-installed not-installed
Run one of the following commands to remove dovecot-imapd and dovecot-pop3d
:
# apt purge dovecot-imapd dovecot-pop3d
- Ensure Samba is not installed (Automated)
Run the following command to verify samba
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n'
samba
samba unknown ok not-installed not-installed
Run the following command to remove samba
:
# apt purge samba
- Ensure HTTP Proxy Server is not installed (Automated)
Run the following command to verify squid
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n'
squid
squid unknown ok not-installed not-installed
Run the following command to remove squid
:
# apt purge squid
- Ensure SNMP Server is not installed (Automated)
Run the following command to verify snmpd
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' snmp
snmp unknown ok not-installed not-installed
Run the following command to remove snmp
:
# apt purge snmp
- Ensure NIS Server is not installed (Automated)
Run the following command to verify nis
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' nis
nis unknown ok not-installed not-installed
Run the following command to remove nis
:
# apt purge nis
- Ensure mail transfer agent is configured for local-only mode (Automated)
Run the following command to verify that the MTA is not listening on any non-loopback address (127.0.0.1
or ::1
).
# ss -lntu | grep -E ':25\s' | grep -E -v '\s(127.0.0.1|::1):25\s'
Nothing should be returned
Edit /etc/postfix/main.cf
and add the following line to the RECEIVING MAIL section.
If the line already exists, change it to look like the line below:
inet_interfaces = loopback-only
Run the following command to restart postfix
:
# systemctl restart postfix
- Ensure rsync service is either not installed or masked (Automated)
Run the following command to verify rsync
is not installed:
dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' rsync
rsync unknown ok not-installed not-installed
OR
Run the following commands to verify that rsync is inactive and masked:
# systemctl is-active rsync
inactive
# systemctl is-enabled rsync
masked
Run the following command to remove rsync
:
# apt purge rsync
OR
Run the following commands to stop and mask rsync
:
# systemctl stop rsync
# systemctl mask rsync
- Ensure NIS Client is not installed (Automated)
Verify nis
is not installed. Use the following command to provide the needed
information:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' nis
nis unknown ok not-installed not-installed
Uninstall nis
:
# apt purge nis
- Ensure rsh client is not installed (Automated)
Verify rsh
is not installed. Use the following command to provide the needed
information:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' rsh-client
rsh-client unknown ok not-installed not-installed
Uninstall rsh
:
# apt purge rsh
- Ensure talk client is not installed (Automated)
Verify talk
is not installed. The following command may provide the needed information:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' talk
talk unknown ok not-installed not-installed
Uninstall talk
:
# apt purge talk
- Ensure telnet client is not installed (Automated)
Verify telnet
is not installed. The following command may provide the needed information:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' telnet
telnet unknown ok not-installed not-installed
Uninstall telnet
:
# apt purge telnet
- Ensure LDAP client is not installed (Automated)
Verify ldap-utils
is not installed. The following command may provide the needed information:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' ldap-utils
ldap-utils unknown ok not-installed not-installed
Uninstall ldap-utils
:
# apt purge ldap-utils
- Ensure RPC is not installed (Automated)
Verify rpcbind
is not installed. The following command may provide the needed information:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' rpcbind
rpcbind unknown ok not-installed not-installed
Uninstall rpcbind
:
# apt purge rpcbind
Run the following command:
# lsof -i -P -n | grep -v "(ESTABLISHED)"
Review the output to ensure that all services listed are required on the system. If a listed service is not required, remove the package containing the service. If the package containing a non-essential service is required, stop and mask the non-essential service.
Run the following command to remove the package containing the service:
# apt purge <package_name>
OR If required packages have a dependency:
Run the following command to stop and mask the service:
# systemctl --now mask <service_name>
- Ensure system is checked to determine if IPv6 is enabled (Manual)
Run the following script to verify IPv6 status on the system:
#!/usr/bin/env bash
{
output=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf"
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq --ipv6.disable=1 && output="IPv6 Disabled in \"$grubfile\""
fi
if grep -Pqs -- "^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $searchloc && \
grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $searchloc && \
sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" && \
sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
[ -n "$output" ] && output="$output, and in sysctl config" ||output="ipv6 disabled in sysctl config"
fi
[ -n "$output" ] && echo -e "\n$output\n" || echo -e "\nIPv6 is enabled on the system\n"
}
It is recommended that IPv6 be enabled and configured in accordance with Benchmark recommendations.
If IPv6 is to be disabled, use one of the two following methods to disable IPv6 on the system:
To disable IPv6 through the GRUB2 config, run the following command to add ipv6.disable=1
to the GRUB_CMDLINE_LINUX
parameters:
Edit /etc/default/grub
and add ipv6.disable=1
to the GRUB_CMDLINE_LINUX
parameters:
Example:
GRUB_CMDLINE_LINUX="ipv6.disable=1"
Run the following command to update the grub2
configuration:
# update-grub
OR To disable IPv6 through sysctl settings, set the following parameters in /etc/sysctl.conf
or a /etc/sysctl.d/*
file:
Example:
# printf "
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
" >> /etc/sysctl.d/60-disable_ipv6.conf
Run the following command to set the active kernel parameters:
# {
sysctl -w net.ipv6.conf.all.disable_ipv6=1
sysctl -w net.ipv6.conf.default.disable_ipv6=1
sysctl -w net.ipv6.route.flush=1
}
- Ensure wireless interfaces are disabled (Automated)
Many if not all laptop workstations and some desktop workstations will connect via wireless requiring these interfaces be enabled.
Run the following script to verify no wireless interfaces are active on the system:
#!/bin/bash
if command -v nmcli >/dev/null 2>&1 ; then
if nmcli radio all | grep -Eq '\s*\S+\s+disabled\s+\S+\s+disabled\b'; then
echo "Wireless is not enabled"
else
nmcli radio all
fi
elif [ -n "$(find /sys/class/net/*/ -type d -name wireless)" ]; then
t=0
mname=$(for driverdir in $(find /sys/class/net/*/ -type d -name wireless | xargs -0 dirname); do basename "$(readlink -f "$driverdir"/device/driver/module)";done | sort -u)
for dm in $mname; do
if grep -Eq "^\s*install\s+$dm\s+/bin/(true|false)" /etc/modprobe.d/*.conf; then
/bin/true
else
echo "$dm is not disabled"
t=1
fi
done
[ "$t" -eq 0 ] && echo "Wireless is not enabled"
else
echo "Wireless is not enabled"
fi
Output should be:
Wireless is not enabled
Run the following script to disable any wireless interfaces:
#!/bin/bash
if command -v nmcli >/dev/null 2>&1 ; then
nmcli radio all off
else
if [ -n "$(find /sys/class/net/*/ -type d -name wireless)" ]; then
mname=$(for driverdir in $(find /sys/class/net/*/ -type d -name wireless | xargs -0 dirname); do basename "$(readlink -f "$driverdir"/device/driver/module)";done | sort -u)
for dm in $mname; do
echo "install $dm /bin/true" >> /etc/modprobe.d/disable_wireless.conf
done
fi
fi
The following network parameters are intended for use if the system is to act as a host only. A system is considered host only if the system has a single interface, or has multiple interfaces but will not be configured as a router
- Ensure packet redirect sending is disabled (Automated)
Run the following script to verify:
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.send_redirects=0 net.ipv4.conf.default.send_redirects=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPC
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set:
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.send_redirects=0 net.ipv4.conf.default.send_redirects=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPF
done
}
- Ensure IP forwarding is disabled (Automated)
Run the following script to verify:
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.ip_forward=0 net.ipv6.conf.all.forwarding=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
ipv6_chk()
{
l_ipv6s=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq -- ipv6.disable=1 && l_ipv6s="disabled"
fi
if grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && grep -Pqs -- "^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" && sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
l_ipv6s="disabled"
fi
if [ -n "$l_ipv6s" ]; then
l_output="$l_output\n - IPv6 is disabled on the system,\"$l_kpname\" is not applicable"
else
KPC
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
if grep -q '^net.ipv6.' <<< "$l_kpe"; then
ipv6_chk
else
KPC
fi
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set:
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.ip_forward=0 net.ipv6.conf.all.forwarding=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
IPV6F_CHK()
{
l_ipv6s=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq -- ipv6.disable=1 && l_ipv6s="disabled"
fi
if grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && \
grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && \
sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" && \
sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
l_ipv6s="disabled"
fi
if [ -n "$l_ipv6s" ]; then
echo -e "\n - IPv6 is disabled on the system, \"$l_kpname\" is not applicable"
else
KPF
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
if grep -q '^net.ipv6.' <<< "$l_kpe"; then
l_kpfile="/etc/sysctl.d/60-netipv6_sysctl.conf"
IPV6F_CHK
else
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF
fi
done
}
The following network parameters are intended for use on both host only and router systems. A system acts as a router if it has at least two interfaces and is configured to perform routing functions.
- Ensure source routed packets are not accepted (Automated)
Run the following script to verify:
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.accept_source_route=0 net.ipv4.conf.default.accept_source_route=0 net.ipv6.conf.all.accept_source_route=0 net.ipv6.conf.default.accept_source_route=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
ipv6_chk()
{
l_ipv6s=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq -- ipv6.disable=1 && l_ipv6s="disabled"
fi
if grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && grep -Pqs -- "^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" && sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
l_ipv6s="disabled"
fi
if [ -n "$l_ipv6s" ]; then
l_output="$l_output\n - IPv6 is disabled on the system, \"$l_kpname\" is not applicable"
else
KPC
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
if grep -q '^net.ipv6.' <<< "$l_kpe"; then
ipv6_chk
else
KPC
fi
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set:
- net.ipv4.conf.all.accept_source_route = 0
- net.ipv4.conf.default.accept_source_route = 0
- net.ipv6.conf.all.accept_source_route = 0
- net.ipv6.conf.default.accept_source_route = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.accept_source_route=0 net.ipv4.conf.default.accept_source_route=0 net.ipv6.conf.all.accept_source_route=0 net.ipv6.conf.default.accept_source_route=0" l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in\"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
IPV6F_CHK()
{
l_ipv6s=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq -- ipv6.disable=1 && l_ipv6s="disabled"
fi
if grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && \
grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && \
sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" && \
sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
l_ipv6s="disabled"
fi
if [ -n "$l_ipv6s" ]; then
echo -e "\n - IPv6 is disabled on the system, \"$l_kpname\" is not applicable"
else
KPF
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
if grep -q '^net.ipv6.' <<< "$l_kpe"; then
l_kpfile="/etc/sysctl.d/60-netipv6_sysctl.conf"
IPV6F_CHK
else
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF
fi
done
}
- Ensure ICMP redirects are not accepted (Automated)
Run the following script to verify:
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.accept_redirects=0 net.ipv4.conf.default.accept_redirects=0 net.ipv6.conf.all.accept_redirects=0 net.ipv6.conf.default.accept_redirects=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
ipv6_chk()
{
l_ipv6s=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq -- ipv6.disable=1 && l_ipv6s="disabled"
fi
if grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && grep -Pqs -- "^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" && sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
l_ipv6s="disabled"
fi
if [ -n "$l_ipv6s" ]; then
l_output="$l_output\n - IPv6 is disabled on the system, \"$l_kpname\" is not applicable"
else
KPC
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
if grep -q '^net.ipv6.' <<< "$l_kpe"; then
ipv6_chk
else
KPC
fi
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set:
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.accept_redirects=0 net.ipv4.conf.default.accept_redirects=0 net.ipv6.conf.all.accept_redirects=0 net.ipv6.conf.default.accept_redirects=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
IPV6F_CHK()
{
l_ipv6s=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq -- ipv6.disable=1 && l_ipv6s="disabled"
fi
if grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && \
grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && \
sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" && \
sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
l_ipv6s="disabled"
fi
if [ -n "$l_ipv6s" ]; then
echo -e "\n - IPv6 is disabled on the system, \"$l_kpname\" is not applicable"
else
KPF
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
if grep -q '^net.ipv6.' <<< "$l_kpe"; then
l_kpfile="/etc/sysctl.d/60-netipv6_sysctl.conf"
IPV6F_CHK
else
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF
fi
done
}
- Ensure secure ICMP redirects are not accepted (Automated)
Run the following script to verify:
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.default.secure_redirects=0 net.ipv4.conf.all.secure_redirects=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPC
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set:
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
#!/usr/bin/env bash
kernel_parameter_fix()
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.default.secure_redirects=0 net.ipv4.conf.all.secure_redirects=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPF
done
}
- Ensure suspicious packets are logged (Automated)
Run the following script to verify:
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.log_martians=1 net.ipv4.conf.default.log_martians=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPC
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set:
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.log_martians=1 net.ipv4.conf.default.log_martians=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPF
done
}
- Ensure broadcast ICMP requests are ignored (Automated)
Run the following script to verify net.ipv4.icmp_echo_ignore_broadcasts = 1
:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.icmp_echo_ignore_broadcasts=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPC
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set net.ipv4.icmp_echo_ignore_broadcasts = 1
:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.icmp_echo_ignore_broadcasts=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPF
done
}
- Ensure bogus ICMP responses are ignored (Automated)
Run the following script to verify icmp_ignore_bogus_error_responses = 1
:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="icmp_ignore_bogus_error_responses=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPC
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set icmp_ignore_bogus_error_responses = 1
:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="icmp_ignore_bogus_error_responses=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPF
done
}
- Ensure Reverse Path Filtering is enabled (Automated)
If you are using asymmetrical routing on your system, you will not be able to enable this feature without breaking the routing
Run the following script to verify:
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.rp_filter=1 net.ipv4.conf.default.rp_filter=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPC
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set:
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.conf.all.rp_filter=1 net.ipv4.conf.default.rp_filter=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPF
done
}
- Ensure TCP SYN Cookies is enabled (Automated)
Run the following script to verify net.ipv4.tcp_syncookies = 1
:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.tcp_syncookies=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPC
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set net.ipv4.tcp_syncookies = 1
:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv4.tcp_syncookies=1"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
KPF
done
}
- Ensure IPv6 router advertisements are not accepted (Automated)
Run the following script to verify:
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv6.conf.all.accept_ra=0 net.ipv6.conf.default.accept_ra=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPC()
{
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
l_pafile="$(grep -Psl -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc)"
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
if [ "$l_krp" = "$l_kpvalue" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in the running configuration"
else
l_output2="$l_output2\n - \"$l_kpname\" is set to \"$l_krp\" in the running configuration"
fi
if [ -n "$l_pafile" ]; then
l_output="$l_output\n - \"$l_kpname\" is set to \"$l_kpvalue\" in \"$l_pafile\""
else
l_output2="$l_output2\n - \"$l_kpname = $l_kpvalue\" is not set in a kernel parameter configuration file"
fi
[ -n "$l_fafile" ] && l_output2="$l_output2\n - \"$l_kpname\" is set incorrectly in \"$l_fafile\""
}
ipv6_chk()
{
l_ipv6s=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq -- ipv6.disable=1 && l_ipv6s="disabled"
fi
if grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && grep -Pqs -- "^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" &&sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs -"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
l_ipv6s="disabled"
fi
if [ -n "$l_ipv6s" ]; then
l_output="$l_output\n - IPv6 is disabled on the system, \"$l_kpname\" is not applicable"
else
KPC
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
if grep -q '^net.ipv6.' <<< "$l_kpe"; then
ipv6_chk
else
KPC
fi
done
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to set:
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_parlist="net.ipv6.conf.all.accept_ra=0 net.ipv6.conf.default.accept_ra=0"
l_searchloc="/run/sysctl.d/*.conf /etc/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /lib/sysctl.d/*.conf /etc/sysctl.conf $([ -f /etc/default/ufw ] && awk -F= '/^\s*IPT_SYSCTL=/ {print $2}' /etc/default/ufw)"
KPF()
{
# comment out incorrect parameter(s) in kernel parameter file(s)
l_fafile="$(grep -s -- "^\s*$l_kpname" $l_searchloc | grep -Pv --"\h*=\h*$l_kpvalue\b\h*" | awk -F: '{print $1}')"
for l_bkpf in $l_fafile; do
echo -e "\n - Commenting out \"$l_kpname\" in \"$l_bkpf\""
sed -ri "/$l_kpname/s/^/# /" "$l_bkpf"
done
# Set correct parameter in a kernel parameter file
if ! grep -Pslq -- "^\h*$l_kpname\h*=\h*$l_kpvalue\b\h*(#.*)?$" $l_searchloc; then
echo -e "\n - Setting \"$l_kpname\" to \"$l_kpvalue\" in \"$l_kpfile\""
echo "$l_kpname = $l_kpvalue" >> "$l_kpfile"
fi
# Set correct parameter in active kernel parameters
l_krp="$(sysctl "$l_kpname" | awk -F= '{print $2}' | xargs)"
if [ "$l_krp" != "$l_kpvalue" ]; then
echo -e "\n - Updating \"$l_kpname\" to \"$l_kpvalue\" in the active kernel parameters"
sysctl -w "$l_kpname=$l_kpvalue"
sysctl -w "$(awk -F'.' '{print $1"."$2".route.flush=1"}' <<< "$l_kpname")"
fi
}
IPV6F_CHK()
{
l_ipv6s=""
grubfile=$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl -- '^\h*(kernelopts=|linux|kernel)' {} \;)
if [ -s "$grubfile" ]; then
! grep -P -- "^\h*(kernelopts=|linux|kernel)" "$grubfile" | grep -vq -- ipv6.disable=1 && l_ipv6s="disabled"
fi
if grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && \
grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$" $l_searchloc && \
sysctl net.ipv6.conf.all.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.all\.disable_ipv6\h*=\h*1\h*(#.*)?$" && \
sysctl net.ipv6.conf.default.disable_ipv6 | grep -Pqs --"^\h*net\.ipv6\.conf\.default\.disable_ipv6\h*=\h*1\h*(#.*)?$"; then
l_ipv6s="disabled"
fi
if [ -n "$l_ipv6s" ]; then
echo -e "\n - IPv6 is disabled on the system, \"$l_kpname\" is not applicable"
else
KPF
fi
}
for l_kpe in $l_parlist; do
l_kpname="$(awk -F= '{print $1}' <<< "$l_kpe")"
l_kpvalue="$(awk -F= '{print $2}' <<< "$l_kpe")"
if grep -q '^net.ipv6.' <<< "$l_kpe"; then
l_kpfile="/etc/sysctl.d/60-netipv6_sysctl.conf"
IPV6F_CHK
else
l_kpfile="/etc/sysctl.d/60-netipv4_sysctl.conf"
KPF
fi
done
}
- Ensure DCCP is disabled (Automated)
Run the following script to verify dccp
is disabled.
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_mname="dccp" # set module name
# Check how module will be loaded
l_loadable="$(modprobe -n -v "$l_mname")"
if grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
l_output="$l_output\n - module: \"$l_mname\" is not loadable: \"$l_loadable\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is loadable: \"$l_loadable\""
fi
# Check is the module currently loaded
if ! lsmod | grep "$l_mname" > /dev/null 2>&1; then
l_output="$l_output\n - module: \"$l_mname\" is not loaded"
else
l_output2="$l_output2\n - module: \"$l_mname\" is loaded"
fi
# Check if the module is deny listed
if grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
l_output="$l_output\n - module: \"$l_mname\" is deny listed in: \"$(grep -Pl -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*)\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is not deny listed"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to disabled dccp
.
#!/usr/bin/env bash
{
l_mname="dccp" # set module name
if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
echo -e " - setting module: \"$l_mname\" to be not loadable"
echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
fi
if lsmod | grep "$l_mname" > /dev/null 2>&1; then
echo -e " - unloading module \"$l_mname\""
modprobe -r "$l_mname"
fi
if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
echo -e " - deny listing \"$l_mname\""
echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
fi
}
- Ensure SCTP is disabled (Automated)
Run the following script to verify sctp
is disabled:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_mname="sctp" # set module name
# Check how module will be loaded
l_loadable="$(modprobe -n -v "$l_mname")"
if grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
l_output="$l_output\n - module: \"$l_mname\" is not loadable: \"$l_loadable\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is loadable: \"$l_loadable\""
fi
# Check is the module currently loaded
if ! lsmod | grep "$l_mname" > /dev/null 2>&1; then
l_output="$l_output\n - module: \"$l_mname\" is not loaded"
else
l_output2="$l_output2\n - module: \"$l_mname\" is loaded"
fi
# Check if the module is deny listed
if grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
l_output="$l_output\n - module: \"$l_mname\" is deny listed in: \"$(grep -Pl -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*)\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is not deny listed"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to disable sctp
:
#!/usr/bin/env bash
{
l_mname="sctp" # set module name
if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
echo -e " - setting module: \"$l_mname\" to be not loadable"
echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
fi
if lsmod | grep "$l_mname" > /dev/null 2>&1; then
echo -e " - unloading module \"$l_mname\""
modprobe -r "$l_mname"
fi
if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
echo -e " - deny listing \"$l_mname\""
echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
fi
}
- Ensure RDS is disabled (Automated)
Run the following script to verify rds
is disabled:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_mname="rds" # set module name
# Check how module will be loaded
l_loadable="$(modprobe -n -v "$l_mname")"
if grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
l_output="$l_output\n - module: \"$l_mname\" is not loadable: \"$l_loadable\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is loadable: \"$l_loadable\""
fi
# Check is the module currently loaded
if ! lsmod | grep "$l_mname" > /dev/null 2>&1; then
l_output="$l_output\n - module: \"$l_mname\" is not loaded"
else
l_output2="$l_output2\n - module: \"$l_mname\" is loaded"
fi
# Check if the module is deny listed
if grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
l_output="$l_output\n - module: \"$l_mname\" is deny listed in: \"$(grep -Pl -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*)\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is not deny listed"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
# lsmod | grep rds
<No output>
Run the following script to disable rds
:
#!/usr/bin/env bash
{
l_mname="rds" # set module name
if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
echo -e " - setting module: \"$l_mname\" to be not loadable"
echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
fi
if lsmod | grep "$l_mname" > /dev/null 2>&1; then
echo -e " - unloading module \"$l_mname\""
modprobe -r "$l_mname"
fi
if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
echo -e " - deny listing \"$l_mname\""
echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
fi
}
- Ensure TIPC is disabled (Automated)
Run the following script to verify tipc
is disabled:
#!/usr/bin/env bash
{
l_output="" l_output2=""
l_mname="tipc" # set module name
# Check how module will be loaded
l_loadable="$(modprobe -n -v "$l_mname")"
if grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
l_output="$l_output\n - module: \"$l_mname\" is not loadable: \"$l_loadable\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is loadable: \"$l_loadable\""
fi
# Check is the module currently loaded
if ! lsmod | grep "$l_mname" > /dev/null 2>&1; then
l_output="$l_output\n - module: \"$l_mname\" is not loaded"
else
l_output2="$l_output2\n - module: \"$l_mname\" is loaded"
fi
# Check if the module is deny listed
if grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
l_output="$l_output\n - module: \"$l_mname\" is deny listed in: \"$(grep -Pl -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*)\""
else
l_output2="$l_output2\n - module: \"$l_mname\" is not deny listed"
fi
# Report results. If no failures output in l_output2, we pass
if [ -z "$l_output2" ]; then
echo -e "\n- Audit Result:\n ** PASS **\n$l_output\n"
else
echo -e "\n- Audit Result:\n ** FAIL **\n - Reason(s) for audit failure:\n$l_output2\n"
[ -n "$l_output" ] && echo -e "\n- Correctly set:\n$l_output\n"
fi
}
Run the following script to disable tipc
:
#!/usr/bin/env bash
{
l_mname="tipc" # set module name
if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
echo -e " - setting module: \"$l_mname\" to be not loadable"
echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
fi
if lsmod | grep "$l_mname" > /dev/null 2>&1; then
echo -e " - unloading module \"$l_mname\""
modprobe -r "$l_mname"
fi
if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
echo -e " - deny listing \"$l_mname\""
echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
fi
}
If nftables or iptables are being used in your environment, please follow the guidance in their respective section and pass-over the guidance in this section.
- Ensure ufw is installed (Automated)
Run the following command to verify that Uncomplicated Firewall (UFW) is installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' ufw
ufw install ok installed installed
Run the following command to install Uncomplicated Firewall (UFW):
apt install ufw
- Ensure iptables-persistent is not installed with ufw (Automated)
Run the following command to verify that the iptables-persistent
package is not installed:
dpkg-query -s iptables-persistent
package 'iptables-persistent' is not installed and no information is available
Run the following command to remove the iptables-persistent package:
# apt purge iptables-persistent
- Ensure ufw service is enabled (Automated)
When running ufw enable or starting ufw via its initscript, ufw will flush its chains.
This is required so ufw can maintain a consistent state, but it may drop existing connections (eg ssh). ufw does support adding rules before enabling the firewall.
Run the following command before running ufw enable.
# ufw allow proto tcp from any to any port 22
The rules will still be flushed, but the ssh port will be open after enabling the firewall. Please note that once ufw is 'enabled', ufw will not flush the chains when adding or removing rules (but will when modifying a rule or changing the default policy)
By default, ufw will prompt when enabling the firewall while running under ssh. This can be disabled by using ufw --force enable
Run the following command to verify that the ufw
daemon is enabled:
# systemctl is-enabled ufw.service
enabled
Run the following command to verify that the ufw
daemon is active:
# systemctl is-active ufw
active
Run the following command to verify ufw
is active
# ufw status
Status: active
Run the following command to unmask the ufw
daemon:
# systemctl unmask ufw.service
Run the following command to enable and start the ufw
daemon:
# systemctl --now enable ufw.service
active
Run the following command to enable ufw
:
# ufw enable
- Ensure ufw loopback traffic is configured (Automated)
Run the following commands and verify output includes the listed rules in order:
# ufw status verbose
To Action From
-- ------ ----
Anywhere on lo ALLOW IN Anywhere
Anywhere DENY IN 127.0.0.0/8
Anywhere (v6) on lo ALLOW IN Anywhere (v6)
Anywhere (v6) DENY IN ::1
Anywhere ALLOW OUT Anywhere on lo
Anywhere (v6) ALLOW OUT Anywhere (v6) on lo
Run the following commands to implement the loopback rules:
# ufw allow in on lo
# ufw allow out on lo
# ufw deny in from 127.0.0.0/8
# ufw deny in from ::1
- Ensure ufw outbound connections are configured (Manual)
Run the following command and verify all rules for new outbound connections match site policy:
# ufw status numbered
Configure ufw in accordance with site policy. The following command will implement a policy to allow all outbound connections on all interfaces:
# ufw allow out on all
- Ensure ufw firewall rules exist for all open ports (Automated)
Run the following script to verify a firewall rule exists for all open ports:
#!/usr/bin/env bash
ufw_out="$(ufw status verbose)"
ss -tuln | awk '($5!~/%lo:/ && $5!~/127.0.0.1:/ && $5!~/::1/) {split($5, a, ":"); print a[2]}' | sort | uniq | while read -r lpn; do
! grep -Pq "^\h*$lpn\b" <<< "$ufw_out" && echo "- Port: \"$lpn\" is missing a firewall rule"
done
For each port identified in the audit which does not have a firewall rule, add rule for accepting or denying inbound connections:
Example:
# ufw allow in <port>/<tcp or udp protocol>
- Ensure ufw default deny firewall policy (Automated)
Any port and protocol not explicitly allowed will be blocked. The following rules should be considered before applying the default deny.
ufw allow git
ufw allow in http
ufw allow out http <- required for apt to connect to repository
ufw allow in https
ufw allow out https
ufw allow out 53
ufw logging on
Run the following command and verify that the default policy for incoming, outgoing, and routed directions is deny, reject, or disabled:
# ufw status verbose | grep Default:
Example output:
Default: deny (incoming), deny (outgoing), disabled (routed)
Run the following commands to implement a default deny policy:
# ufw default deny incoming
# ufw default deny outgoing
# ufw default deny routed
If Uncomplicated Firewall (UFW) or iptables are being used in your environment, please follow the guidance in their respective section and pass-over the guidance in this section.
The following will implement the firewall rules of this section and open ICMP, IGMP, and port 22(ssh) from anywhere. Opening the ports for ICMP, IGMP, and port 22(ssh) needs to be updated in accordance with local site policy. Allow port 22(ssh) needs to be updated to only allow systems requiring ssh connectivity to connect, as per site policy.
Save the script below as /etc/nftables.rules
#!/sbin/nft -f
# This nftables.rules config should be saved as /etc/nftables.rules
# flush nftables rulesset
flush ruleset
# Load nftables ruleset
# nftables config with inet table named filter
table inet filter {
# Base chain for input hook named input (Filters inbound network packets)
chain input {
type filter hook input priority 0; policy drop;
# Ensure loopback traffic is configured
iif "lo" accept
ip saddr 127.0.0.0/8 counter packets 0 bytes 0 drop
ip6 saddr ::1 counter packets 0 bytes 0 drop
# Ensure established connections are configured
ip protocol tcp ct state established accept
ip protocol udp ct state established accept
ip protocol icmp ct state established accept
# Accept port 22(SSH) traffic from anywhere
tcp dport ssh accept
# Accept ICMP and IGMP from anywhere
icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept
icmp type { destination-unreachable, router-advertisement, router-solicitation, time-exceeded, parameter-problem } accept
ip protocol igmp accept
}
# Base chain for hook forward named forward (Filters forwarded network packets)
chain forward {
type filter hook forward priority 0; policy drop;
}
# Base chain for hook output named output (Filters outbount network packets)
chain output {
type filter hook output priority 0; policy drop;
# Ensure outbound and established connections are configured
ip protocol tcp ct state established,related,new accept
ip protocol udp ct state established,related,new accept
ip protocol icmp ct state established,related,new accept
}
}
Run the following command to load the file into nftables
# nft -f /etc/nftables.rules
All changes in the nftables subsections are temporary.
To make these changes permanent:
Run the following command to create the nftables.rules
file
nft list ruleset > /etc/nftables.rules
Add the following line to /etc/nftables.conf
include "/etc/nftables.rules"
- Ensure nftables is installed (Automated)
Run the following command to verify that nftables
is installed:
# dpkg-query -s nftables | grep 'Status: install ok installed'
Status: install ok installed
Run the following command to install nftables
:
# apt install nftables
- Ensure ufw is uninstalled or disabled with nftables (Automated)
Run the following commands to verify that ufw
is either not installed or disabled. Only one of the following needs to pass.
Run the following command to verify that ufw
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' ufw
ufw unknown ok not-installed not-installed
Run the following command to verify ufw
is disabled:
# ufw status
Status: inactive
Run the following commands to verify that the ufw
service is masked:
# systemctl is-enabled ufw
masked
- Ensure iptables are flushed with nftables (Manual)
Run the following commands to ensure no iptables rules exist
For iptables
:
# iptables -L
No rules should be returned
For ip6tables
:
# ip6tables -L
No rules should be returned
Run the following commands to flush iptables:
For iptables
:
# iptables -F
For ip6tables
:
# ip6tables -F
- Ensure a nftables table exists (Automated)
Run the following command to verify that a nftables
table exists:
# nft list tables
Return should include a list of nftables
:
Example:
table inet filter
Run the following command to create a table in nftables
# nft create table inet <table name>
Example:
# nft create table inet filter
- Ensure nftables base chains exist (Automated)
If configuring nftables over ssh, creating a base chain with a policy of drop will cause loss of connectivity.
Ensure that a rule allowing ssh has been added to the base chain prior to setting the base chain's policy to drop
Run the following commands and verify that base chains exist for INPUT
.
# nft list ruleset | grep 'hook input'
type filter hook input priority 0;
Run the following commands and verify that base chains exist for FORWARD
.
# nft list ruleset | grep 'hook forward'
type filter hook forward priority 0;
Run the following commands and verify that base chains exist for OUTPUT
.
# nft list ruleset | grep 'hook output'
type filter hook output priority 0;
Run the following command to create the base chains:
# nft create chain inet <table name> <base chain name> { type filter hook
<(input|forward|output)> priority 0 \; }
Example:
# nft create chain inet filter input { type filter hook input priority 0 \; }
# nft create chain inet filter forward { type filter hook forward priority 0
\; }
# nft create chain inet filter output { type filter hook output priority 0 \;
}
- Ensure nftables loopback traffic is configured (Automated)
Run the following commands to verify that the loopback interface is configured:
# nft list ruleset | awk '/hook input/,/}/' | grep 'iif "lo" accept'
iif "lo" accept
# nft list ruleset | awk '/hook input/,/}/' | grep 'ip saddr'
ip saddr 127.0.0.0/8 counter packets 0 bytes 0 drop
IF IPv6 is enabled on the system:
Run the following command to verify that the IPv6 loopback interface is configured:
# nft list ruleset | awk '/hook input/,/}/' | grep 'ip6 saddr'
ip6 saddr ::1 counter packets 0 bytes 0 drop
Run the following commands to implement the loopback rules:
# nft add rule inet filter input iif lo accept
# nft create rule inet filter input ip saddr 127.0.0.0/8 counter drop
IF IPv6 is enabled on the system:
Run the following command to implement the IPv6 loopback rule:
# nft add rule inet filter input ip6 saddr ::1 counter drop
- Ensure nftables outbound and established connections are configured (Manual)
Run the following commands and verify all rules for established incoming connections match site policy: site policy:
# nft list ruleset | awk '/hook input/,/}/' | grep -E 'ip protocol
(tcp|udp|icmp) ct state'
Output should be similar to:
ip protocol tcp ct state established accept
ip protocol udp ct state established accept
ip protocol icmp ct state established accept
Run the folllowing command and verify all rules for new and established outbound connections match site policy
# nft list ruleset | awk '/hook output/,/}/' | grep -E 'ip protocol
(tcp|udp|icmp) ct state'
Output should be similar to:
ip protocol tcp ct state established,related,new accept
ip protocol udp ct state established,related,new accept
ip protocol icmp ct state established,related,new accept
Configure nftables in accordance with site policy. The following commands will implement a policy to allow all outbound connections and all established connections:
# nft add rule inet filter input ip protocol tcp ct state established accept
# nft add rule inet filter input ip protocol udp ct state established accept
# nft add rule inet filter input ip protocol icmp ct state established accept
# nft add rule inet filter output ip protocol tcp ct state new,related,established accept
# nft add rule inet filter output ip protocol udp ct state new,related,established accept
# nft add rule inet filter output ip protocol icmp ct state new,related,established accept
- Ensure nftables default deny firewall policy (Automated)
Run the following commands and verify that base chains contain a policy of DROP
.
# nft list ruleset | grep 'hook input'
type filter hook input priority 0; policy drop;
# nft list ruleset | grep 'hook forward'
type filter hook forward priority 0; policy drop;
# nft list ruleset | grep 'hook output'
type filter hook output priority 0; policy drop;
Run the following command for the base chains with the input, forward, and output hooks to implement a default DROP policy:
# nft chain <table family> <table name> <chain name> { policy drop \; }
Example:
# nft chain inet filter input { policy drop \; }
# nft chain inet filter forward { policy drop \; }
# nft chain inet filter output { policy drop \; }
- Ensure nftables service is enabled (Automated)
Run the following command and verify that the nftables service is enabled:
# systemctl is-enabled nftables
enabled
Run the following command to enable the nftables service:
# systemctl enable nftables
- Ensure nftables rules are permanent (Automated)
Run the following commands to verify that input, forward, and output base chains are configured to be applied to a nftables ruleset on boot:
Run the following command to verify the input base chain:
# [ -n "$(grep -E '^\s*include' /etc/nftables.conf)" ] && awk '/hook input/,/}/' $(awk '$1 ~ /^\s*include/ { gsub("\"","",$2);print $2 }' /etc/nftables.conf)
Output should be similar to:
type filter hook input priority 0; policy drop;
# Ensure loopback traffic is configured
iif "lo" accept
ip saddr 127.0.0.0/8 counter packets 0 bytes 0 drop
ip6 saddr ::1 counter packets 0 bytes 0 drop
# Ensure established connections are configured
ip protocol tcp ct state established accept
ip protocol udp ct state established accept
ip protocol icmp ct state established accept
# Accept port 22(SSH) traffic from anywhere
tcp dport ssh accept
# Accept ICMP and IGMP from anywhere
icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd�neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept
Review the input base chain to ensure that it follows local site policy Run the following command to verify the forward base chain:
# [ -n "$(grep -E '^\s*include' /etc/nftables.conf)" ] && awk '/hook forward/,/}/' $(awk '$1 ~ /^\s*include/ { gsub("\"","",$2);print $2 }' /etc/nftables.conf)
Output should be similar to:
# Base chain for hook forward named forward (Filters forwarded network packets)
chain forward {
type filter hook forward priority 0; policy drop;
}
Review the forward base chain to ensure that it follows local site policy.
Run the following command to verify the forward base chain:
# [ -n "$(grep -E '^\s*include' /etc/nftables.conf)" ] && awk '/hook output/,/}/' $(awk '$1 ~ /^\s*include/ { gsub("\"","",$2);print $2 }' /etc/nftables.conf)
Output should be similar to:
# Base chain for hook output named output (Filters outbound network packets)
chain output {
type filter hook output priority 0; policy drop;
# Ensure outbound and established connections are configured
ip protocol tcp ct state established,related,new accept
ip protocol tcp ct state established,related,new accept
ip protocol udp ct state established,related,new accept
ip protocol icmp ct state established,related,new accept
}
Review the output base chain to ensure that it follows local site policy.
Edit the /etc/nftables.conf
file and un-comment or add a line with include <Absolute path to nftables rules file>
for each nftables file you want included in the nftables ruleset on boot
Example:
# vi /etc/nftables.conf
Add the line:
include "/etc/nftables.rules"
If Uncomplicated Firewall (UFW) or nftables are being used in your environment, please follow the guidance in their respective section and pass-over the guidance in this section.
- Ensure iptables packages are installed (Automated)
Run the following command to verify that iptables
and iptables-persistent
are installed:
# apt list iptables iptables-persistent | grep installed
iptables-persistent/<version> [installed,automatic]
iptables/<version> [installed,automatic]
Run the following command to install iptables
and iptables-persistent
# apt install iptables iptables-persistent
- Ensure nftables is not installed with iptables (Automated)
Run the following commend to verify that nftables
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' nftables
nftables unknown ok not-installed not-installed
Run the following command to remove nftables
:
# apt purge nftables
- Ensure ufw is uninstalled or disabled with iptables (Automated)
Run the following commands to verify that ufw
is either not installed or disabled. Only one of the following needs to pass.
Run the following command to verify that ufw
is not installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n' ufw
ufw unknown ok not-installed not-installed
Run the following command to verify ufw
is disabled:
# ufw status
Status: inactive
Run the following commands to verify that the ufw
service is masked:
# systemctl is-enabled ufw
masked
Run one of the following commands to either remove ufw
or stop and mask ufw
Run the following command to remove ufw
:
# apt purge ufw
OR
Run the following commands to disable ufw
:
# ufw disable
# systemctl stop ufw
# systemctl mask ufw
Note: This section broadly assumes starting with an empty IPtables firewall ruleset (established by flushing the rules with iptables -F). Remediation steps included only affect the live system, you will also need to configure your default firewall configuration to apply on boot. Configuration of a live systems firewall directly over a remote connection will often result in being locked out. It is advised to have a known good firewall configuration set to run on boot and to configure an entire firewall structure in a script that is then run and tested before saving to boot.
The following script will implement the firewall rules of this section and open port 22(ssh) from anywhere:
#!/bin/bash
# Flush IPtables rules
iptables -F
# Ensure default deny firewall policy
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP
# Ensure loopback traffic is configured
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A INPUT -s 127.0.0.0/8 -j DROP
# Ensure outbound and established connections are configured
iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
iptables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT
# Open inbound ssh(tcp port 22) connections
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
- Ensure iptables default deny firewall policy (Automated)
Run the following command and verify that the policy for the INPUT
, OUTPUT
, and FORWARD
chains is DROP
or REJECT
:
# iptables -L
Chain INPUT (policy DROP)
Chain FORWARD (policy DROP)
Chain OUTPUT (policy DROP)
Run the following commands to implement a default DROP
policy:
# iptables -P INPUT DROP
# iptables -P OUTPUT DROP
# iptables -P FORWARD DROP
- Ensure iptables loopback traffic is configured (Automated)
Run the following commands and verify output includes the listed rules in order (packet and byte counts may differ):
# iptables -L INPUT -v -n
Chain INPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- * * 127.0.0.0/8 0.0.0.0/0
# iptables -L OUTPUT -v -n
Chain OUTPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0
Run the following commands to implement the loopback rules:
# iptables -A INPUT -i lo -j ACCEPT
# iptables -A OUTPUT -o lo -j ACCEPT
# iptables -A INPUT -s 127.0.0.0/8 -j DROP
- Ensure iptables outbound and established connections are configured (Manual)
Run the following command and verify all rules for new outbound, and established connections match site policy:
# iptables -L -v -n
Configure iptables in accordance with site policy. The following commands will implement a policy to allow all outbound connections and all established connections:
# iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
# iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
# iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
# iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
# iptables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
# iptables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT
- Ensure iptables firewall rules exist for all open ports (Automated)
Run the following command to determine open ports:
# ss -4tuln
Netid State Recv-Q Send-Q Local Address:Port Peer
Address:Port
udp UNCONN 0 0 *:68
*:*
udp UNCONN 0 0 *:123
*:*
tcp LISTEN 0 128 *:22
*:*
Run the following command to determine firewall rules:
# iptables -L INPUT -v -n
Chain INPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source
destination
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- * * 127.0.0.0/8 0.0.0.0/0
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0
tcp dpt:22 state NEW
Verify all open ports listening on non-localhost addresses have at least one firewall rule.The last line identified by the "tcp dpt:22 state NEW" identifies it as a firewall rule for new connections on tcp port 22.
For each port identified in the audit which does not have a firewall rule establish a proper rule for accepting inbound connections:
# iptables -A INPUT -p <protocol> --dport <port> -m state --state NEW -j
ACCEPT
If IPv6 in enabled on the system, the ip6tables should be configured
The following script will implement the firewall rules of this section and open port 22(ssh) from anywhere:
#!/bin/bash
# Flush ip6tables rules
ip6tables -F
# Ensure default deny firewall policy
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
ip6tables -P FORWARD DROP
# Ensure loopback traffic is configured
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT
ip6tables -A INPUT -s ::1 -j DROP
# Ensure outbound and established connections are configured
ip6tables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT
# Open inbound ssh(tcp port 22) connections
ip6tables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
- Ensure ip6tables default deny firewall policy (Automated)
Run the following command and verify that the policy for the INPUT
, OUTPUT
, and FORWARD
chains is DROP
or REJECT
:
# ip6tables -L
Chain INPUT (policy DROP)
Chain FORWARD (policy DROP)
Chain OUTPUT (policy DROP)
OR verify IPv6 is disabled:
Run the following script. Output will confirm if IPv6 is disabled on the system.
#!/usr/bin/bash
output=""
grubfile="$(find -L /boot -name 'grub.cfg' -type f)"
[ -f "$grubfile" ] && ! grep "^\s*linux" "$grubfile" | grep -vq ipv6.disable=1 && output="ipv6 disabled in grub config"
grep -Eqs "^\s*net\.ipv6\.conf\.all\.disable_ipv6\s*=\s*1\b" /etc/sysctl.conf /etc/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf \
/run/sysctl.d/*.conf && grep -Eqs
"^\s*net\.ipv6\.conf\.default\.disable_ipv6\s*=\s*1\b" /etc/sysctl.conf /etc/sysctl.d/*.conf \
/usr/lib/sysctl.d/*.conf /run/sysctl.d/*.conf && sysctl
net.ipv6.conf.all.disable_ipv6 | grep -Eq \
"^\s*net\.ipv6\.conf\.all\.disable_ipv6\s*=\s*1\b" && sysctl
net.ipv6.conf.default.disable_ipv6 | \
grep -Eq "^\s*net\.ipv6\.conf\.default\.disable_ipv6\s*=\s*1\b" &&
output="ipv6 disabled in sysctl config"
[ -n "$output" ] && echo -e "\n$output" || echo -e "\n*** IPv6 is enabled on the system ***"
Run the following commands to implement a default DROP policy:
# ip6tables -P INPUT DROP
# ip6tables -P OUTPUT DROP
# ip6tables -P FORWARD DROP
- Ensure ip6tables loopback traffic is configured (Automated)
Run the following commands and verify output includes the listed rules in order (packet and byte counts may differ):
# ip6tables -L INPUT -v -n
Chain INPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all lo * ::/0 ::/0
0 0 DROP all * * ::1 ::/0
# ip6tables -L OUTPUT -v -n
Chain OUTPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all * lo ::/0 ::/0
OR verify IPv6 is disabled:
Run the following script. Output will confirm if IPv6 is disabled on the system.
#!/usr/bin/bash
output=""
grubfile="$(find -L /boot -name 'grub.cfg' -type f)"
[ -f "$grubfile" ] && ! grep "^\s*linux" "$grubfile" | grep -vq ipv6.disable=1 && output="ipv6 disabled in grub config"
grep -Eqs "^\s*net\.ipv6\.conf\.all\.disable_ipv6\s*=\s*1\b" /etc/sysctl.conf /etc/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf \
/run/sysctl.d/*.conf && grep -Eqs
"^\s*net\.ipv6\.conf\.default\.disable_ipv6\s*=\s*1\b" /etc/sysctl.conf /etc/sysctl.d/*.conf \
/usr/lib/sysctl.d/*.conf /run/sysctl.d/*.conf && sysctl
net.ipv6.conf.all.disable_ipv6 | grep -Eq \
"^\s*net\.ipv6\.conf\.all\.disable_ipv6\s*=\s*1\b" && sysctl
net.ipv6.conf.default.disable_ipv6 | \
grep -Eq "^\s*net\.ipv6\.conf\.default\.disable_ipv6\s*=\s*1\b" &&
output="ipv6 disabled in sysctl config"
[ -n "$output" ] && echo -e "\n$output" || echo -e "\n*** IPv6 is enabled on the system ***"
Run the following commands to implement the loopback rules:
# ip6tables -A INPUT -i lo -j ACCEPT
# ip6tables -A OUTPUT -o lo -j ACCEPT
# ip6tables -A INPUT -s ::1 -j DROP
- Ensure ip6tables outbound and established connections are configured (Manual)
Run the following command and verify all rules for new outbound, and established connections match site policy:
# ip6tables -L -v -n
OR verify IPv6 is disabled:
Run the following script. Output will confirm if IPv6 is disabled on the system.
#!/usr/bin/bash
output=""
grubfile="$(find -L /boot -name 'grub.cfg' -type f)"
[ -f "$grubfile" ] && ! grep "^\s*linux" "$grubfile" | grep -vq ipv6.disable=1 && output="ipv6 disabled in grub config"
grep -Eqs "^\s*net\.ipv6\.conf\.all\.disable_ipv6\s*=\s*1\b" /etc/sysctl.conf /etc/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf \
/run/sysctl.d/*.conf && grep -Eqs
"^\s*net\.ipv6\.conf\.default\.disable_ipv6\s*=\s*1\b" /etc/sysctl.conf /etc/sysctl.d/*.conf \
/usr/lib/sysctl.d/*.conf /run/sysctl.d/*.conf && sysctl
net.ipv6.conf.all.disable_ipv6 | grep -Eq \
"^\s*net\.ipv6\.conf\.all\.disable_ipv6\s*=\s*1\b" && sysctl
net.ipv6.conf.default.disable_ipv6 | \
grep -Eq "^\s*net\.ipv6\.conf\.default\.disable_ipv6\s*=\s*1\b" &&
output="ipv6 disabled in sysctl config"
[ -n "$output" ] && echo -e "\n$output" || echo -e "\n*** IPv6 is enabled on the system ***"
Configure iptables in accordance with site policy. The following commands will implement a policy to allow all outbound connections and all established connections:
# ip6tables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
# ip6tables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
# ip6tables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
# ip6tables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
# ip6tables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
# ip6tables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT
- Ensure ip6tables firewall rules exist for all open ports (Automated)
Run the following command to determine open ports:
# ss -6tuln
Netid State Recv-Q Send-Q Local Address:Port Peer
Address:Port
udp UNCONN 0 0 ::1:123
:::*
udp UNCONN 0 0 :::123
:::*
tcp LISTEN 0 128 :::22
:::*
tcp LISTEN 0 20 ::1:25
:::*
Run the following command to determine firewall rules:
# ip6tables -L INPUT -v -n
Chain INPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all lo * ::/0 ::/0
0 0 DROP all * * ::1 ::/0
0 0 ACCEPT tcp * * ::/0 ::/0
tcp dpt:22 state NEW
Verify all open ports listening on non-localhost addresses have at least one firewall rule. The last line identified by the "tcp dpt:22 state NEW" identifies it as a firewall rule for new connections on tcp port 22
OR verify IPv6 is disabled:
Run the following script. Output will confirm if IPv6 is disabled on the system.
#!/usr/bin/bash
output=""
grubfile="$(find -L /boot -name 'grub.cfg' -type f)"
[ -f "$grubfile" ] && ! grep "^\s*linux" "$grubfile" | grep -vq ipv6.disable=1 && output="ipv6 disabled in grub config"
grep -Eqs "^\s*net\.ipv6\.conf\.all\.disable_ipv6\s*=\s*1\b" /etc/sysctl.conf /etc/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf \
/run/sysctl.d/*.conf && grep -Eqs
"^\s*net\.ipv6\.conf\.default\.disable_ipv6\s*=\s*1\b" /etc/sysctl.conf /etc/sysctl.d/*.conf \
/usr/lib/sysctl.d/*.conf /run/sysctl.d/*.conf && sysctl
net.ipv6.conf.all.disable_ipv6 | grep -Eq \
"^\s*net\.ipv6\.conf\.all\.disable_ipv6\s*=\s*1\b" && sysctl
net.ipv6.conf.default.disable_ipv6 | \
grep -Eq "^\s*net\.ipv6\.conf\.default\.disable_ipv6\s*=\s*1\b" &&
output="ipv6 disabled in sysctl config"
[ -n "$output" ] && echo -e "\n$output" || echo -e "\n*** IPv6 is enabled on the system ***"
For each port identified in the audit which does not have a firewall rule establish a proper rule for accepting inbound connections:
# ip6tables -A INPUT -p <protocol> --dport <port> -m state --state NEW -j ACCEPT
-
Ensure auditing is enabled
default event logs will be logged to
/var/log/audit/audit.log
which will be configured in
/etc/audit/auditd.conf
.The following types of audit rules can be specified:
- Control rules: Configuration of the auditing system.
- File system rules: Allow the auditing of access to a particular file or a directory. Also known as file watches.
- System call rules: Allow logging of system calls that any specified program makes.
Audit rules can be set:
- On the command line using the auditctl utility. These rules are not persistent across reboots.
- In
/etc/audit/audit.rules
. These rules have to be merged and loaded before they are active.
Run the following command and verify and audispd-plugins are installed:
# dpkg-query -W -f='${binary:Package}\t${Status}\t${db:Status-Status}\n'
auditd audispd-plugins audispd-plugins install ok installed installed auditd install ok installed installed
If plugin is not installed run the following command
# apt install auditd audispd-plugins
Ensure auditd service is enabled and active (Automated)
Run the following command to verify
auditd
is enabled:# systemctl is-enabled auditd
enabled
Verify result is "enabled". Run the following command to verify auditd is active:
# systemctl is-active auditd
active
Verify result is active
Run the following command to enable and start auditd:
# systemctl --now enable auditd
Ensure auditing for processes that start prior to auditd is enabled (Automated)
Run the following command:
# find /boot -type f -name 'grub.cfg' -exec grep -Ph -- '^\h*linux' {} + | grep -v 'audit=1'
Nothing should be returned.
Remediation:
Edit
/etc/default/grub
and add audit=1 to GRUB_CMDLINE_LINUX: Example:GRUB_CMDLINE_LINUX="audit=1"
Run the following command to update the grub2 configuration:
# update-grub
Ensure audit_backlog_limit is sufficient (Automated)
Run the following command and verify the audit_backlog_limit= parameter is set:
# find /boot -type f -name 'grub.cfg' -exec grep -Ph -- '^\h*linux' {} + | grep -Pv 'audit_backlog_limit=\d+\b'
Nothing should be returned.
Remediation:
Edit
/etc/default/
grub and addaudit_backlog_limit=N
to GRUB_CMDLINE_LINUX. The recommended size forN
is8192
or larger. Example:GRUB_CMDLINE_LINUX="audit_backlog_limit=8192"
Run the following command to update the grub2 configuration:
# update-grub
Default Value: if
audit_backlog_limit
is not set, the system defaults toaudit_backlog_limit=64
-
Configure Data Retention
Ensure audit log storage size is configured (Automated)
When auditing, it is important to carefully configure the storage requirements for audit logs. By default, auditd will max out the log files at 5MB and retain only 4 copies of them. Older versions will be deleted. It is possible on a system that the 20 MBs of audit logs may fill up the system causing loss of audit data. While the recommendations here provide guidance, check your site policy for audit storage requirements
Audit: Run the following command and ensure output is in compliance with site policy:
# grep -Po -- '^\h*max_log_file\h*=\h*\d+\b' /etc/audit/auditd.conf max_log_file = <MB>
Remediation: Set the following parameter in
/etc/audit/auditd.conf
in accordance with site policy:max_log_file = <MB>
Default Value: max_log_file = 8
Additional Information:
The max_log_file parameter is measured in megabytes. Other methods of log rotation may be appropriate based on site policy. One example is time-based rotation strategies which don't have native support in auditd configurations. Manual audit of custom configurations should be evaluated for effectiveness and completeness
Ensure audit logs are not automatically deleted(Automated)
Description:
The max_log_file_action setting determines how to handle the audit log file reaching the max file size. A value of keep_logs will rotate the logs but never delete old logs.Rationale:
In high security contexts, the benefits of maintaining a long audit history exceed the cost of storing the audit history.Audit: Run the following command and verify output matches:
# grep max_log_file_action /etc/audit/auditd.conf max_log_file_action = keep_logs
Remediation:
Set the following parameter in/etc/audit/auditd.conf
:max_log_file_action = keep_logs
Ensure system is disabled when audit logs are full(Automated)
Description:
The auditd daemon can be configured to halt the system when the audit logs are full. The admin_space_left_action parameter tells the system what action to take when the system has detected that it is low on disk space. Valid values are ignore, syslog, suspend, single, and halt.
- ignore, the audit daemon does nothing
- Syslog, the audit daemon will issue a warning to syslog
- Suspend, the audit daemon will stop writing records to the disk
- single, the audit daemon will put the computer system in single user mode
- halt, the audit daemon will shutdown the system
Rationale:
In high security contexts, the risk of detecting unauthorized access or nonrepudiation exceeds the benefit of the system's availability.
Impact: If the admin_space_left_action parameter is set to halt the audit daemon will shutdown the system when the disk partition containing the audit logs becomes full.
Audit: Run the following commands and verify output matches:
# grep space_left_action /etc/audit/auditd.conf space_left_action = email # grep action_mail_acct /etc/audit/auditd.conf action_mail_acct = root
Run the following command and verify the output is either halt or single:
# grep -E 'admin_space_left_action\s*=\s*(halt|single)' /etc/audit/auditd.conf admin_space_left_action = <halt|single>
Remediation: Set the following parameters in
/etc/audit/auditd.conf
:space_left_action = email action_mail_acct = root
set admin_space_left_action to either halt or single in /etc/audit/auditd.conf. Example:
admin_space_left_action = halt
Configure auditd rules
The Audit system operates on a set of rules that define what is to be captured in the log files. The following types of Audit rules can be specified:
- Control rules: Allow the Audit system's behavior and some of its configuration to be modified.
- File system rules: Allow the auditing of access to a particular file or a directory. (Also known as file watches)
- System call rules: Allow logging of system calls that any specified program makes.
Audit rules can be set:
- on the command line using the auditctl utility. Note that these rules are not persistent across reboots.
- in a file ending in .rules in the /etc/audit/audit.d/ directory.
-
Configure auditd rules
Ensure changes to system administration scope (sudoers)is collected (Automated)
Description:
Monitor scope changes for system administrators. If the system has been properly configured to force system administrators to log in as themselves first and then use the sudo command to execute privileged commands, it is possible to monitor changes in scope. The file
/etc/sudoers
, or files in/etc/sudoers.d
, will be written to when the file(s) or related attributes have changed. The audit records will be tagged with the identifier "scope".Rationale:
Changes in the
/etc/sudoers
and/etc/sudoers.d
files can indicate that an unauthorized change has been made to the scope of system administrator activity.Audit:
On disk configuration
Run the following command to check the on disk rules:
# awk '/^ *-w/ \ &&/\/etc\/sudoers/ \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules
Verify the output matches:
-w /etc/sudoers -p wa -k scope -w /etc/sudoers.d -p wa -k scope
Running configuration
Run the following command to check loaded rules:
# auditctl -l | awk '/^ *-w/ \ &&/\/etc\/sudoers/ \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)'
Verify the output matches:
-w /etc/sudoers -p wa -k scope -w /etc/sudoers.d -p wa -k scope
Remediation:
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor scope changes for system administrators. Example:# printf " -w /etc/sudoers -p wa -k scope -w /etc/sudoers.d -p wa -k scope " >> /etc/audit/rules.d/50-scope.rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Additional Information:
Potential reboot required
If the auditing configuration is locked (-e 2), then augenrules will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration.
System call structure
For performance (man 7 audit.rules) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page.
Ensure actions as another user are always logged (Automated)
Description:
sudo
provides users with temporary elevated privileges to perform operations, either as the superuser or another user.Rationale: Creating an audit log of users with temporary elevated privileges and the operation(s) they performed is essential to reporting. Administrators will want to correlate the events written to the audit trail with the records written to
sudo
's logfile to verify if unauthorized commands have been executed.Audit:
64 Bit systems
On disk configuration
Run the following command to check the on disk rules:
# awk '/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&(/ -F *auid!=unset/||/ -F *auid!=-1/||/ -F *auid!=4294967295/) \ &&(/ -C *euid!=uid/||/ -C *uid!=euid/) \ &&/ -S *execve/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules
Verify the output matches:
-a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation -a always,exit -F arch=b32 -C euid!=uid -F auid!=unset -S execve -k user_emulation
Running configuration
Run the following command to check loaded rules:
# auditctl -l | awk '/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&(/ -F *auid!=unset/||/ -F *auid!=-1/||/ -F *auid!=4294967295/) \ &&(/ -C *euid!=uid/||/ -C *uid!=euid/) \ &&/ -S *execve/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)'
Verify the output matches:
-a always,exit -F arch=b64 -S execve -C uid!=euid -F auid!=-1 -F key=user_emulation -a always,exit -F arch=b32 -S execve -C uid!=euid -F auid!=-1 -F key=user_emulation
32 Bit systems
Follow the same procedures as for 64 bit systems and ignore any entries with b64.
Remediation:
Create audit rules
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor elevated privileges.64 Bit systems
Example:
# printf " -a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation -a always,exit -F arch=b32 -C euid!=uid -F auid!=unset -S execve -k user_emulation " >> /etc/audit/rules.d/50-user_emulation.rules
Load audit rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
32 Bit systems
Follow the same procedures as for 64 bit systems and ignore any entries with b64.
Additional Information:
Potential reboot required
If the auditing configuration is locked (-e 2), then augenrules will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration.
System call structure
For performance (man 7 audit.rules) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page.
Ensure events that modify the sudo log file are collected (Automated)
Description:
Monitor the sudo log file. If the system has been properly configured to disable the use of the su command and force all administrators to have to log in first and then use sudo to execute privileged commands, then all administrator commands will be logged to /var/log/sudo.log . Any time a command is executed, an audit event will be triggered as the /var/log/sudo.log file will be opened for write and the executed administration command will be written to the log.
Rationale:
Changes in
/var/log/sudo.log
indicate that an administrator has executed a command or the log file itself has been tampered with. Administrators will want to correlate the events written to the audit trail with the records written to/var/log/sudo.log
to verify if unauthorized commands have been executed.Audit:
On disk configuration Run the following command to check the on disk rules:# { SUDO_LOG_FILE_ESCAPED=$(grep -r logfile /etc/sudoers* | sed -e 's/.*logfile=//;s/,? .*//' -e 's/"//g' -e 's|/|\\/|g') [ -n "${SUDO_LOG_FILE_ESCAPED}" ] && awk "/^ *-w/ \ &&/"${SUDO_LOG_FILE_ESCAPED}"/ \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)" /etc/audit/rules.d/*.rules \ || printf "ERROR: Variable 'SUDO_LOG_FILE_ESCAPED' is unset.\n" }
Verify output of matches:
-w /var/log/sudo.log -p wa -k sudo_log_file
Running configuration
Run the following command to check loaded rules:
# { SUDO_LOG_FILE_ESCAPED=$(grep -r logfile /etc/sudoers* | sed -e 's/.*logfile=//;s/,? .*//' -e 's/"//g' -e 's|/|\\/|g') [ -n "${SUDO_LOG_FILE_ESCAPED}" ] && auditctl -l | awk "/^ *-w/ \ &&/"${SUDO_LOG_FILE_ESCAPED}"/ \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)" \ || printf "ERROR: Variable 'SUDO_LOG_FILE_ESCAPED' is unset.\n" }
Verify output matches:
-w /var/log/sudo.log -p wa -k sudo_log_file
Remediation:
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in .rules extension, with the relevant rules to monitor events that modify the sudo log file. Example:# { SUDO_LOG_FILE=$(grep -r logfile /etc/sudoers* | sed -e 's/.*logfile=//;s/,? .*//' -e 's/"//g') [ -n "${SUDO_LOG_FILE}" ] && printf " -w ${SUDO_LOG_FILE} -p wa -k sudo_log_file " >> /etc/audit/rules.d/50-sudo.rules || printf "ERROR: Variable 'SUDO_LOG_FILE_ESCAPED' is unset.\n" }
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Additional Information:
Potential reboot required
If the auditing configuration is locked (
-e 2
), thenaugenrules
will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration.System call structure
For performance (
man 7 audit.rules
) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page.Ensure events that modify date and time information are collected (Automated)
Description:
Capture events where the system date and/or time has been modified. The parameters in this section are set to determine if the;
adjtimex
- tune kernel clocksettimeofday
- set time usingtimeval
andtimezone
structuresstime
- using seconds since 1/1/1970clock_settime
- allows for the setting of several internal clocks and timers
system calls have been executed. Further, ensure to write an audit record to the configured audit log file upon exit, tagging the records with a unique identifier such as "time-change".
On disk configuration Run the following command to check the on disk rules:
# { awk '/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&/ -S/ \ &&(/adjtimex/ \ ||/settimeofday/ \ ||/clock_settime/ ) \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules awk '/^ *-w/ \ &&/\/etc\/localtime/ \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules }
Verify output of matches:
-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k timechange -a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -k timechange -w /etc/localtime -p wa -k time-change
Running configuration
Run the following command to check loaded rules:
# { auditctl -l | awk '/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&/ -S/ \ &&(/adjtimex/ \ ||/settimeofday/ \ ||/clock_settime/ ) \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' auditctl -l | awk '/^ *-w/ \ &&/\/etc\/localtime/ \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' }
Verify the output includes:
a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -F key=time-change -a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -F key=time-change -w /etc/localtime -p wa -k time-change
Remediation:
Create audit rules Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor events that modify date and time information.64 Bit systems
Example:
# printf " -a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k timechange -a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -k timechange -w /etc/localtime -p wa -k time-change " >> /etc/audit/rules.d/50-time-change.rules
Load audit rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Ensure events that modify the system's network environment are collected (Automated)
Description:
Record changes to network environment files or system calls. The below parameters monitors the following system calls, and wr
- sethostname - set the systems host name
- setdomainname - set the systems domain name
The files being monitored are:
/etc/issue
and/etc/issue.net
- messages displayed pre-login/etc/hosts
- file containing host names and associated IP addresses/etc/networks
- symbolic names for networks/etc/network/
- directory containing network interface scripts and configurations files
Rationale:
Monitoring
sethostname
andsetdomainname
will identify potential unauthorized changes to host and domainname of a system. The changing of these names could potentially break security parameters that are set based on those names. The/etc/hosts
file is monitored for changes that can indicate an unauthorized intruder is trying to change machine associations with IP addresses and trick users and processes into connecting to unintended machines. Monitoring/etc/issue
and/etc/issue.net
is important, as intruders could put disinformation into those files and trick users into providing information to the intruder. Monitoring/etc/network
is important as it can show if network interfaces or scripts are being modified in a way that can lead to the machine becoming unavailable or compromised. All audit records should have a relevant tag associated with them.
Audit:
64 Bit systems
On disk configuration
Run the following commands to check the on disk rules:
# awk '/^ *-a *always,exit/ \ &&/ -F *arch=b(32|64)/ \ &&/ -S/ \ &&(/sethostname/ \ ||/setdomainname/) \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules # awk '/^ *-w/ \ &&(/\/etc\/issue/ \ ||/\/etc\/issue.net/ \ ||/\/etc\/hosts/ \ ||/\/etc\/network/) \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules
Verify the output matches:
-a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale -a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale -w /etc/issue -p wa -k system-locale -w /etc/issue.net -p wa -k system-locale -w /etc/hosts -p wa -k system-locale -w /etc/networks -p wa -k system-locale
Running configuration
Run the following command to check loaded rules:# auditctl -l | awk '/^ *-a *always,exit/ \ &&/ -F *arch=b(32|64)/ \ &&/ -S/ \ &&(/sethostname/ \ ||/setdomainname/) \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' # auditctl -l | awk '/^ *-w/ \ &&(/\/etc\/issue/ \ ||/\/etc\/issue.net/ \ ||/\/etc\/hosts/ \ ||/\/etc\/network/) \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)'
Verify the output includes:
-a always,exit -F arch=b64 -S sethostname,setdomainname -F key=system-locale -a always,exit -F arch=b32 -S sethostname,setdomainname -F key=system-locale -w /etc/issue -p wa -k system-locale -w /etc/issue.net -p wa -k system-locale -w /etc/hosts -p wa -k system-locale -w /etc/networks -p wa -k system-locale -w /etc/network/ -p wa -k system-locale
Remediation:
Create audit rules
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor events that modify the system's network environment.64 Bit systems
Example:
# printf " -a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale -a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale -w /etc/issue -p wa -k system-locale -w /etc/issue.net -p wa -k system-locale -w /etc/hosts -p wa -k system-locale -w /etc/networks -p wa -k system-locale -w /etc/network/ -p wa -k system-locale " >> /etc/audit/rules.d/50-system_local.rules
Load audit rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Ensure use of privileged commands are collected (Automated)
Description:
Monitor privileged programs, those that have the
setuid
and/orsetgid
bit set on execution, to determine if unprivileged users are running these commands.Rationale:
Execution of privileged commands by non-privileged users could be an indication of someone trying to gain unauthorized access to the system.
Impact:
Both the audit and remediation section of this recommendation will traverse all mounted file systems that is not mounted with either
noexec
ornosuid
mount options. If there are large file systems without these mount options, such traversal will be significantly detrimental to the performance of the systemBefore running either the audit or remediation section, inspect the output of the following command to determine exactly which file systems will be traversed:
# findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid"
To exclude a particular file system due to adverse performance impacts, update the audit and remediation sections by adding a sufficiently unique string to the
grep
statement. The above command can be used to test the modified exclusions.Audit:
On disk configuration
Run the following command to check on disk rules:
# for PARTITION in $(findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid" | awk '{print $1}'); do for PRIVILEGED in $(find "${PARTITION}" -xdev -perm /6000 -type f); do grep -qr "${PRIVILEGED}" /etc/audit/rules.d && printf "OK: '${PRIVILEGED}' found in auditing rules.\n" || printf "Warning: '${PRIVILEGED}' not found in on disk configuration.\n" done done
Verify that all output is OK.
Running configuration
Run the following command to check loaded rules:
# { RUNNING=$(auditctl -l) [ -n "${RUNNING}" ] && for PARTITION in $(findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid" | awk '{print $1}'); do for PRIVILEGED in $(find "${PARTITION}" -xdev -perm /6000 -type f); do printf -- "${RUNNING}" | grep -q "${PRIVILEGED}" && printf "OK: '${PRIVILEGED}' found in auditing rules.\n" || printf "Warning: '${PRIVILEGED}' not found in running configuration.\n" done done \ || printf "ERROR: Variable 'RUNNING' is unset.\n" }
Verify that all output is OK.
Special mount points
If there are any special mount points that are not visible by default from
findmnt
as per the above audit, said file systems would have to be manually audited.Remediation:
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor the use of privileged commands. Example:# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) AUDIT_RULE_FILE="/etc/audit/rules.d/50-privileged.rules" NEW_DATA=() for PARTITION in $(findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid" | awk '{print $1}'); do readarray -t DATA < <(find "${PARTITION}" -xdev -perm /6000 -type f | awk -v UID_MIN=${UID_MIN} '{print "-a always,exit -F path=" $1 " -F perm=x -F auid>="UID_MIN" -F auid!=unset -k privileged" }') for ENTRY in "${DATA[@]}"; do NEW_DATA+=("${ENTRY}") done done readarray &> /dev/null -t OLD_DATA < "${AUDIT_RULE_FILE}" COMBINED_DATA=( "${OLD_DATA[@]}" "${NEW_DATA[@]}" ) printf '%s\n' "${COMBINED_DATA[@]}" | sort -u > "${AUDIT_RULE_FILE}" }
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Special mount points
If there are any special mount points that are not visible by default from just scanning /, change the
PARTITION
variable to the appropriate partition and re-run the remediation.
4.1.3.7 Ensure unsuccessful file access attempts are collected (Automated)
Description:
Monitor for unsuccessful attempts to access files. The following parameters are associated with system calls that control files:
- creation -
creat
- opening -
open
,openat
- truncation -
truncate
,ftruncate
An audit log record will only be written if all of the following criteria is met for the user when trying to access a file:
- a non-privileged user (auid>=UID_MIN)
- is not a Daemon event (auid=4294967295/unset/-1)
- if the system call returned EACCES (permission denied) or EPERM (some other permanent error associated with the specific system call)
Rationale:
Failed attempts to open, create or truncate files could be an indication that an individual or process is trying to gain unauthorized access to the system.
Audit:
64 Bit systems
On disk configuration
Run the following command to check the on disk rules:
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && awk "/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&(/ -F *auid!=unset/||/ -F *auid!=-1/||/ -F *auid!=4294967295/) \ &&/ -F *auid>=${UID_MIN}/ \ &&(/ -F *exit=-EACCES/||/ -F *exit=-EPERM/) \ &&/ -S/ \ &&/creat/ \ &&/open/ \ &&/truncate/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)" /etc/audit/rules.d/*.rules \ || printf "ERROR: Variable 'UID_MIN' is unset.\n" }
Verify the output includes:
-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=- EACCES -F auid>=1000 -F auid!=unset -k access -a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=- EPERM -F auid>=1000 -F auid!=unset -k access -a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=- EACCES -F auid>=1000 -F auid!=unset -k access -a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=- EPERM -F auid>=1000 -F auid!=unset -k access
Running configuration
Run the following command to check loaded rules:
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && auditctl -l | awk "/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&(/ -F *auid!=unset/||/ -F *auid!=-1/||/ -F *auid!=4294967295/) \ &&/ -F *auid>=${UID_MIN}/ \ &&(/ -F *exit=-EACCES/||/ -F *exit=-EPERM/) \ &&/ -S/ \ &&/creat/ \ &&/open/ \ &&/truncate/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)" \ || printf "ERROR: Variable 'UID_MIN' is unset.\n" }
Verify the output includes:
-a always,exit -F arch=b64 -S open,truncate,ftruncate,creat,openat -F exit=- EACCES -F auid>=1000 -F auid!=-1 -F key=access -a always,exit -F arch=b64 -S open,truncate,ftruncate,creat,openat -F exit=- EPERM -F auid>=1000 -F auid!=-1 -F key=access -a always,exit -F arch=b32 -S open,truncate,ftruncate,creat,openat -F exit=- EACCES -F auid>=1000 -F auid!=-1 -F key=access -a always,exit -F arch=b32 -S open,truncate,ftruncate,creat,openat -F exit=- EPERM -F auid>=1000 -F auid!=-1 -F key=access
Remediation:
Create audit rules
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor unsuccessful file access attempts.64 Bit systems
Example:
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && printf " -a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=- EACCES -F auid>=${UID_MIN} -F auid!=unset -k access -a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=- EPERM -F auid>=${UID_MIN} -F auid!=unset -k access -a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=- EACCES -F auid>=${UID_MIN} -F auid!=unset -k access -a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=- EPERM -F auid>=${UID_MIN} -F auid!=unset -k access " >> /etc/audit/rules.d/50-access.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n" }
Load audit rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Ensure events that modify user/group information are collected (Automated)
Description:
Record events affecting the modification of user or group information, including that of passwords and old passwords if in use.
/etc/group
- system groups/etc/passwd
- system users/etc/gshadow
- encrypted password for each group/etc/shadow
- system user passwords/etc/security/opasswd
- storage of old passwords if the relevant PAM module is in use
The parameters in this section will watch the files to see if they have been opened for write or have had attribute changes (e.g. permissions) and tag them with the identifier "identity" in the audit log file.
Rationale: Unexpected changes to these files could be an indication that the system has been compromised and that an unauthorized user is attempting to hide their activities or compromise additional accounts.
Audit:
On disk configuration
Run the following command to check the on disk rules:
# awk '/^ *-w/ \ &&(/\/etc\/group/ \ ||/\/etc\/passwd/ \ ||/\/etc\/gshadow/ \ ||/\/etc\/shadow/ \ ||/\/etc\/security\/opasswd/) \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules
Verify the output matches:
-w /etc/group -p wa -k identity -w /etc/passwd -p wa -k identity -w /etc/gshadow -p wa -k identity -w /etc/shadow -p wa -k identity -w /etc/security/opasswd -p wa -k identity
Running configuration
Run the following command to check loaded rules:
# auditctl -l | awk '/^ *-w/ \ &&(/\/etc\/group/ \ ||/\/etc\/passwd/ \ ||/\/etc\/gshadow/ \ ||/\/etc\/shadow/ \ ||/\/etc\/security\/opasswd/) \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)'
Verify the output matches:
-w /etc/group -p wa -k identity -w /etc/passwd -p wa -k identity -w /etc/gshadow -p wa -k identity -w /etc/shadow -p wa -k identity -w /etc/security/opasswd -p wa -k identity
Remediation:
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor events that modify user/group information.Example:
# printf " -w /etc/group -p wa -k identity -w /etc/passwd -p wa -k identity -w /etc/gshadow -p wa -k identity -w /etc/shadow -p wa -k identity -w /etc/security/opasswd -p wa -k identity " >> /etc/audit/rules.d/50-identity.rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Ensure discretionary access control permission modification events are collected (Automated)
Description:
Monitor changes to file permissions, attributes, ownership and group. The parameters in this section track changes for system calls that affect file permissions and attributes. The following commands and system calls effect the permissions, ownership and various attributes of files.
chmod
fchmod
fchmodat
chown
fchown
fchownat
lchown
setxattr
lsetxattr
fsetxattr
removexattr
lremovexattr
fremovexattr
In all cases, an audit record will only be written for non-system user ids and will ignore Daemon events. All audit records will be tagged with the identifier "perm_mod."
In all cases, an audit record will only be written for non-system user ids and will ignore Daemon events. All audit records will be tagged with the identifier "perm_mod."
Rationale:
Monitoring for changes in file attributes could alert a system administrator to activity that could indicate intruder activity or policy violation.
Audit:
64 Bit systems
On disk configuration
Run the following command to check the on disk rules:
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && awk "/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&(/ -F *auid!=unset/||/ -F *auid!=-1/||/ -F *auid!=4294967295/) \ &&/ -S/ \ &&/ -F *auid>=${UID_MIN}/ \ &&(/chmod/||/fchmod/||/fchmodat/ \ ||/chown/||/fchown/||/fchownat/||/lchown/ \ ||/setxattr/||/lsetxattr/||/fsetxattr/ \ ||/removexattr/||/lremovexattr/||/fremovexattr/) \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)" /etc/audit/rules.d/*.rules \ || printf "ERROR: Variable 'UID_MIN' is unset.\n" }
Verify the output matches:
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod -a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod -a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod -a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
Running configuration
Run the following command to check loaded rules:
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && auditctl -l | awk "/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&(/ -F *auid!=unset/||/ -F *auid!=-1/||/ -F *auid!=4294967295/) \ &&/ -S/ \ &&/ -F *auid>=${UID_MIN}/ \ &&(/chmod/||/fchmod/||/fchmodat/ \ ||/chown/||/fchown/||/fchownat/||/lchown/ \ ||/setxattr/||/lsetxattr/||/fsetxattr/ \ ||/removexattr/||/lremovexattr/||/fremovexattr/) \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)" \ || printf "ERROR: Variable 'UID_MIN' is unset.\n" }
Verify the output matches:
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=-1 -F key=perm_mod -a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=-1 -F key=perm_mod -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=-1 -F key=perm_mod -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>=1000 -F auid!=-1 -F key=perm_mod -a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=-1 -F key=perm_mod -a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=-1 -F key=perm_mod
32 Bit systems
Follow the same procedures as for 64 bit systems and ignore any entries with
b64
.Remediation:
Create audit rules Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor discretionary access control permission modification events.64 Bit systems
Example:
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && printf " -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod -a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod -a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod -a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod " >> /etc/audit/rules.d/50-perm_mod.rules || printf "ERROR: Variable 'UI
Load audit rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Ensure successful file system mounts are collected (Automated)
Description:
Monitor the use of the
mount
system call. Themount
(andumount
) system call controls the mounting and unmounting of file systems. The parameters below configure the system to create an audit record when the mount system call is used by a nonprivileged userRationale:
It is highly unusual for a non privileged user to
mount
file systems to the system. While trackingmount
commands gives the system administrator evidence that external media may have been mounted (based on a review of the source of the mount and confirming it's an external media type), it does not conclusively indicate that data was exported to the media. System administrators who wish to determine if data were exported, would also have to track successfulopen
,creat
andtruncate
system calls requiring write access to a file under the mount point of the external media file system. This could give a fair indication that a write occurred. The only way to truly prove it, would be to track successful writes to the external media. Tracking write system calls could quickly fill up the audit log and is not recommended. Recommendations on configuration options to track data export to media is beyond the scope of this document.Audit:
64 Bit systems
On disk configuration
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && awk "/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&(/ -F *auid!=unset/||/ -F *auid!=-1/||/ -F *auid!=4294967295/) \ &&/ -F *auid>=${UID_MIN}/ \ &&/ -S/ \ &&/mount/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)" /etc/audit/rules.d/*.rules \ || printf "ERROR: Variable 'UID_MIN' is unset.\n" }
Verify the output matches:
-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=unset -k mounts -a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=unset -k mounts
Running configuration
Run the following command to check loaded rules:
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && auditctl -l | awk "/^ *-a *always,exit/ \ &&/ -F *arch=b[2346]{2}/ \ &&(/ -F *auid!=unset/||/ -F *auid!=-1/||/ -F *auid!=4294967295/) \ &&/ -F *auid>=${UID_MIN}/ \ &&/ -S/ \ &&/mount/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)" \ || printf "ERROR: Variable 'UID_MIN' is unset.\n" }
Verify the output matches:
-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=-1 -F key=mounts -a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=-1 -F key=mounts
32 Bit systems
Follow the same procedures as for 64 bit systems and ignore any entries with
b64
.Remediation:
Create audit rules Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor discretionary access control permission modification events.64 bit systems
Example:
# { UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) [ -n "${UID_MIN}" ] && printf " -a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=unset -k mounts -a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=unset -k mounts " >> /etc/audit/rules.d/50-mounts.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n" }
Load audit rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Ensure session initiation information is collected (Automated)
Description:
Monitor session initiation events. The parameters in this section track changes to the files associated with session events
/var/run/utmp
- tracks all currently logged in users./var/log/wtmp
- file tracks logins, logouts, shutdown, and reboot events./var/log/btmp
- keeps track of failed login attempts and can be read by entering the command/usr/bin/last
-f/var/log/btmp
.
All audit records will be tagged with the identifier "session."
Rationale:
Monitoring these files for changes could alert a system administrator to logins occurring at unusual hours, which could indicate intruder activity (i.e. a user logging in at a time when they do not normally log in).
Audit:
On disk configuration
Run the following command to check the on disk rules:
# awk '/^ *-w/ \ &&(/\/var\/run\/utmp/ \ ||/\/var\/log\/wtmp/ \ ||/\/var\/log\/btmp/) \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules
Verify the output matches:
-w /var/run/utmp -p wa -k session -w /var/log/wtmp -p wa -k session -w /var/log/btmp -p wa -k session
Running configuration
Run the following command to check loaded rules:
# auditctl -l | awk '/^ *-w/ \ &&(/\/var\/run\/utmp/ \ ||/\/var\/log\/wtmp/ \ ||/\/var\/log\/btmp/) \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)'
Verify the output matches:
-w /var/run/utmp -p wa -k session -w /var/log/wtmp -p wa -k session -w /var/log/btmp -p wa -k session
Remediation:
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor session initiation information.Example:
# printf " -w /var/run/utmp -p wa -k session -w /var/log/wtmp -p wa -k session -w /var/log/btmp -p wa -k session " >> /etc/audit/rules.d/50-session.rules
Merge and load the rules into active configuration:
# augenrules --load
Check if reboot is required.
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Ensure login and logout events are collected (Automated)
Description:
Monitor login and logout events. The parameters below track changes to files associated with login/logout events.
/var/log/lastlog
- maintain records of the last time a user successfully logged in./var/run/faillock
- directory maintains records of login failures via thepam_faillock
module.
Rationale:
Monitoring login/logout events could provide a system administrator with information associated with brute force attacks against user logins.
Audit:
64 Bit systems
On disk configuration
# awk '/^ *-w/ \ &&(/\/var\/log\/lastlog/ \ ||/\/var\/run\/faillock/) \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)' /etc/audit/rules.d/*.rules
Verify the output matches:
w /var/log/lastlog -p wa -k logins -w /var/run/faillock -p wa -k logins
Running configuration
Run the following command to check loaded rules:
# auditctl -l | awk '/^ *-w/ \ &&(/\/var\/log\/lastlog/ \ ||/\/var\/run\/faillock/) \ &&/ +-p *wa/ \ &&(/ key= *[!-~]* *$/||/ -k *[!-~]* *$/)'
Verify the output matches:
-w /var/log/lastlog -p wa -k logins -w /var/run/faillock -p wa -k logins
Remediation:
Edit or create a file in the
/etc/audit/rules.d/
directory, ending in.rules
extension, with the relevant rules to monitor login and logout events.Example:
# printf " -w /var/log/lastlog -p wa -k logins -w /var/run/faillock -p wa -k logins " >> /etc/audit/rules.d/50-login.rules
Merge and load the rules into active configuration:
Check if reboot is required.
# augenrules --load
# if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi
Ensure file deletion events by users are collected (Automated)
Description:
Monitor the use of system calls associated with the deletion or renaming of files and file attributes. This configuration statement sets up monitoring for:
unlink
- remove a fileunlinkat
- remove a file attributerename
- rename a filerenameat
rename a file attribute system calls and tags them with the identifier "delete".
Rationale:
Monitoring these calls from non-privileged users could provide a system administrator with evidence that inappropriate removal of files and file attributes associated with protected files is occurring. While this audit option will look at all events, system administrators will want to look for specific privileged files that are being deleted or altered
-
Configure auditd file access
-
Sercurity principals for logging
-
Configure journald
-
Ensure journald is configured to send logs to remote log host
-
configure rsyslog