# How To Secure A Linux Server An evolving how-to guide for securing a Linux server. ## Table of Contents - [Introduction](#introduction) - [Document Objective](#document-objective) - [Why Yet Another Guide](#why-yet-another-guide) - [Contributing](#contributing) - [For The Lazy - Editing Configuration Files](#for-the-lazy---editing-configuration-files) - [Before You Start](#before-you-start) - [Notes](#notes) - [Installing Linux](#installing-linux) - [Pre/Post Installation](#prepost-installation) - [Securing Linux](#securing-linux) - [SSH Public/Private Keys](#ssh-publicprivate-keys) - [Change Default `umask`](#change-default-umask) - [Password Protect GRUB](#password-protect-grub) - [Limit Who Can Use `sudo`](#limit-who-can-use-sudo) - [Disable Root Login](#disable-root-login) - [Secure SSH](#secure-ssh) - [Create SSH Group For `AllowGroups`](#create-ssh-group-for-allowgroups) - [Secure `/etc/ssh/sshd_config`](#secure-etcsshsshd_config) - [Deactivate Short Moduli](#deactivate-short-moduli) - [Force Accounts To Use Secure Passwords](#force-accounts-to-use-secure-passwords) - [UFW: Uncomplicated Firewall](#ufw-uncomplicated-firewall) - [Fail2ban: Intrusion Detection And Prevention](#fail2ban-intrusion-detection-and-prevention) - [2FA/MFA for SSH](#2famfa-for-ssh) - [Other Stuff](#other-stuff) - [Mount `/tmp` In RAM Using `tmpfs`](#mount-tmp-in-ram-using-tmpfs) - [Configure Gmail as MTA](#configure-gmail-as-mta) - [Lynis - Linux Security Auditing](#lynis---linux-security-auditing) - [Miscellaneous](#miscellaneous) - [Contacting Me](#contacting-me) - [To Do / To Add](#to-do--to-add) - [Disclaimer / Warranty](#disclaimer--warranty) ## Introduction ### Document Objective This guide's purpose is to teach you how to secure a Linux server. Hopefully you already understand why good security is important. That is a heavy topic onto itself and answering it is out-of-scope for this document. If you don't know the answer to that question, I advise you research it. At a high level, the second a device, like a server, is in the public domain -- i.e visible to the outside world -- it becomes a target for bad-actors. An unsecured device is a playground for bad-actors who want access to confidential data, or more nodes for their coordinated large-scale DDOS attacks. There are a lot of things you can do to secure a Linux server and this guide will attempt to cover as many of them as possible. More topics/material will be added as I learn, or as folks [contribute](#contributing). This guide... - **...is** a work in progress. - **...is** focused on **at-home** Linux servers. All of the concepts/recommendations here apply to larger/professional environments but those use-cases call for more advanced and specialized configurations that are out-of-scope for this guide. - **...does not** teach you about Linux, or how to use it. - **...does not** tell you how to [install Linux](#installing-linux). - **...does not** teach you everything you need to know about security nor does it get into all aspects of system/server security. Physical security, for example, is out of scope for this guide. - **...does not** talk about how programs/tools work, nor does it delve into their nook and crannies. Most of the programs/tools this guide references are very powerful and highly configurable. The goal is to cover the bare necessities that will get you started. To learn more, read the documentation. - **...aims** to make it easy by providing code you can copy-and-paste. You might need to modify the commands before you paste so keep your favorite [text editor](https://notepad-plus-plus.org/) handy. ([Table of Contents](#table-of-contents)) ### Why Yet Another Guide This guide may appear duplicative/unnecessary because there are countless articles online that tell you how to [how to secure Linux](https://duckduckgo.com/?q=how+to+secure+linux&t=ffab&atb=v151-7&ia=web) but the information is spread across different articles, that cover different things, and in different ways. Who has time to scour through hundreds of articles? As I was going through research for my Debian build, I kept notes. At the end I realized that, along with what I already knew, and what I was learning, I had the makings of a how-to guide. I figured I'd put it online to hopefully help others **learn**, and **save time**. I've never found one guide that covers everything -- this guide is my attempt to remedy that. Many of the things covered in this guide may be rather basic/trivial, but most of us do not install Linux every day and it is easy to forget those basic things. IT automation tools like [Ansible](https://www.ansible.com/), [Chef](https://www.chef.io/), [Jenkins](https://jenkins.io/), [Puppet](https://puppet.com/), etc. help with the tedious task of installing/configuring a server but IMHO they are better suited for multiple or large scale deployments. IMHO, the overhead required to use those kinds of automation tools is wholly unnecessary for a one-time single server install for home use. ([Table of Contents](#table-of-contents)) ### Contributing I wanted to put this document on [GitHub](http://www.github.com) to make it easy to collaborate. The more folks that contribute, the better and more complete this guide will become. To contribute you can fork and submit a pull request or submit a [new issue](https://github.com/imthenachoman/How-To-Secure-A-Linux-Server/issues/new). ([Table of Contents](#table-of-contents)) ### For The Lazy - Editing Configuration Files I am very lazy and do not like to edit files by hand if I don't need to. I also assume everyone else is just like me. :) So, when and where possible, I have provided `code` snippets to quickly do what is needed, like add or change a line in a configuration file. The `code` snippets use basic commands like `echo`, `cat`, `sed`, `awk`, and `grep`. How the `code` snippets work, like what each command/part does, is out of scope for this guide -- the `man` pages are your friend. **Note:** The `code` snippets do not validate/verify the change went through -- i.e. the line was actually added or changed. I'll leave the verifying part in your capable hands. The steps in this guide do include taking backups of all files that will be changed. Not all changes can be automated with `code` snippets. Those changes need good, old fashioned, manual editing. For example, you can't just append a line to an [INI](https://en.wikipedia.org/wiki/INI_file) type file. Use your [favorite](https://en.wikipedia.org/wiki/Vi) Linux text editor. ([Table of Contents](#table-of-contents)) ## Before You Start ### Notes - Debian is my distribution of choice and what this guide was tested on. The lines below will, in most cases, work in other distributions -- although, file paths and settings may differ slightly. Check your distribution's documentation. - Do not **blindly** copy-and-paste without understanding what you're pasting. Some commands will need to be modified for your needs before they'll work -- usernames for example. - Your use-case may call for a different configuration. You'll want to understand what each section does and apply as you deem appropriate. You may not, for example, want to [disable root login](#disable-root-login) because you have no means of recovering, or you may not want to [password protect GRUB](#password-protect-grub) because there is zero risk of a bad actor getting physical access to your server. ([Table of Contents](#table-of-contents)) ### Installing Linux Installing Linux is out-of-scope for this document. If you need help, start with your distribution's documentation. Regardless of the distribution, the high-level process usually goes like so: 1. download the ISO 1. burn/copy/transfer it to your install medium (e.g. a CD or USB stick) 1. boot your server from your install medium 1. follow the prompts to install ([Table of Contents](#table-of-contents)) ### Pre/Post Installation - If you're opening ports on your router so you can access your server from the outside, disable the port forwarding until your system is up and secured. - Where applicable, use the expert install option so you have tighter control of what is running on your server. **Only install what you absolutely need.** I, personally, do not install anything other than SSH. - Unless you're doing everything physically connected to your server, you'll need SSH access so be sure it is installed. - Be sure to keep your system up-to-date (i.e. `sudo apt update && sudo apt upgrade` on Debian based systems) - At some point, like maybe right after configuring [SSH public/private keys](#ssh-publicprivate-keys), make sure you perform any tasks specific to your setup like: - configuring network - configuring mount points in `/etc/fstab` (like [mounting `/tmp` in RAM using `tmpfs`](#mount-tmp-in-ram-using-tmpfs)) - creating the initial user accounts - etc... - Your server will need to be able to send e-mails so you can get important security alerts. If you're not setting up a mail server check [Configure Gmail as MTA](#configure-gmail-as-mta). ([Table of Contents](#table-of-contents)) ## Securing Linux ### SSH Public/Private Keys #### Why Using SSH public/private keys is more secure than using a password. It also makes it easier and faster, to connect to our server because you don't have to enter a password. At a high level, public/private keys work by using two keys to encrypt data: 1. One key, the **public** key, **can only encrypt data**, not decrypt it 1. The other key, the **private** key, can decrypt the data For SSH, a public and private key is created on the client. The public key is then securely transferred to the server you want to connect to. After this is done, SSH uses the public and private keys to establish a secure connection. #### Goals - 4096 bit RSA public/private SSH keys - private key on your client - public key on your server #### References - https://www.ssh.com/ssh/public-key-authentication - https://help.ubuntu.com/community/SSH/OpenSSH/Keys - https://linux-audit.com/using-ed25519-openssh-keys-instead-of-dsa-rsa-ecdsa/ - `man ssh-keygen` - `man ssh-copy-id` #### Steps 1. From the computer you're going to use to connect to your server, **the client**, not the server itself, create an [ed25519](https://linux-audit.com/using-ed25519-openssh-keys-instead-of-dsa-rsa-ecdsa/) key: ``` bash ssh-keygen -t ed25519 ``` 1. Transfer it to your server: ``` bash ssh-copy-id user@server ``` You'll need to do this for every computer and account you'll be connecting to your server from/as. Now would be a good time to [perform any tasks specific to your setup](#post-install). ([Table of Contents](#table-of-contents)) ### Linux Kernel `sysctl` Hardening (WIP) #### References - https://geektnt.com/sysctl-conf-hardening.html - https://linoxide.com/how-tos/linux-server-protection/ - https://www.cyberciti.biz/faq/linux-kernel-etcsysctl-conf-security-hardening/ - https://github.com/klaver/sysctl/blob/master/sysctl.conf ([Table of Contents](#table-of-contents)) ### Change Default `umask` #### Why `umask` controls the **default** permissions of files/folders when they are created. Insecure file/folder permissions give other accounts potentially unauthorized access to your data. This may include the ability to make configuration changes. - For **non-root** accounts, there is no need for other accounts to get any access to the account's files/folders **by default**. - For the **root** account, there is no need for the file/folder primary group or other accounts to have any access to **root**'s files/folders **by default**. When and if other accounts need access to a file/folder, you want to explicitly grant it using a combination of file/folder permissions and primary group. #### Why Not Changing the default `umask` can create unexpected problems. #### Goals - set default `umask` for **non-root** accounts to **0027** - set default `umask` for the **root** account to **0077** #### References - https://www.linuxnix.com/umask-define-linuxunix/ - https://serverfault.com/questions/818783/which-umask-is-more-secure-in-linux-022-or-027 - https://www.cyberciti.biz/tips/understanding-linux-unix-umask-value-usage.html - `man umask` #### Steps 1. Set default `umask` for **non-root** accounts to **0027** by **adding** this line to `/etc/profile` and `/etc/bash.bashrc`: ``` umask 0027 ``` [For the lazy](#for-the-lazy---editing-configuration-files): ``` bash sudo cp --preserve /etc/profile /etc/profile.$(date +"%Y%m%d%H%M%S") sudo cp --preserve /etc/bash.bashrc /etc/bash.bashrc.$(date +"%Y%m%d%H%M%S") echo -e "\numask 0027 # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" | sudo tee -a /etc/profile /etc/bash.bashrc ``` 1. We also need to **add** this line to `/etc/login.defs`: ``` UMASK 0027 ``` [For the lazy](#for-the-lazy---editing-configuration-files): ``` bash sudo cp --preserve /etc/login.defs /etc/login.defs.$(date +"%Y%m%d%H%M%S") echo -e "\nUMASK 0027 # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" | sudo tee -a /etc/login.defs ``` 1. Set default `umask` for the **root** account to **0077** by **adding** this line to `/root/.bashrc`: ``` umask 0077 ``` [For the lazy](#for-the-lazy---editing-configuration-files): ``` bash sudo cp --preserve /root/.bashrc /root/.bashrc.$(date +"%Y%m%d%H%M%S") echo -e "\numask 0077 # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" | sudo tee -a /root/.bashrc ``` ([Table of Contents](#table-of-contents)) ### Password Protect GRUB #### Why If a bad actor has physical access to your server, they could use GRUB to gain unauthorized access to your system. #### Why Not If you forget the password, you'll have to go through [some work](https://www.cyberciti.biz/tips/howto-recovering-grub-boot-loader-password.html) to recover the password. #### Goals - auto boot the default Debian install and require a password for anything else #### Notes - This will only protect GRUB and anything behind it like your operating systems. Check your motherboard's documentation for password protecting your BIOS to prevent a bad actor from circumventing GRUB. #### References - https://selivan.github.io/2017/12/21/grub2-password-for-all-but-default-menu-entries.html - https://help.ubuntu.com/community/Grub2/Passwords - https://computingforgeeks.com/how-to-protect-grub-with-password-on-debian-ubuntu-and-kali-linux/ - `man grub` - `man grub-mkpasswd-pbkdf2` #### Steps 1. Create a [Password-Based Key Derivation Function 2 (PBKDF2)](https://en.wikipedia.org/wiki/PBKDF2) hash of your password: ``` bash grub-mkpasswd-pbkdf2 -c 100000 ``` The below output is from using `password` as the password: ``` Enter password: Reenter password: PBKDF2 hash of your password is grub.pbkdf2.sha512.100000.2812C233DFC899EFC3D5991D8CA74068C99D6D786A54F603E9A1EFE7BAEDDB6AA89672F92589FAF98DB9364143E7A1156C9936328971A02A483A84C3D028C4FF.C255442F9C98E1F3C500C373FE195DCF16C56EEBDC55ABDD332DD36A92865FA8FC4C90433757D743776AB186BD3AE5580F63EF445472CC1D151FA03906D08A6D ``` 1. Copy everything **after** `PBKDF2 hash of your password is `, **starting from and including** `grub.pbkdf2.sha512...` to the end. You'll need this in the next step. 1. Create the file `/etc/grub.d/01_password` and **add** the below code after replacing `[hash]` with the hash you copied from the first step: ``` bash #!/bin/sh set -e cat << EOF set superusers="grub" password_pbkdf2 grub [hash] EOF ``` For example: ``` bash #!/bin/sh set -e cat << EOF set superusers="grub" password_pbkdf2 grub grub.pbkdf2.sha512.100000.2812C233DFC899EFC3D5991D8CA74068C99D6D786A54F603E9A1EFE7BAEDDB6AA89672F92589FAF98DB9364143E7A1156C9936328971A02A483A84C3D028C4FF.C255442F9C98E1F3C500C373FE195DCF16C56EEBDC55ABDD332DD36A92865FA8FC4C90433757D743776AB186BD3AE5580F63EF445472CC1D151FA03906D08A6D EOF ``` 1. Set the file's execute bit so `update-grub` includes it when it updates GRUB's configuration: ``` bash sudo chmod a+x /etc/grub.d/01_password ``` 1. Make a backup of `/etc/grub.d/10_linux` and unset execute bit so `update-grub` doesn't try to run it: ``` bash sudo cp --preserve /etc/grub.d/10_linux /etc/grub.d/10_linux.$(date +"%Y%m%d%H%M%S") sudo chmod a-x /etc/grub.d/10_linux.* ``` 1. To make the default Debian install unrestricted (**without** the password) while keeping everything else restricted (**with** the password) modify `/etc/grub.d/10_linux` and **add** `--unrestricted` to the `CLASS` variable. [For the lazy](#for-the-lazy---editing-configuration-files): ``` bash sudo sed -i -r -e "/^CLASS=/ a CLASS=\"\${CLASS} --unrestricted\" # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" /etc/grub.d/10_linux ``` 1. Update GRUB with `update-grub`: ``` bash sudo update-grub ``` ([Table of Contents](#table-of-contents)) ### Limit Who Can Use `sudo` #### Why `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 already have a special group intended for this purpose so check first. - Debian creates the `sudo` group - RedHat creates the `wheel` group #### Steps 1. Create a group: ``` bash sudo groupadd sudo ``` 1. Add account(s) to the group: ``` bash sudo usermod -a -G sudo user1 sudo usermod -a -G sudo user2 sudo usermod -a -G sudo ... ``` You'll need to do this for every account on your server that needs SSH access. 1. Edit `/etc/sudoers`: ``` bash sudo cp --preserve /etc/sudoers /etc/sudoers.$(date +"%Y%m%d%H%M%S") sudo visudo ``` 1. Add this line if it is not already there: ``` %sudo ALL=(ALL:ALL) ALL ``` ([Table of Contents](#table-of-contents)) ### Disable Root Login #### Why If you have `sudo` [configured properly](#limit-who-can-use-sudo), then the **root** account will mostly never need to log in directly -- either at the terminal or remotely. #### Why Not **Be warned, this can cause issues with some configurations!** If your installation uses [`sulogin`](https://linux.die.net/man/8/sulogin) (like Debian) to drop to a **root** console during boot failures, then locking the **root** account will prevent `sulogin` from opening the **root** shell and you will get this error: Cannot open access to console, the root account is locked. See sulogin(8) man page for more details. Press Enter to continue. To work around this, you can use the `--force` option for `sulogin`. Some distributions already include this, or some other, workaround. #### Goal - locked **root** account that nobody can use to log in as **root** #### Notes - Some distributions disable **root** login by default (e.g. Ubuntu) so you may not need to do this step. Check with your distribution's documentation. #### References - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=806852 - https://github.com/systemd/systemd/issues/7115 - https://github.com/karelzak/util-linux/commit/7ff1162e67164cb4ece19dd809c26272461aa254 - https://github.com/systemd/systemd/issues/11596 - `man systemd` #### Steps 1. Lock the **root** account: ``` bash sudo passwd -l root ``` ([Table of Contents](#table-of-contents)) ### Secure SSH #### Create SSH Group For `AllowGroups` ##### Why To make it easy to control who can SSH to the server. ##### Goals - a UNIX group that we'll use in [Secure `/etc/ssh/sshd_config`](#secure-etcsshsshd_config) to limit who can SSH to the server ##### Notes - This is a per-requisite step to support the `AllowGroup` setting set in [Secure `/etc/ssh/sshd_config`](#secure-etcsshsshd_config). ##### References - `man groupadd` - `man usermod` ##### Steps 1. Create a group: ``` bash sudo groupadd sshusers ``` 1. Add account(s) to the group: ``` bash sudo usermod -a -G sshusers user1 sudo usermod -a -G sshusers user2 sudo usermod -a -G sshusers ... ``` You'll need to do this for every account on your server that needs SSH access. ([Table of Contents](#table-of-contents)) #### Secure `/etc/ssh/sshd_config` ##### Why SSH is a door into your server. This is especially true if you are opening ports on your router so you can SSH to your server from outside your home network. If it is not secured properly, a bad-actor could use it to gain unauthorized access to your system. ##### Goal - a secure SSH configuration ##### Notes - Make sure you've completed [Create SSH Group For `AllowGroups`](#create-ssh-group-for-allowgroups) first. ##### References - Mozilla's OpenSSH guidelines for OpenSSH 6.7+ at https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67 - https://linux-audit.com/audit-and-harden-your-ssh-configuration/ - https://www.ssh.com/ssh/sshd_config/ - https://www.techbrown.com/harden-ssh-secure-linux-vps-server/ - `man sshd_config` ##### Steps 1. Make a backup of `/etc/ssh/sshd_config`: ``` bash sudo cp --preserve /etc/ssh/sshd_config /etc/ssh/sshd_config.$(date +"%Y%m%d%H%M%S") ``` 1. Edit `/etc/ssh/sshd_config` then **find and edit or add** these settings that should apply regardless of your configuration/setup: ``` ######################################################################################################## # start settings from https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67 as of 2019-01-01 ######################################################################################################## # Supported HostKey algorithms by order of preference. HostKey /etc/ssh/ssh_host_ed25519_key HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_ecdsa_key KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256 Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com # LogLevel VERBOSE logs user's key fingerprint on login. Needed to have a clear audit track of which key was using to log in. LogLevel VERBOSE # Log sftp level file access (read/write/etc.) that would not be easily logged otherwise. Subsystem sftp /usr/lib/ssh/sftp-server -f AUTHPRIV -l INFO # Use kernel sandbox mechanisms where possible in unprivileged processes # Systrace on OpenBSD, Seccomp on Linux, seatbelt on MacOSX/Darwin, rlimit elsewhere. UsePrivilegeSeparation sandbox ######################################################################################################## # end settings from https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67 as of 2019-01-01 ######################################################################################################## # only use the newer, more secure protocl Protocol 2 # disable X11 forwarding as X11 is very insecure # you really shouldn't be running X on a server anyway X11Forwarding no # disable port forwarding AllowTcpForwarding no AllowStreamLocalForwarding no GatewayPorts no PermitTunnel no # don't allow login if the account has an empty password PermitEmptyPasswords no # ignore .rhosts and .shosts IgnoreRhosts yes # verify hostname matches IP UseDNS no Compression no TCPKeepAlive no AllowAgentForwarding no PermitRootLogin no ``` 1. Then **find and edit or add** these settings, and set values as per your requirements: |Setting|Valid Values|Example|Description|Notes| |--|--|--|--|--| |**AllowGroups**|local UNIX group name|`AllowGroups sshusers`|group to allow SSH access to|| |**ClientAliveCountMax**|number|`ClientAliveCountMax 0`|maximum number of client alive messages sent without response|| |**ClientAliveInterval**|number of seconds|`ClientAliveInterval 300`|timeout in seconds before a response request|| |**ListenAddress**|space separated list of local addresses|