Hardening
Hardening Ubuntu. Systemd edition.
Install / Use
/learn @konstruktoid/HardeningREADME
image::logo/horizontal.png[Ubuntu Hardening] = Hardening Ubuntu. Systemd edition.
:icons: font
A quick way to make a Ubuntu server a bit more secure.
NOTE: Read the code and do not run this script without first testing in a non-operational environment. The code is not idempotent, use the https://github.com/konstruktoid/ansible-role-hardening[Ansible role] in instead.
Use the newly installed and configured system as a reference, or golden, image. Use that image as a baseline installation media and ensure that any future installation comply with benchmarks and policies using a configuration management tool, e.g https://www.ansible.com/[Ansible] or https://puppet.com/[Puppet].
Tested on Ubuntu 22.04 (Jammy Jellyfish) and Ubuntu 24.04 (Noble Numbat).
If you're interested in testing your host settings, you'll find the link:README.adoc#tests[instructions here].
NOTE: There is a https://slsa.dev/[SLSA] artifact present under the https://github.com/konstruktoid/hardening/actions/workflows/slsa.yml[slsa workflow] for file checksum verification.
== Ansible playbook
An Ansible playbook is available in the https://github.com/konstruktoid/ansible-role-hardening[konstruktoid/ansible-role-hardening] repository.
== Howto
. Start the server installation.
. Pick language and keyboard layout.
. Select "Ubuntu Server (minimized)".
. Configure network connections.
. Partition the system, see below for recommendations.
. Do not install the OpenSSH server, "Featured Server Snaps", or any other packages.
. Finish the installation and reboot.
. Log in.
. If wanted, set a Grub2 password with grub-mkpasswd-pbkdf2. See https://help.ubuntu.com/community/Grub2/Passwords[https://help.ubuntu.com/community/Grub2/Passwords]
for more information.
. Install necessary packages: sudo apt-get -y install git net-tools procps --no-install-recommends.
. Download the script: git clone https://github.com/konstruktoid/hardening.git.
. Change the configuration options in the ubuntu.cfg file. Make sure to update the CHANGEME variable otherwise the script will fail.
. Run the script: sudo bash ubuntu.sh.
. Reboot.
=== Recommended partitions and options
[source,shell]
/boot (rw) /home (rw,nosuid,nodev) /var/log (rw,nosuid,nodev,noexec) /var/log/audit (rw,nosuid,nodev,noexec) /var/tmp (rw,nosuid,nodev,noexec)
Note that /tmp will be added automatically by the script.
== Configuration options
[source,shell]
FW_ADMIN='127.0.0.1' // <1> SSH_GRPS='sudo' // <2> SSH_PORT='22' // <3> SYSCTL_CONF='./misc/sysctl.conf' // <4> AUDITD_MODE='1' // <5> AUDITD_RULES='./misc/audit-base.rules ./misc/audit-aggressive.rules ./misc/audit-docker.rules' // <6> LOGROTATE_CONF='./misc/logrotate.conf' // <7> NTPSERVERPOOL='0.ubuntu.pool.ntp.org 1.ubuntu.pool.ntp.org 2.ubuntu.pool.ntp.org 3.ubuntu.pool.ntp.org pool.ntp.org' // <8> TIMEDATECTL='' // <9> VERBOSE='N' // <10> AUTOFILL='N' // <11> ADMINEMAIL="root@localhost" // <12> KEEP_SNAPD='Y' // <13> CHANGEME='' // <14>
Configuration files // <15>
ADDUSER='/etc/adduser.conf' AUDITDCONF='/etc/audit/auditd.conf' AUDITRULES='/etc/audit/rules.d/hardening.rules' COMMONPASSWD='/etc/pam.d/common-password' COMMONACCOUNT='/etc/pam.d/common-account' COMMONAUTH='/etc/pam.d/common-auth' COREDUMPCONF='/etc/systemd/coredump.conf' DEFAULTGRUB='/etc/default/grub.d' DISABLEFS='/etc/modprobe.d/disablefs.conf' DISABLEMOD='/etc/modprobe.d/disablemod.conf' DISABLENET='/etc/modprobe.d/disablenet.conf' FAILLOCKCONF='/etc/security/faillock.conf' JOURNALDCONF='/etc/systemd/journald.conf' LIMITSCONF='/etc/security/limits.conf' LOGINDCONF='/etc/systemd/logind.conf' LOGINDEFS='/etc/login.defs' LOGROTATE='/etc/logrotate.conf' PAMLOGIN='/etc/pam.d/login' PSADCONF='/etc/psad/psad.conf' PSADDL='/etc/psad/auto_dl' RESOLVEDCONF='/etc/systemd/resolved.conf' RKHUNTERCONF='/etc/default/rkhunter' RSYSLOGCONF='/etc/rsyslog.conf' SECURITYACCESS='/etc/security/access.conf' SSHFILE='/etc/ssh/ssh_config' SSHDFILE='/etc/ssh/sshd_config' SYSCTL='/etc/sysctl.conf' SYSTEMCONF='/etc/systemd/system.conf' TIMESYNCD='/etc/systemd/timesyncd.conf' UFWDEFAULT='/etc/default/ufw' USERADD='/etc/default/useradd' USERCONF='/etc/systemd/user.conf'
<1> The IP addresses that will be able to connect with SSH, separated by spaces.
<2> Which group the users have to be member of in order to acess via SSH, separated by spaces.
<3> Configure SSH port.
<4> Stricter sysctl settings.
<5> Auditd failure mode. 0=silent 1=printk 2=panic.
<6> Auditd rules.
<7> Logrotate settings.
<8> NTP server pool.
<9> Add a specific time zone or use the system default by leaving it empty.
<10> If you want all the details or not.
<11> Let the script guess the FW_ADMIN and SSH_GRPS settings.
<12> Add a valid email address, so PSAD can send notifications.
<13> If 'Y' then the snapd package will be held to prevent removal.
<14> Add something just to verify that you actually glanced the code.
<15> Default configuration file locations.
== Functions
=== Function list in execution order
Note that all functions has the f_ prefix in the code.
==== pre
Sets apt flags and performs basic permission check.
The pre function is located in link:scripts/pre[./scripts/pre].
==== kernel
Sets https://github.com/jeffmurphy/NetPass/blob/master/doc/netfilter_conntrack_perf.txt#L175[/sys/module/nf_conntrack/parameters/hashsize]
to 1048576 if hashsize exists and is writable.
Sets https://man7.org/linux/man-pages/man7/kernel_lockdown.7.html[/sys/kernel/security/lockdown]
to confidentiality if lockdown exists and is writable.
The kernel function is located in link:scripts/kernel[./scripts/kernel].
==== firewall
Configures https://help.ubuntu.com/community/UFW[UFW] if installed.
Allows connections from the adresses in $FW_ADMIN to the $SSH_PORT.
Sets logging and IPT_SYSCTL=/etc/sysctl.conf.
The firewall function is located in link:scripts/ufw[./scripts/ufw].
==== disablenet
Disables the dccp, sctp, rds and tipc kernel modules.
The disablenet function is located in link:scripts/disablenet[./scripts/disablenet].
==== disablefs
Disables the cramfs freevxfs jffs2 ksmbd hfs hfsplus udf kernel
modules.
The disablefs function is located in link:scripts/disablefs[./scripts/disablefs].
==== disablemod
Disables the bluetooth, bnep, btusb, cpia2, firewire-core, floppy,
n_hdlc, net-pf-31, pcspkr, soundcore, thunderbolt, usb-midi,
usb-storage, uvcvideo, v4l2_common kernel modules.
Note that disabling the usb-storage module will disable any usage of USB
storage devices, if such devices are needed USBGuard should be configured
accordingly and usb-storage removed from the disablemod function.
The disablemod function is located in link:scripts/disablemod[./scripts/disablemod].
==== systemdconf
Sets CrashShell=no, DefaultLimitCORE=0, DefaultLimitNOFILE=1024,
DefaultLimitNPROC=1024, DumpCore=no in $SYSTEMCONF
and $USERCONF.
The systemdconf function is located in link:scripts/systemdconf[./scripts/systemdconf].
==== resolvedconf
Sets DNS=$dnslist, DNSOverTLS=opportunistic, DNSSEC=allow-downgrade, FallbackDNS=1.0.0.1
in $RESOLVEDCONF, where $dnslist is an array with the nameservers present
in /etc/resolv.conf.
The resolvedconf function is located in link:scripts/resolvedconf[./scripts/resolvedconf].
==== logindconf
Sets IdleAction=lock, IdleActionSec=15min, KillExcludeUsers=root,
KillUserProcesses=1, RemoveIPC=yes in $LOGINDCONF.
The logindconf function is located in link:scripts/logindconf[./scripts/logindconf].
==== journalctl
Copies link:misc/logrotate.conf[./misc/logrotate.conf] to $LOGROTATE.
Sets Compress=yes, ForwardToSyslog=yes, Storage=persistent in
$JOURNALDCONF.
Sets $FileCreateMode 0600/ in $RSYSLOGCONF.
if RSYSLOGCONF is writable.
The journalctl function is located in link:scripts/journalctl[./scripts/journalctl].
==== timesyncd
Sets NTP=${SERVERARRAY}, FallbackNTP=${FALLBACKARRAY}, RootDistanceMaxSec=1
in $TIMESYNCD where the arrays are up to four time servers with < 50ms
latency.
The timesyncd function is located in link:scripts/timesyncd[./scripts/timesyncd].
==== fstab
Configures the /boot and /home partitions with defaults,nosuid,nodev if
they are available in /etc/fstab.
Configures the /var/log, /var/log/audit and /var/tmp partitions with
defaults,nosuid,nodev,noexec if they are available in /etc/fstab.
Adds /run/shm tmpfs rw,noexec,nosuid,nodev,
/dev/shm tmpfs rw,noexec,nosuid,nodev and
/proc proc rw,nosuid,nodev,noexec,relatime,hidepid=2 to /etc/fstab if
the partition isn't present in /etc/fstab.
Removes any floppy drivers from /etc/fstab.
Copies ./config/tmp.mount[./config/tmp.mount] to
/etc/systemd/system/tmp.mount, removes /tmp from /etc/fstab
and enables the tmpfs /tmp mount instead.
The /proc hidepid option is described in https://www.kernel.org/doc/html/latest/filesystems/proc.html#mount-options[https://www.kernel.org/doc/html/latest/filesystems/proc.html#mount-options].
The fstab function is located in link:scripts/fstab[./scripts/fstab].
==== prelink
Reverts binaries and libraries to their original content before they were
prelinked and uninstalls prelink.
The prelink function is located in link:scripts/prelink[./scripts/prelink].
==== aptget_configure
Sets apt options Acquire::http::AllowRedirect "false";, APT::Get::AllowUnauthenticated "false";,
APT::Periodic::AutocleanInterval "7";,
APT::Install-Recommends "false";, APT::Get::AutomaticRemove "true";,
APT::Install-Suggests "false";, Acquire::AllowDowngradeToInsecureRepositories "false";,
Acquire::AllowInsecureRepositories "false";, APT::Sandbox::Seccomp "1";
See https://manpages.ubuntu.com/manpages/jammy/man5/apt.conf.5.html[https://manpages.ubuntu.com/manpages/jammy/man5/apt.conf.5.html].
The aptget_configure func
