Books / How To Secure A Linux Server / Chapter 3
Linux Security Basics
In this chapter
Limit Who Can Use sudo
sudo lets accounts run commands as other accounts, including root. We want to make sure that only the accounts we want can use sudo.
Goals
- sudo privileges limited to those who are in a group we specify
Notes
- Your installation may have already done this, or may already have a special group intended for this purpose so check first.
- Debian creates the sudo group
- RedHat creates the wheel group
- See https://github.com/imthenachoman/How-To-Secure-A-Linux-Server/issues/39 for a note on some distributions making it so
sudo
does not require a password. Thanks to sbrl for sharing.
Steps
-
Create a group:
sudo groupadd sudousers
-
Add account(s) to the group:
sudo usermod -a -G sudousers user1 sudo usermod -a -G sudousers user2 sudo usermod -a -G sudousers ...
You’ll need to do this for every account on your server that needs sudo privileges.
-
Make a backup of the sudo’s configuration file
/etc/sudoers
:sudo cp --archive /etc/sudoers /etc/sudoers-COPY-$(date +"%Y%m%d%H%M%S")
-
Edit sudo’s configuration file
/etc/sudoers
:sudo visudo
-
Tell sudo to only allow users in the
sudousers
group to use sudo by adding this line if it is not already there:%sudousers ALL=(ALL:ALL) ALL
Limit Who Can Use su
su also lets accounts run commands as other accounts, including root. We want to make sure that only the accounts we want can use su.
Goals
- su privileges limited to those who are in a group we specify
Steps
-
Create a group:
sudo groupadd suusers
-
Add account(s) to the group:
sudo usermod -a -G suusers user1 sudo usermod -a -G suusers user2 sudo usermod -a -G suusers ...
You’ll need to do this for every account on your server that needs sudo privileges.
-
Make it so only users in this group can execute
/bin/su
:sudo dpkg-statoverride --update --add root suusers 4750 /bin/su
Run applications in a sandbox with FireJail
It’s absolutely better, for many applications, to run in a sandbox.
Browsers (even more the Closed Source ones) and eMail Clients are highly suggested.
Goals
- confine applications in a jail (few safe directories) and block access to the rest of the system
Steps
-
Install the software:
sudo apt install firejail firejail-profiles
Note: for Debian 10 Stable, official Backport is suggested:
sudo apt install -t buster-backports firejail firejail-profiles
-
Allow an application (installed in
/usr/bin
or/bin
) to run only in a sandbox (see few examples below here):sudo ln -s /usr/bin/firejail /usr/local/bin/google-chrome-stable sudo ln -s /usr/bin/firejail /usr/local/bin/firefox sudo ln -s /usr/bin/firejail /usr/local/bin/chromium sudo ln -s /usr/bin/firejail /usr/local/bin/evolution sudo ln -s /usr/bin/firejail /usr/local/bin/thunderbird
-
Run the application as usual (via terminal or launcher) and check if is running in a jail:
firejail --list
-
Allow a sandboxed app to run again as it was before (example: firefox)
sudo rm /usr/local/bin/firefox
NTP Client
Many security protocols leverage the time. If your system time is incorrect, it could have negative impacts to your server. An NTP client can solve that problem by keeping your system time in-sync with global NTP servers
How It Works
NTP stands for Network Time Protocol. In the context of this guide, an NTP client on the server is used to update the server time with the official time pulled from official servers. Check https://www.pool.ntp.org/en/ for all of the public NTP servers.
Goals
- NTP client installed and keeping server time in-sync
Steps
-
Install ntp.
On Debian based systems:
sudo apt install ntp
-
Make a backup of the NTP client’s configuration file
/etc/ntp.conf
:sudo cp --archive /etc/ntp.conf /etc/ntp.conf-COPY-$(date +"%Y%m%d%H%M%S")
-
The default configuration, at least on Debian, is already pretty secure. The only thing we’ll want to make sure is we’re the
pool
directive and not anyserver
directives. Thepool
directive allows the NTP client to stop using a server if it is unresponsive or serving bad time. Do this by commenting out allserver
directives and adding the below to/etc/ntp.conf
.pool pool.ntp.org iburst
sudo sed -i -r -e "s/^((server|pool).*)/# \1 # commented by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")/" /etc/ntp.conf echo -e "\npool pool.ntp.org iburst # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" | sudo tee -a /etc/ntp.conf
Example
/etc/ntp.conf
:driftfile /var/lib/ntp/ntp.drift statistics loopstats peerstats clockstats filegen loopstats file loopstats type day enable filegen peerstats file peerstats type day enable filegen clockstats file clockstats type day enable restrict -4 default kod notrap nomodify nopeer noquery limited restrict -6 default kod notrap nomodify nopeer noquery limited restrict 127.0.0.1 restrict ::1 restrict source notrap nomodify noquery pool pool.ntp.org iburst # added by user on 2019-03-09 @ 10:23:35
-
Restart ntp:
sudo service ntp restart
-
Check the status of the ntp service:
sudo systemctl status ntp
● ntp.service - LSB: Start NTP daemon Loaded: loaded (/etc/init.d/ntp; generated; vendor preset: enabled) Active: active (running) since Sat 2019-03-09 15:19:46 EST; 4s ago Docs: man:systemd-sysv-generator(8) Process: 1016 ExecStop=/etc/init.d/ntp stop (code=exited, status=0/SUCCESS) Process: 1028 ExecStart=/etc/init.d/ntp start (code=exited, status=0/SUCCESS) Tasks: 2 (limit: 4915) CGroup: /system.slice/ntp.service └─1038 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 108:113 Mar 09 15:19:46 host ntpd[1038]: Listen and drop on 0 v6wildcard [::]:123 Mar 09 15:19:46 host ntpd[1038]: Listen and drop on 1 v4wildcard 0.0.0.0:123 Mar 09 15:19:46 host ntpd[1038]: Listen normally on 2 lo 127.0.0.1:123 Mar 09 15:19:46 host ntpd[1038]: Listen normally on 3 enp0s3 10.10.20.96:123 Mar 09 15:19:46 host ntpd[1038]: Listen normally on 4 lo [::1]:123 Mar 09 15:19:46 host ntpd[1038]: Listen normally on 5 enp0s3 [fe80::a00:27ff:feb6:ed8e%2]:123 Mar 09 15:19:46 host ntpd[1038]: Listening on routing socket on fd #22 for interface updates Mar 09 15:19:47 host ntpd[1038]: Soliciting pool server 108.61.56.35 Mar 09 15:19:48 host ntpd[1038]: Soliciting pool server 69.89.207.199 Mar 09 15:19:49 host ntpd[1038]: Soliciting pool server 45.79.111.114
-
Check ntp’s status:
sudo ntpq -p
remote refid st t when poll reach delay offset jitter ============================================================================== pool.ntp.org .POOL. 16 p - 64 0 0.000 0.000 0.000 *lithium.constan 198.30.92.2 2 u - 64 1 19.900 4.894 3.951 ntp2.wiktel.com 212.215.1.157 2 u 2 64 1 48.061 -0.431 0.104
Securing /proc
To quote https://linux-audit.com/linux-system-hardening-adding-hidepid-to-proc/:
When looking in
/proc
you will discover a lot of files and directories. Many of them are just numbers, which represent the information about a particular process ID (PID). By default, Linux systems are deployed to allow all local users to see this all information. This includes process information from other users. This could include sensitive details that you may not want to share with other users. By applying some filesystem configuration tweaks, we can change this behavior and improve the security of the system.
Note: This may break on some systemd
systems. Please see https://github.com/imthenachoman/How-To-Secure-A-Linux-Server/issues/37 for more information. Thanks to nlgranger for sharing.
Goals
/proc
mounted withhidepid=2
so users can only see information about their processes
Steps
-
Make a backup of
/etc/fstab
:sudo cp --archive /etc/fstab /etc/fstab-COPY-$(date +"%Y%m%d%H%M%S")
-
Add this line to
/etc/fstab
to have/proc
mounted withhidepid=2
:proc /proc proc defaults,hidepid=2 0 0
echo -e "\nproc /proc proc defaults,hidepid=2 0 0 # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" | sudo tee -a /etc/fstab
-
Reboot the system:
sudo reboot now
Note: Alternatively, you can remount
/proc
without rebooting withsudo mount -o remount,hidepid=2 /proc
Force Accounts To Use Secure Passwords
By default, accounts can use any password they want, including bad ones. pwquality/pam_pwquality addresses this security gap by providing “a way to configure the default password quality requirements for the system passwords” and checking “its strength against a system dictionary and a set of rules for identifying poor choices.”
How It Works
On Linux, PAM is responsible for authentication. There are four tasks to PAM that you can read about at https://en.wikipedia.org/wiki/Linux_PAM. This section talks about the password task.
When there is a need to set or change an account password, the password task of PAM handles the request. In this section we will tell PAM’s password task to pass the requested new password to libpam-pwquality to make sure it meets our requirements. If the requirements are met it is used/set; if it does not meet the requirements it errors and lets the user know.
Goals
- enforced strong passwords
Steps
-
Install libpam-pwquality.
On Debian based systems:
sudo apt install libpam-pwquality
-
Make a backup of PAM’s password configuration file
/etc/pam.d/common-password
:sudo cp --archive /etc/pam.d/common-password /etc/pam.d/common-password-COPY-$(date +"%Y%m%d%H%M%S")
-
Tell PAM to use libpam-pwquality to enforce strong passwords by editing the file
/etc/pam.d/common-password
and change the line that starts like this:password requisite pam_pwquality.so
to this:
password requisite pam_pwquality.so retry=3 minlen=10 difok=3 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1 maxrepeat=3 gecoschec
The above options are:
retry=3
= prompt user 3 times before returning with error.minlen=10
= the minimum length of the password, factoring in any credits (or debits) from these:dcredit=-1
= must have at least one digitucredit=-1
= must have at least one upper case letterlcredit=-1
= must have at least one lower case letterocredit=-1
= must have at least one non-alphanumeric character
difok=3
= at least 3 characters from the new password cannot have been in the old passwordmaxrepeat=3
= allow a maximum of 3 repeated charactersgecoschec
= do not allow passwords with the account’s name
sudo sed -i -r -e "s/^(password\s+requisite\s+pam_pwquality.so)(.*)$/# \1\2 # commented by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")\n\1 retry=3 minlen=10 difok=3 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1 maxrepeat=3 gecoschec # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")/" /etc/pam.d/common-password
Automatic Security Updates and Alerts
It is important to keep a server updated with the latest critical security patches and updates. Otherwise you’re at risk of known security vulnerabilities that bad-actors could use to gain unauthorized access to your server.
Unless you plan on checking your server every day, you’ll want a way to automatically update the system and/or get emails about available updates.
You don’t want to do all updates because with every update there is a risk of something breaking. It is important to do the critical updates but everything else can wait until you have time to do it manually.
Why you shouldn’t enable automatic updates?
Automatic and unattended updates may break your system and you may not be near your server to fix it. This would be especially problematic if it broke your SSH access.
Notes:
- Each distribution manages packages and updates differently. So far I only have steps for Debian based systems.
- Your server will need a way to send e-mails for this to work
Goals
- Automatic, unattended, updates of critical security patches
- Automatic emails of remaining pending updates
Debian Based Systems
How It Works
On Debian based systems you can use:
- unattended-upgrades to automatically do system updates you want (i.e. critical security updates)
- apt-listchanges to get details about package changes before they are installed/upgraded
- apticron to get emails for pending package updates
We will use unattended-upgrades to apply critical security patches. We can also apply stable updates since they’ve already been thoroughly tested by the Debian community.
Steps
-
Install unattended-upgrades, apt-listchanges, and apticron:
sudo apt install unattended-upgrades apt-listchanges apticron
-
Now we need to configure unattended-upgrades to automatically apply the updates. This is typically done by editing the files
/etc/apt/apt.conf.d/20auto-upgrades
and/etc/apt/apt.conf.d/50unattended-upgrades
that were created by the packages. However, because these file may get overwritten with a future update, we’ll create a new file instead. Create the file/etc/apt/apt.conf.d/51myunattended-upgrades
and add this:// Enable the update/upgrade script (0=disable) APT::Periodic::Enable "1"; // Do "apt-get update" automatically every n-days (0=disable) APT::Periodic::Update-Package-Lists "1"; // Do "apt-get upgrade --download-only" every n-days (0=disable) APT::Periodic::Download-Upgradeable-Packages "1"; // Do "apt-get autoclean" every n-days (0=disable) APT::Periodic::AutocleanInterval "7"; // Send report mail to root // 0: no report (or null string) // 1: progress report (actually any string) // 2: + command outputs (remove -qq, remove 2>/dev/null, add -d) // 3: + trace on APT::Periodic::Verbose "2"; APT::Periodic::Unattended-Upgrade "1"; // Automatically upgrade packages from these Unattended-Upgrade::Origins-Pattern { "o=Debian,a=stable"; "o=Debian,a=stable-updates"; "origin=Debian,codename=${distro_codename},label=Debian-Security"; }; // You can specify your own packages to NOT automatically upgrade here Unattended-Upgrade::Package-Blacklist { }; // Run dpkg --force-confold --configure -a if a unclean dpkg state is detected to true to ensure that updates get installed even when the system got interrupted during a previous run Unattended-Upgrade::AutoFixInterruptedDpkg "true"; //Perform the upgrade when the machine is running because we wont be shutting our server down often Unattended-Upgrade::InstallOnShutdown "false"; // Send an email to this address with information about the packages upgraded. Unattended-Upgrade::Mail "root"; // Always send an e-mail Unattended-Upgrade::MailOnlyOnError "false"; // Remove all unused dependencies after the upgrade has finished Unattended-Upgrade::Remove-Unused-Dependencies "true"; // Remove any new unused dependencies after the upgrade has finished Unattended-Upgrade::Remove-New-Unused-Dependencies "true"; // Automatically reboot WITHOUT CONFIRMATION if the file /var/run/reboot-required is found after the upgrade. Unattended-Upgrade::Automatic-Reboot "true"; // Automatically reboot even if users are logged in. Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
Notes:
- Check
/usr/lib/apt/apt.systemd.daily
for details on theAPT::Periodic
options - Check https://github.com/mvo5/unattended-upgrades for details on the
Unattended-Upgrade
options
- Check
-
Run a dry-run of unattended-upgrades to make sure your configuration file is okay:
sudo unattended-upgrade -d --dry-run
If everything is okay, you can let it run whenever it’s scheduled to or force a run with
unattended-upgrade -d
. -
Configure apt-listchanges to your liking:
sudo dpkg-reconfigure apt-listchanges
-
For apticron, the default settings are good enough but you can check them in
/etc/apticron/apticron.conf
if you want to change them. For example, my configuration looks like this:EMAIL="root" NOTIFY_NO_UPDATES="1"
More Secure Random Entropy Pool (WIP)
Steps
-
Install rng-tools.
On Debian based systems:
sudo apt-get install rng-tools
-
Now we need to set the hardware device used to generate random numbers by adding this to
/etc/default/rng-tools
:HRNGDEVICE=/dev/urandom
echo "HRNGDEVICE=/dev/urandom" | sudo tee -a /etc/default/rng-tools
-
Restart the service:
sudo systemctl stop rng-tools.service sudo systemctl start rng-tools.service
-
Test randomness:
- https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security_guide/sect-security_guide-encryption-using_the_random_number_generator
- https://wiki.archlinux.org/index.php/Rng-tools