| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
An evolving how-to guide for securing a Linux server that, hopefully, also teaches you a little about security and why it matters.
(TOC made with nGitHubTOC)
This guide's purpose is to teach you how to secure a Linux server.
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.
I assume you're using this guide because you, hopefully, already understand why good security is important. That is a heavy topic onto itself and breaking it down is out-of-scope for this guide. If you don't know the answer to that question, I advise you research it first.
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 your data, or to use your server as another node for their large-scale DDOS attacks.
What's worse is, without good security, you may never know if your server has been compromised. A bad-actor may have gained unauthorized access to your server and copied your data without changing anything so you'd never know. Or your server may have been part of a DDOS attack and you wouldn't know. Look at many of the large scale data breaches in the news -- the companies often did not discover the data leak or intrusion until long after the bad-actors were gone.
Contrary to popular belief, bad-actors don't always want to change something or lock you out of your data for money. Sometimes they just want the data on your server for their data warehouses (there is big money in big data) or to covertly use your server for their nefarious purposes.
This guide may appear duplicative/unnecessary because there are countless articles online that tell you how to how to secure Linux 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.
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, Chef, Jenkins, Puppet, 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.
This guide...
Some of the sections in this guide are generally considered safe and shouldn't make your system unusable.
Some sections cover things that are high risk because there is a possibility they can make your system unusable, or are considered unnecessary by many because the risks outweigh any rewards. These sections are tagged with [DZ] and the content is hidden by default. !! PROCEED AT YOUR OWN RISK !!
Some sections are not necessary to secure your server but are still helpful. For example, you don't need to configure your server to send mail through Gmail but you will want someway to send e-mails so you get critical system/security alerts. These sections are tagged with [NS].
Regardless of the section, as is with anything in this guide, use with caution and proceed at your own risk.
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 type file. Use your favorite Linux text editor.
I wanted to put this guide on GitHub 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.
Before you start you will want to identify what your Principles are. What is your threat model? Some things to think about:
These are just a few things to think about. Before you start securing your server you will want to understand what you're trying to protect against and why so you know what you need to do.
This guide is intended to be distribution agnostic so users can use any distribution they want. With that said, there are a few things to keep in mind:
You want a distribution that...
Installing Linux is out-of-scope for this guide because each distribution does it differently and the installation instructions are usually well documented. If you need help, start with your distribution's documentation. Regardless of the distribution, the high-level process usually goes like so:
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.
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.
Check the references below for more details but, at a high level, public/private keys work by using a pair of keys to verify identity.
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 verify identity and then establishing a secure connection. Identity is verified by the server encrypting a challenge message with the public key, then sending it to the client. If the client cannot decrypt the challenge message with the private key, the identity can't be verified and a connection will not be established.
They are considered more secure because you need the private key to establish an SSH connection. If you set PasswordAuthentication no in /etc/ssh/sshd_config, then SSH won't let you connect without the private key.
You can also set a passphrase for the keys which would require you to enter the key passphrase when connecting using public/private keys. Keep in mind doing this means you can't use the key for automation because you'll have no way to send the passphrase in your scripts. ssh-agent is a program that is shipped in many Linux distros (and usually already running) that will allow you to hold your unencrypted private key in memory for a configurable duration. Simply run ssh-add and it will prompt you for your passphrase. You will not be prompted for your passphrase again until the configurable duration has passed.
We will be using Ed25519 keys which, according to https://linux-audit.com/:
It is using an elliptic curve signature scheme, which offers better security than ECDSA and DSA. At the same time, it also has good performance.
From the computer you're going to use to connect to your server, the client, not the server itself, create an Ed25519 key with ssh-keygen:
ssh-keygen -t ed25519Generating public/private ed25519 key pair. Enter file in which to save the key (/home/user/.ssh/id_ed25519): Created directory '/home/user/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/user/.ssh/id_ed25519. Your public key has been saved in /home/user/.ssh/id_ed25519.pub. The key fingerprint is: SHA256:F44D4dr2zoHqgj0i2iVIHQ32uk/Lx4P+raayEAQjlcs user@client The key's randomart image is: +--[ED25519 256]--+ |xxxx x | |o.o +. . | | o o oo . | |. E oo . o . | | o o. o S o | |... .. o o | |.+....+ o | |+.=++o.B.. | |+..=**=o=. | +----[SHA256]-----+
Note: If you set a passphrase, you'll need to enter it every time you connect to your server using this key, unless you're using ssh-agent.
When you SSH to your server, your server will look for your public key in the .ssh/authorized_keys file in your home directory. So we need to append the contents of the public key ~/.ssh/id_ed25519.pub from the machine you're on (the client) to the ~/.ssh/authorized_keys file on the target server. You'll want to do this in a secure way since the public key gives access to your server. One approach is to copy it to a USB stick and physically transfer it to the server. If you're sure there is nobody listening between the client you're on and your server, you can use ssh-copy-id to transfer and append the public key:
ssh-copy-id user@server /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_ed25519.pub" The authenticity of host 'host (192.168.1.96)' can't be established. ECDSA key fingerprint is SHA256:QaDQb/X0XyVlogh87sDXE7MR8YIK7ko4wS5hXjRySJE. Are you sure you want to continue connecting (yes/no)? yes /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys user@host's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'user@host'" and check to make sure that only the key(s) you wanted were added.
Now would be a good time to perform any tasks specific to your setup.
sudo lets accounts run commands as other accounts, including root. We want to make sure that only the accounts we want can use sudo.
Create a group:
sudo groupadd sudousersAdd 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.
Edit /etc/sudoers:
sudo cp --preserve /etc/sudoers /etc/sudoers.$(date +"%Y%m%d%H%M%S")
sudo visudoTell 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
To make it easy to control who can SSH to the server. By using a group, we can quickly add/remove accounts to the group to quickly allow or not allow SSH access to the server.
Create a group:
sudo groupadd sshusersAdd account(s) to the group:
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.
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.
Make a backup of /etc/ssh/sshd_config and remove default comments to make it easier to read:
sudo cp --preserve /etc/ssh/sshd_config /etc/ssh/sshd_config.$(date +"%Y%m%d%H%M%S")
sudo sed -i -r -e '/^#|^$/ d' /etc/ssh/sshd_configEdit /etc/ssh/sshd_config then find and edit or add these settings that should apply regardless of your configuration/setup:
Note: Your /etc/ssh/sshd_config file may already have some of these settings/lines. You will want to remove those and replace them with the ones below.
######################################################################################################## # 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 # 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 ######################################################################################################## # Log sftp level file access (read/write/etc.) that would not be easily logged otherwise. Subsystem sftp internal-sftp -f AUTHPRIV -l INFO # 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
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 |
|
local addresses sshd should listen on | See Issue #1 for important details. |
| LoginGraceTime | number of seconds | LoginGraceTime 30 | time in seconds before login times-out | |
| MaxAuthTries | number | MaxAuthTries 2 | maximum allowed attempts to login | |
| MaxSessions | number | MaxSessions 2 | maximum number of open sessions | |
| MaxStartups | number | MaxStartups 2 | maximum number of login sessions | |
| PasswordAuthentication | yes or no | PasswordAuthentication no | if login with a password is allowed | |
| Port | any open/available port number | Port 22 | port that sshd should listen on |
Check man sshd_config for more details what these settings mean.
Restart ssh:
sudo service sshd restartPer Mozilla's OpenSSH guidelines for OpenSSH 6.7+, "all Diffie-Hellman moduli in use should be at least 3072-bit-long".
Make a backup of /etc/ssh/moduli:
sudo cp --preserve /etc/ssh/moduli /etc/ssh/moduli.$(date +"%Y%m%d%H%M%S")Remove short moduli:
sudo awk '$5 >= 3071' /etc/ssh/moduli | sudo tee /etc/ssh/moduli.tmp
sudo mv /etc/ssh/moduli.tmp /etc/ssh/moduliMany 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.
Install ntp.
On Debian based systems:
sudo apt install ntpCheck ntp's status:
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-02-16 00:32:20 EST; 3s ago Docs: man:systemd-sysv-generator(8) CGroup: /system.slice/ntp.service └─1051 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 109:114 Feb 16 00:32:20 host ntpd[1051]: Listen normally on 3 enp0s3 192.168.1.96:123 Feb 16 00:32:20 host ntpd[1051]: Listen normally on 4 lo [::1]:123 Feb 16 00:32:20 host ntpd[1051]: Listen normally on 5 enp0s3 [fe80::a00:27ff:feb6:ed8e%2]:123 Feb 16 00:32:20 host ntpd[1051]: Listening on routing socket on fd #22 for interface updates Feb 16 00:32:21 host ntpd[1051]: Soliciting pool server 173.255.206.154 Feb 16 00:32:22 host ntpd[1051]: Soliciting pool server 216.6.2.70 Feb 16 00:32:22 host ntpd[1051]: Soliciting pool server 82.197.188.130 Feb 16 00:32:23 host ntpd[1051]: Soliciting pool server 95.215.175.2 Feb 16 00:32:23 host ntpd[1051]: Soliciting pool server 107.155.79.108 Feb 16 00:32:23 host ntpd[1051]: Soliciting pool server 212.110.158.28
sudo ntpq -premote refid st t when poll reach delay offset jitter ============================================================================== 0.debian.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 1.debian.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 2.debian.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 3.debian.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 -li216-154.membe 45.56.123.24 3 u 119 64 2 51.912 0.663 2.311 +eudyptula.init7 162.23.41.10 2 u 60 64 3 99.378 1.563 3.485 +107.155.79.108 129.7.1.66 2 u 119 64 2 49.171 -1.372 1.441 -212.110.158.28 89.109.251.21 2 u 120 64 2 167.465 -1.064 1.263 *ec2-54-242-183- 128.10.19.24 2 u 62 64 3 19.157 2.536 4.434 -69.195.159.158 128.252.19.1 2 u 119 64 2 42.990 6.302 3.507 -200.89.75.198 ( 200.27.106.115 2 u 58 64 3 160.786 42.737 12.827
Unless you're planning on setting up your own mail server, you'll need a way to send e-mails from your server. This will be important for system alerts/messages.
Install exim4.
On Debian based systems:
sudo apt install exim4Configure exim4:
For Debian based systems:
sudo dpkg-reconfigure exim4-configYou'll be prompted with some questions:
| Prompt | Answer |
|---|---|
| General type of mail configuration | mail sent by smarthost; no local mail |
| System mail name | (default) |
| IP-addresses to listen on for incoming SMTP connections | 127.0.0.1 |
| Other destinations for which mail is accepted | (default) |
| Visible domain name for local users | (default) |
| IP address or host name of the outgoing smarthost | smtp.gmail.com::587 |
| Keep number of DNS-queries minimal (Dial-on-Demand)? | No |
| Split configuration into small files? | No |
Make a backup of /etc/exim4/passwd.client:
sudo cp /etc/exim4/passwd.client /etc/exim4/passwd.client.$(date +"%Y%m%d%H%M%S")Add a line like this to /etc/exim4/passwd.client
*.google.com:yourAccount@gmail.com:yourPassword
Replace yourAccount@gmail.com and yourPassword with your details. If you have 2FA/MFA enabled on your Gmail then you'll need to create and use an app password.
This file has your Gmail password so we need to lock it down:
sudo chown root:Debian-exim /etc/exim4/passwd.client
sudo chmod 640 /etc/exim4/passwd.clientRestart exim4:
sudo service exim4 restartAdd some mail aliases so we can send e-mails to local accounts by adding lines like this to /etc/aliases:
user1: user1@gmail.com user2: user2@gmail.com ...
You'll need to add all the local accounts that exist on your server.
Call me paranoid, and you don't have to agree, but I want to deny all traffic in and out of my server except what I explicitly allow. Why would my server be sending traffic out that I don't know about? And why would external traffic be trying to access my server if I don't know who or what it is? When it comes to good security, my opinion is to reject/deny by default, and allow by exception.
Of course, if you disagree, that is totally fine and can configure UFW to suit your needs.
Either way, ensuring that only traffic we explicitly allow is the job of a firewall. On Linux, the most common firewall is iptables. iptables, however, is rather complicated and confusing (IMHO). This is where UFW comes in. UFW simplifies the process of creating and managing iptables rules.
UFW works by letting you configure rules that:
You can create rules by explicitly specifying the ports or with application configurations that specify the ports.
Install ufw.
On Debian based systems:
sudo apt install ufwDeny all outgoing traffic:
sudo ufw default deny outgoing comment 'deny all outgoing traffic'Default outgoing policy changed to 'deny' (be sure to update your rules accordingly)
If you are not as paranoid as me, and don't want to deny all outgoing traffic, you can allow it instead:
sudo ufw default allow outgoing comment 'allow all outgoing traffic'Deny all incoming traffic:
sudo ufw default deny incoming comment 'deny all incoming traffic'Obviously we want SSH connections in:
sudo ufw limit in ssh comment 'allow SSH connections in'Rules updated Rules updated (v6)
Allow additional traffic as per your needs. Some common use-cases:
# allow traffic out on port 53 -- DNS
sudo ufw allow out 53 comment 'allow DNS calls out'
# allow traffic out on port 123 -- NTP
sudo ufw allow out 123 comment 'allow NTP out'
# allow traffic out for HTTP, HTTPS, or FTP
# apt might needs these depending on which sources you're using
sudo ufw allow out http comment 'allow HTTP traffic out'
sudo ufw allow out https comment 'allow HTTPS traffic out'
sudo ufw allow out ftp comment 'allow FTP traffic out'
# allow mail to go out
sudo ufw allow out 'Mail submission' comment 'allow mail out'
# allow whois
sudo ufw allow out whois comment 'allow whois'
# allow traffic out on port 68 -- the DHCP client
# you only need this if you're using DHCP
sudo ufw allow out 68 comment 'allow the DHCP client to update'Start ufw:
sudo ufw enableCommand may disrupt existing ssh connections. Proceed with operation (y|n)? y Firewall is active and enabled on system startup
If you want to see a status:
sudo ufw statusStatus: active To Action From -- ------ ---- 22/tcp LIMIT Anywhere # allow SSH connections in 22/tcp (v6) LIMIT Anywhere (v6) # allow SSH connections in 53 ALLOW OUT Anywhere # allow DNS calls out 123 ALLOW OUT Anywhere # allow NTP out 80/tcp ALLOW OUT Anywhere # allow HTTP traffic out 443/tcp ALLOW OUT Anywhere # allow HTTPS traffic out 21/tcp ALLOW OUT Anywhere # allow FTP traffic out Mail submission ALLOW OUT Anywhere # allow mail out 43/tcp ALLOW OUT Anywhere # allow whois 53 (v6) ALLOW OUT Anywhere (v6) # allow DNS calls out 123 (v6) ALLOW OUT Anywhere (v6) # allow NTP out 80/tcp (v6) ALLOW OUT Anywhere (v6) # allow HTTP traffic out 443/tcp (v6) ALLOW OUT Anywhere (v6) # allow HTTPS traffic out 21/tcp (v6) ALLOW OUT Anywhere (v6) # allow FTP traffic out Mail submission (v6) ALLOW OUT Anywhere (v6) # allow mail out 43/tcp (v6) ALLOW OUT Anywhere (v6) # allow whois
or
sudo ufw status verboseStatus: active Logging: on (low) Default: deny (incoming), deny (outgoing), disabled (routed) New profiles: skip To Action From -- ------ ---- 22/tcp LIMIT IN Anywhere # allow SSH connections in 22/tcp (v6) LIMIT IN Anywhere (v6) # allow SSH connections in 53 ALLOW OUT Anywhere # allow DNS calls out 123 ALLOW OUT Anywhere # allow NTP out 80/tcp ALLOW OUT Anywhere # allow HTTP traffic out 443/tcp ALLOW OUT Anywhere # allow HTTPS traffic out 21/tcp ALLOW OUT Anywhere # allow FTP traffic out 587/tcp (Mail submission) ALLOW OUT Anywhere # allow mail out 43/tcp ALLOW OUT Anywhere # allow whois 53 (v6) ALLOW OUT Anywhere (v6) # allow DNS calls out 123 (v6) ALLOW OUT Anywhere (v6) # allow NTP out 80/tcp (v6) ALLOW OUT Anywhere (v6) # allow HTTP traffic out 443/tcp (v6) ALLOW OUT Anywhere (v6) # allow HTTPS traffic out 21/tcp (v6) ALLOW OUT Anywhere (v6) # allow FTP traffic out 587/tcp (Mail submission (v6)) ALLOW OUT Anywhere (v6) # allow mail out 43/tcp (v6) ALLOW OUT Anywhere (v6) # allow whois
ufw ships with some default applications. You can see them with:
sudo ufw app listAvailable applications: AIM Bonjour CIFS DNS Deluge IMAP IMAPS IPP KTorrent Kerberos Admin Kerberos Full Kerberos KDC Kerberos Password LDAP LDAPS LPD MSN MSN SSL Mail submission NFS OpenSSH POP3 POP3S PeopleNearby SMTP SSH Socks Telnet Transmission Transparent Proxy VNC WWW WWW Cache WWW Full WWW Secure XMPP Yahoo qBittorrent svnserve
To get details about the app, like which ports it includes, type:
sudo ufw app info [app name]sudo ufw app info DNSProfile: DNS Title: Internet Domain Name Server Description: Internet Domain Name Server Port: 53
If you don't want to create rules by explicitly providing the port number(s), you can create your own application configurations. To do this, create a file in /etc/ufw/applications.d.
For example, here is what you would use for Plex:
cat /etc/ufw/applications.d/plexmediaserver[PlexMediaServer] title=Plex Media Server description=This opens up PlexMediaServer for http (32400), upnp, and autodiscovery. ports=32469/tcp|32413/udp|1900/udp|32400/tcp|32412/udp|32410/udp|32414/udp|32400/udp
Then you can enable it like any other app:
sudo ufw allow plexmediaserverI can't explain it any better than user FINESEC from https://serverfault.com/ did at: https://serverfault.com/a/447604/289829.
Fail2BAN scans log files of various applications such as apache, ssh or ftp and automatically bans IPs that show the malicious signs such as automated login attempts. PSAD on the other hand scans iptables and ip6tables log messages (typically /var/log/messages) to detect and optionally block scans and other types of suspect traffic such as DDoS or OS fingerprinting attempts. It's ok to use both programs at the same time because they operate on different level.
And, since we're already using UFW so we'll follow the awesome instructions by netson at https://gist.github.com/netson/c45b2dc4e835761fbccc to make PSAD work with UFW.
Install psad.
On Debian based systems:
sudo apt install psadMake a backup of /etc/psad/psad.conf:
sudo cp /etc/psad/psad.conf /etc/psad/psad.conf.$(date +"%Y%m%d%H%M%S")Review and update configuration options in /etc/psad/psad.conf. Pay special attention to these:
| Setting | Set To |
|---|---|
| EMAIL_ADDRESSES | your email address(s) |
| HOSTNAME | your server's hostname |
| ENABLE_AUTO_IDS | ENABLE_AUTO_IDS Y; |
| ENABLE_AUTO_IDS_EMAILS | ENABLE_AUTO_IDS_EMAILS Y; |
| EXPECT_TCP_OPTIONS | EXPECT_TCP_OPTIONS Y; |
Check the configuration file psad's documentation at http://www.cipherdyne.org/psad/docs/config.html for more details.
Now we need to make some changes to ufw so it works with psad by telling ufw to log all traffic so psad can analyze it. Do this by editing two files and adding these lines at the end but before the COMMIT line.
Make backups:
sudo cp /etc/ufw/before.rules /etc/ufw/before.rules.$(date +"%Y%m%d%H%M%S")
sudo cp /etc/ufw/before6.rules /etc/ufw/before6.rules.$(date +"%Y%m%d%H%M%S")Edit the files:
And add add this at the end but before the COMMIT line:
# log all traffic so psad can analyze -A INPUT -j LOG --log-tcp-options --log-prefix "[IPTABLES] " -A FORWARD -j LOG --log-tcp-options --log-prefix "[IPTABLES] "
Note: We're adding a log prefix to all the iptables logs. We'll need this for seperating iptables logs to their own file.
For example:
... # log all traffic so psad can analyze -A INPUT -j LOG --log-tcp-options --log-prefix "[IPTABLES] " -A FORWARD -j LOG --log-tcp-options --log-prefix "[IPTABLES] " # don't delete the 'COMMIT' line or these rules won't be processed COMMIT
Now we need to reload/restart ufw and psad for the changes to take effect:
sudo ufw reload
sudo psad -R
sudo psad --sig-update
sudo psad -HAnalyze iptables rules for errors:
sudo psad --fw-analyze[+] Parsing INPUT chain rules. [+] Parsing INPUT chain rules. [+] Firewall config looks good. [+] Completed check of firewall ruleset. [+] Results in /var/log/psad/fw_check [+] Exiting.
Note: If there were any issues you will get an e-mail with the error.
Check the status of psad:
sudo psad --Status[-] psad: pid file /var/run/psad/psadwatchd.pid does not exist for psadwatchd on vm [+] psad_fw_read (pid: 3444) %CPU: 0.0 %MEM: 2.2 Running since: Sat Feb 16 01:03:09 2019 [+] psad (pid: 3435) %CPU: 0.2 %MEM: 2.7 Running since: Sat Feb 16 01:03:09 2019 Command line arguments: [none specified] Alert email address(es): root@localhost [+] Version: psad v2.4.3 [+] Top 50 signature matches: [NONE] [+] Top 25 attackers: [NONE] [+] Top 20 scanned ports: [NONE] [+] iptables log prefix counters: [NONE] Total protocol packet counters: [+] IP Status Detail: [NONE] Total scan sources: 0 Total scan destinations: 0 [+] These results are available in: /var/log/psad/status.out
There will come a time when you'll need to look through your iptables logs. Having all the iptables logs go to their own file will make it a lot easier to find what you're looking for.
The first step is by telling your firewall to prefix all log entries with some unique string. If you're using iptables directly, you would do something like --log-prefix "[IPTABLES] " for all the rules. We took care of this in step step 4 of installing psad.
After you've added a prefix to the firewall logs, we need to tell rsyslog to send those lines to its own file. Do this by creating the file /etc/rsyslog.d/10-iptables.conf and adding this:
:msg, contains, "[IPTABLES] " /var/log/iptables.log & stop
If you're expecting a lot if data being logged by your firewall, prefix the filename with a - "to omit syncing the file after every logging". For example:
:msg, contains, "[IPTABLES] " -/var/log/iptables.log & stop
Note: Remember to change the prefix to whatever you use.
Since we're logging firewall messages to a different file, we need to tell psad where the new file is. Edit /etc/psad/psad.conf and set IPT_SYSLOG_FILE to the path of the log file. For example:
IPT_SYSLOG_FILE /var/log/iptables.log;
Restart psad and rsyslog to activate the changes (or reboot):
sudo psad -R
sudo psad --sig-update
sudo psad -H
sudo service rsyslog restartThe last thing we have to do is tell logrotate to rotate the new log file so it doesn't get to big and fill up our disk. Create the file /etc/logrotate.d/iptables and add this:
/var/log/iptables.log
{
rotate 7
daily
missingok
notifempty
delaycompress
compress
postrotate
invoke-rc.d rsyslog rotate > /dev/null
endscript
}
A firewall will board up all the doors and windows you don't want anyone using so nobody can see they are even there. But what about the doors and windows you want visible so approved folks can use them? Even if the door is locked, how do you ensure that someone doesn't try to force their way in?
That is where Fail2ban comes in. It will monitor network traffic/logs and prevent intrusions by blocking suspicious activity (e.g. multiple successive failed connections in a short time-span).
Install fail2ban.
On Debian based systems:
sudo apt install fail2banWe don't want to edit /etc/fail2ban/fail2ban.conf or /etc/fail2ban/jail.conf because a future update may overwrite those so we'll update a local copy instead. Add this to /etc/fail2ban/jail.local after replacing [LAN SEGMENT] and [your email] with the appropriate values:
[DEFAULT] # the IP address range we want to ignore ignoreip = 127.0.0.1/8 [LAN SEGMENT] # who to send e-mail to destemail = [your e-mail] # who is the email from sender = [your e-mail] # since we're using exim4 to send emails mta = mail # get email alerts action = %(action_mwl)s
Note: Your server will need to be able to send e-mails so Fail2ban can let you know of suspicious activity and when it banned an IP.
We need to create a jail for ssh that tells fail2ban to look at ssh logs and use ufw to ban/unban IPs as needed. Create a jail for ssh by adding this to /etc/fail2ban/jail.d/ssh.local:
[sshd] enabled = true banaction = ufw port = ssh filter = sshd logpath = %(sshd_log)s maxretry = 5
In the above we tell fail2ban to use the ufw as the banaction. Fail2ban ships with an action configuration file for ufw. You can see it in /etc/fail2ban/action.d/ufw.conf
Enable fail2ban and the jail for SSH:
sudo fail2ban-client start
sudo fail2ban-client reload
sudo fail2ban-client add sshdTo check the status:
sudo fail2ban-client statusStatus |- Number of jail: 1 `- Jail list: sshd
sudo fail2ban-client status sshdStatus for the jail: sshd |- Filter | |- Currently failed: 0 | |- Total failed: 0 | `- File list: /var/log/auth.log `- Actions |- Currently banned: 0 |- Total banned: 0 `- Banned IP list:
I have not needed to create a custom jail yet. Once I do, and I figure out how, I will update this guide. Or, if you know how please help contribute.
To unban an IP use this command:
fail2ban-client set [jail] unbanip [IP][jail] is the name of the jail that has the banned IP and [IP] is the IP address you want to unban. For example, to unaban 192.168.1.100 from SSH you would do:
fail2ban-client set sshd unbanip 192.168.1.100The kernel is the brains of a Linux system. Securing it just makes sense.
Changing kernel settings with sysctl is risky and could break your server. If you don't know what you are doing, don't have the time to debug issues, or just don't want to take the risks, I would advise from not following these steps.
I am not as knowledgeable about hardening/securing a Linux kernel as I'd like. As much as I hate to admit it, I do not know what all of these settings do. My understanding is that most of them are general kernel hardening and performance, and the others are to protect against spoofing and DOS attacks.
In fact, since I am not 100% sure exactly what each setting does, I took recommended settings from numerous sites (all linked below) and combined them to figure out what should be set. I figure if multiple reputable sites mention the same setting, it's probably safe.
If you have a better understanding of what these settings do, or have any other feedback/advice on them, please let me know.
I won't provide For the lazy code in this section.
The sysctl settings can be found in the linux-kernel-sysctl-hardening.md file in this repo.
Before you make a kernel sysctl change permanent, you can test it with the sysctl command:
sudo sysctl -w [key=value]Example:
sudo sysctl -w kernel.ctrl-alt-del=0Note: There are no spaces in key=value, including before and after the space.
Once you have tested a setting, and made sure it works without breaking your server, you can make it permanent by adding the values to /etc/sysctl.conf. For example:
$ sudo cat /etc/sysctl.conf
kernel.ctrl-alt-del = 0
fs.file-max = 65535
...
kernel.sysrq = 0After updating the file you can reload the settings or reboot. To reload:
sudo sysctl -pNote: If sysctl has trouble writing any settings then sysctl -w or sysctl -p will write an error to stderr. You can use this to quickly find invalid settings in your /etc/sysctl.conf file:
sudo sysctl -p >/dev/nullIf a bad actor has physical access to your server, they could use GRUB to gain unauthorized access to your system.
If you forget the password, you'll have to go through some work to recover the password.
Create a Password-Based Key Derivation Function 2 (PBKDF2) hash of your password:
grub-mkpasswd-pbkdf2 -c 100000The 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
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.
The update-grub program uses scripts to generate configuration files it will use for GRUB's settings. 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. This tells update-grub to use this username and password for GRUB.
#!/bin/sh
set -e
cat << EOF
set superusers="grub"
password_pbkdf2 grub [hash]
EOFFor example:
#!/bin/sh set -e cat << EOF set superusers="grub" password_pbkdf2 grub grub.pbkdf2.sha512.100000.2812C233DFC899EFC3D5991D8CA74068C99D6D786A54F603E9A1EFE7BAEDDB6AA89672F92589FAF98DB9364143E7A1156C9936328971A02A483A84C3D028C4FF.C255442F9C98E1F3C500C373FE195DCF16C56EEBDC55ABDD332DD36A92865FA8FC4C90433757D743776AB186BD3AE5580F63EF445472CC1D151FA03906D08A6D EOF
Set the file's execute bit so update-grub includes it when it updates GRUB's configuration:
sudo chmod a+x /etc/grub.d/01_passwordMake a backup of /etc/grub.d/10_linux and unset execute bit so update-grub doesn't try to run it:
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.*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.
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_linuxUpdate GRUB with update-grub:
sudo update-grubIf you have sudo configured properly, then the root account will mostly never need to log in directly -- either at the terminal or remotely.
Be warned, this can cause issues with some configurations!
If your installation uses 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.
An alternative to locking the root acount is set a long/complicated root password and store it in a secured, non digital format. That way you have it when/if you need it.
Lock the root account:
sudo passwd -l rootumask 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.
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.
Changing the default umask can create unexpected problems. For example, if you set umask to 0077 for root, then non-root accounts will not have access to application configuration files/folders in /etc/ which could break applications that do not run with root privileges.
Set default umask for non-root accounts to 0027 by adding this line to /etc/profile and /etc/bash.bashrc:
umask 0027
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.bashrcWe also need to add this line to /etc/login.defs:
UMASK 0027
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 Set default umask for the root account to 0077 by adding this line to /root/.bashrc:
umask 0077
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/.bashrcBy 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."
Install libpam-pwquality.
On Debian based systems:
sudo apt install libpam-pwqualityTell 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:
sudo cp --preserve /etc/pam.d/common-password /etc/pam.d/common-password.$(date +"%Y%m%d%H%M%S")
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-passwordEven though SSH is a pretty good security guard for your doors and windows, it is still a visible door that bad-actors can see and try to brute-force in. Fail2ban will monitor for these brute-force attempts but there is no such thing as being too secure.
Using Two Factor Authentication (2FA) / Multi Factor Authentication (MFA) requires anyone entering to have two keys to enter which makes it harder for bad actors. The two keys are:
Without both keys, they won't be able to get in.
Many folks might find the experience cumbersome or annoying. And, acesss to your system is dependent on the accompanying authenticator app that generates the code.
Install it libpam-google-authenticator.
On Debian based systems:
sudo apt install libpam-google-authenticatorMake sure you're logged in as the ID you want to enable 2FA/MFA for and execute google-authenticator to create the necessary token data:
google-authenticatorDo you want authentication tokens to be time-based (y/n) y https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/user@host%3Fsecret%3DR4ZWX34FQKZROVX7AGLJ64684Y%26issuer%3Dhost ... Your new secret key is: R3NVX3FFQKZROVX7AGLJUGGESY Your verification code is 751419 Your emergency scratch codes are: 12345678 90123456 78901234 56789012 34567890 Do you want me to update your "/home/user/.google_authenticator" file (y/n) y Do you want to disallow multiple uses of the same authentication token? This restricts you to one login about every 30s, but it increases your chances to notice or even prevent man-in-the-middle attacks (y/n) Do you want to disallow multiple uses of the same authentication token? This restricts you to one login about every 30s, but it increases your chances to notice or even prevent man-in-the-middle attacks (y/n) y By default, tokens are good for 30 seconds. In order to compensate for possible time-skew between the client and the server, we allow an extra token before and after the current time. If you experience problems with poor time synchronization, you can increase the window from its default size of +-1min (window size of 3) to about +-4min (window size of 17 acceptable tokens). Do you want to do so? (y/n) y If the computer that you are logging into isn't hardened against brute-force login attempts, you can enable rate-limiting for the authentication module. By default, this limits attackers to no more than 3 login attempts every 30s. Do you want to enable rate-limiting (y/n) y
Notice this is not run as root.
Select default option (y in most cases) for all the questions it asks and remember to save the emergency scratch codes.
Now we need to enable it as an authentication method for SSH by adding this line to /etc/pam.d/sshd:
auth required pam_google_authenticator.so nullok
Check here for what nullok means.
sudo cp --preserve /etc/pam.d/sshd /etc/pam.d/sshd.$(date +"%Y%m%d%H%M%S")
echo -e "\nauth required pam_google_authenticator.so nullok # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" | sudo tee -a /etc/pam.d/sshdTell SSH to levearage it by adding this line in /etc/ssh/sshd_config:
ChallengeResponseAuthentication yes
sudo cp --preserve /etc/ssh/sshd_config /etc/ssh/sshd_config.$(date +"%Y%m%d%H%M%S")
echo -e "\nChallengeResponseAuthentication yes # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" | sudo tee -a /etc/ssh/sshd_configRestart ssh:
sudo service sshd restartIt is important to keep your server up-to-date with all security patches. Otherwise you're at risk of known security vulnerabilities that bad-actors could use to gain unauthorized access to your server.
You have two options:
Which option you pick is up to you but I prefer being notified by e-mail when updates are available. This is because an update may break something else. If the server updates it-self then I may not know and, if I do find out, I'll have to scramble to fix it. If it e-mails me when updates are available, then I can do the updates at my schedule.
Install apticron.
On Debian based systems:
sudo apt install apticronSet the value of EMAIL in /etc/apticron/apticron.conf to your e-mail address.
As you use your system, and you install and uninstall software, you'll eventually end up with orphaned, or unused software/packages/libraries. You don't need to remove them, but if you don't need them, why keep them? When security is a priority, anything not explicitly needed is a potential security threat. You want to keep your server as trimmed and lean as possible.
For Debian based distributions, you can use deborphan to find orphaned packages.
Install deborphan.
sudo apt install deborphanRun deborphan as root to see a list of orphaned packages:
sudo deborphanlibxapian30 libpipeline1
Pass it's output to apt to remove them:
sudo apt --autoremove purge $(deborphan)You will want to repeatedly run this command until deborphan no longer returns any orphaned packages.
while [[ $(deborphan | wc -l) != 0 ]] ; do
sudo apt --autoremove purge $(deborphan)
doneFrom https://cisofy.com/lynis/:
Lynis is a battle-tested security tool for systems running Linux, macOS, or Unix-based operating system. It performs an extensive health scan of your systems to support system hardening and compliance testing.
Install lynis. https://cisofy.com/lynis/#installation has detailed instructions on how to install it for your distribution.
On Debian based systems, using CISOFY's community software repository:
sudo wget -O - https://packages.cisofy.com/keys/cisofy-software-public.key | sudo apt-key add -
sudo apt install apt-transport-https
sudo echo "deb https://packages.cisofy.com/community/lynis/deb/ stable main" | sudo tee /etc/apt/sources.list.d/cisofy-lynis.list
sudo apt update
sudo apt install lynisUpdate it:
sudo lynis update infoRun a security audit:
sudo lynis audit systemThis will scan your server, report its audit findings, and at the end it will give you suggestions. Spend some time going through the output and address gaps as necessary.
For any questions, comments, concerns, feedback, or issues, submit a new issue.
This guide comes with ABSOLUTELY NO WARRANTY. Use with caution. I take no responsibility for anything, related to or not related to this guide.
| Back | FazBrowse Home | New Git URL |