This is a very short, quick and dirty guide to setting up a first server. Basically this servers as a memory aid for the auther, and most of the content is inspired by tutorials from DigitalOcean.
Getting a Server
DigitalOcean is definitely an amazing service provider for developers, from the beginner to the expert. I suggest creating an account with them and getting a cheap droplet to start on your journey.
To get started with web development, simply create a Ubuntu 20.04 Focal Fossa LTS droplet. For now, allowing root access with a password is just fine, a key-based authentication can be set up later on.
A Non-Root User
The root user has very broad privileges and can pretty much do whatever it wants. It is thus not very prudent to log in as the root user all the time. Thus, the first thing to do now, is to create another user using the adduser command (replace symplectos with whatever username you desire, obviously):
To allow the newly created user to sometimes carry out administrative tasks as root, the newly created used has to be added to the superuser do group, or sudo, for short. This can be done with the usermod command:
usermod -aG sudo symplectos
To protect the newly created server against intrusions, a basic firewall should be set up.
The DigitalOcean droplet comes installed with Uncomplicated Firewall, or UFW, for short.
By default, UFW blocks all incoming and outgoing requests. A very handy feature of UFW is that it recognized standard applications and thus allows for very easy management of common firewall rules. Please note that all the following commands must be run as root or with superuser do.
To see a list of all available applications recognized by UFW, simply type
ufw app list
Since this is a newly installed server, the output should look as follows:
Available applications: OpenSSH
Obviously ssh traffic should be allowed to pass through the firewall, else it will be impossible to remotely log in to the server at all. To allow an application to go through the firewall, simply run the following command:
ufw allow OpenSSH
To finally enable the firewall, type:
The status of UFW can be checked via the following command:
In the current context, the output should look as follows:
Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6)
To further harden the security of the server, I suggest disabling remote access for the root user and to only allow key-based authentication for the newly created non-root user.
Create SSH Keys with OpenSSH
The standard OpenSSH installation contains a tool to create new SSH keys. By default, a 2048-bit RSA key pair is created.
ssh-keygend -f ~/.ssh/symplectos-key-rsa -t rsa -b 4096
Elliptic curve algorithms are more modern. The Elliptic Curve Digital Signature Algorithm (ECDSA), with a key size of 521 bits, can be used as follows:
ssh-keygen -f ~/.ssh/symplectos-key-ecdsa -t ecdsa -b 521
I suggest using a passphrase to protect the key.
To use public key authentication, the public key must be copied to the server and installed as an authorized key. A convenient way to do so, is to use the ssh-copy-id command as follows:
ssh-copy-id -i ~/.ssh/symplectos-key-ecdsa firstname.lastname@example.org
Once the public key has been configured on the server, any user in possession of the private key will be allowed to log in remotely.
If the ssh-copy-id is not available, proceed to the Using an Already Existing Key section to proceed.
Creating SSH Keys with Putty
To create ssh keys with Putty, puttygen must first be downloaded from the Putty website.
Once downloaded, start the program and create the keys. The same remarks as above, i.e. ECDSA is probably better than RSA, do apply.
To add the private key to the server, proceed to the next section.
Using an Already Existing Key
There are two options to upload an already existing key, not counting the ssh-copy-id command.
Piping Into SSH with Password-Based Access
Make sure the ~/.ssh folder exists on the remote server, and then append the piped contents of the public key into the ~/.ssh/authorized_key file as follows:
cat ~/.ssh/symplectos.pub | \ ssh email@example.com "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
After entering the password to authenticate the user, the key will be copied and key-based access will be available henceforth.
Manually without Password-Based Access
On the local machine, output the contents of the public key, either via puttygen or, on Linux, by using cat, for example, and copy the output:
On the server, create the ~/.ssh directory:
To append the public key to the authorized_keys file, open that file with a text editor and paste the above copied content into the file:
echo "ssh-rsa EXAMPLE...== firstname.lastname@example.org" \ >> ~/.ssh/authorized_keys
Note, change ssh-rsa to ssh-ecdsa if using the ECDSA algorithm.
Last but not least permissions for the folder and the authorized_keys file must be set to 700 and 600 respectively. Also, make sure the non-root user owns the directory and the files. To do so, run the following command with superuser do:
chmod -R go= ~/.ssh chown -R $USER:$USER ~/.ssh
To restrict ssh access to only those users within a specific group, create that group and add the desired users:
sudo groupadd sshusers sudo usermod -a -G sshusers symplectos
To configure the sshd service, a configuration file can be created in the /etc/ssh/sshd_config.d/ folder.
The following example disabled all password-based access. For an explanation of each parameter, check the official manpage
sudo vim /etc/sshd_config.d/bell0bytes.conf # set log level to verbose to have a clear audit track of which key was used to login LogLevel VERBOSE # don't let users set environment variables PermitUserEnvironment no # only user the newer, more secure protocol Protocol 2 # disable X11 forwarding 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 # don't allow password auth PasswordAuthentication no # disallow root login PermitRootLogin no # ignore .rhosts and .shosts IgnoreRhosts yes HostbasedAuthentication no # verify hostname matches IP UseDNS no # disable compression after login Compression no # send keep alive messages TCPKeepAlive no # disallow agent forwarding AllowAgentForwarding no # allow only certain user groups AllowGroups sshusers # maximum number of client alive messages sent without response ClientAliveCountMax 0 # timeout in seconds before a response request ClientAliveInterval 300 # local addresses sshd should listen on # ... # time in seconds before login times out LoginGraceTime 30 # maximum allowed attempts to login MaxAuthTries 3 # maximum number of open sessions MaxSessions 2 # maximum number of login sessions MaxStartups 3
After restarting the sshd service
sudo systemctl restart sshd
it should be possible for the non-root user to log in to the server with key-based access.
Intrusion Detection with PSAD
PSAD, also known as Port Scan Attack Detector, is a collection of lightweight system daemons that run on Linux systems and analyze the log messages of iptables to detect port scans and other suspicious traffic. PSAD is used to change an Intrusion Detection System into an Intrusion Prevention System.
A serverfault user, FINESEC, explained this quite well:
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.
Note: See the next section for Fail2Ban instructions.
To install psad, simply run
sudo apt install psad
Configuration is done in the /etc/psad/psad.conf file. Check the official docs for a list of all possible parameters.
This parameter specifies the name of the server, i.e.
This parameter can be used to define a home network, i.e. a safe network, whose IP addresses should not be considered as attackers. Networks should be specified in CIDR notation and multiple networks can be given as a comma separated list:
HOME_NET 192.168.178.0/24, 2001:7e8:c87b:d800::/64;
This setting might be fine for a homelab, but for a droplet on Digital Ocean, I am not completely comfortable with setting the entire IP range as a HOME_NET.
This parameter specifies how PSAD is supposed to send alerts. Since I will eventually set up a monitoring tool, I usually set this parameter to noemail:
If set to Y, PSAD will watch init services.
If this parameter is set to Y, PSAD will automatically block malicious IPs by adding rules into iptables.
By setting this parameter to Y, TCP information will be logged and analyzed as well, which helps to detect certain scan or attack signatures.
This parameter specifies the location of the syslog file, which should be set to /var/log/syslog on Ubuntu.
This parameter specifies for how long IPs are banned. The default is an hour, i.e. 3600s.
To make UFW play nice with psad, tell UFW to log all traffic in such a way that psad can analyze it. This can be done by adding the following line to the two files before.rules and before6.rules in the /etc/ufw directory, at the very end of the files, just before the COMMIT:
# log all traffic so psad can analyze it -A INPUT -j LOG --log-tcp-options --log-prefix "[IPTABLES] " -A FORWARD -j LOG --log-tcp-options --log-prefix "[IPTABLES] "
Reload UFW and PSAD
Reload UFW and PSAD by typing:
sudo systemctl restart ufw sudo psad -R sudo psad --sig-update sudo psad -H
Analyize and Check
To analyze traffic, simply run:
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.
And to check the status of psad, run:
sudo psad --Status
The output might look like this:
[+] Top 50 signature matches: "P2P Napster Client Data communication attempt" (tcp), Count: 4, Unique sources: 2, Sid: 564 "MISC Microsoft SQL Server communication attempt" (tcp), Count: 2, Unique sources: 1, Sid: 100205 "POLICY HP JetDirect LCD commnication attempt" (tcp), Count: 2, Unique sources: 1, Sid: 568 "SCAN UPnP communication attempt" (udp), Count: 1, Unique sources: 1, Sid: 100074 [+] Top 25 attackers: 18.104.22.168 DL: 2, Packets: 1, Sig count: 1 22.214.171.124 DL: 2, Packets: 3, Sig count: 3 126.96.36.199 DL: 2, Packets: 2, Sig count: 2 188.8.131.52 DL: 2, Packets: 12, Sig count: 2 184.108.40.206 DL: 2, Packets: 1, Sig count: 1 220.127.116.11 DL: 1, Packets: 6, Sig count: 0 18.104.22.168 DL: 1, Packets: 7, Sig count: 0
To unblock all the IP addresses blocked by PSAD, ru the following command:
To unban a specific IP address, run:
psad --fw-rm-block-ip 192.168.178.1
PSAD monitors network activity to detect and prevent potential intrusions. Fail2Ban can be used to protect additional applications or services that might run on a server, such as ssh, apache or nginx.
Fail2Ban is a free and open source Intrusion Prevention Software tool written in Python. The main purpose of Fail2ban is thus to scan the log files of various services, such as SSH, FTP, SMTP, Apache or NGINX, and to block those IP addresses that perform too many suspicious actions, such as failed login attempts or script injection attempts.
If key-based access is enabled, it is still a good idea to activate fail2ban, especially since it can later on be used to safeguard apache and nginx webservers as well.
To install fail2ban, simply run
sudo apt-get install fail2ban
Configuring fail2ban is done using a local jail file (to be sure that a package upgrade won't overwrite local changes). To get started, copy the already existing conf file:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
The first thing to configure is to tell fail2ban to use UFW instead of iptables, and to add any local, or safe, IP addresses to be ignored, meaning that access from these addresses will never be banned. Usually this should be set to the loopback addresses as well as any internal addresses:
ignoreip = 127.0.0.1/8 ::1 192.168.178.0/24 2001:7e8:c87b:d800::/64 banaction = ufw
Once done, enable and start fail2ban:
sudo systemctl enable fail2ban sudo systemctl start fail2ban
By default, fail2ban protects sshd by parsing its log file, which is located at /var/log/auth.log. To check the filter fail2ban is using to parse the lock, open the /etc/fail2ban/filters.d/sshd.conf file. Note that to each jail, defining the actions to be taken on an infraction, corresponds a filter file, with the same name, specifying how to parse a specific log file.
To check the status of fail2ban and all its jails, the fail2ban-client can be used as follows:
sudo fail2ban-client status sudo fail2ban-client sshd
Status |- Number of jail: 1 `- Jail list: sshd Status 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:
Manually Banning and Unbanning IPs
To ban or unban IP addresses, the unbanip and the banip commands be used, respectively:
sudo fail2ban-client set sshd unbanip 123.456.789.012 sudo fail2ban-client set sshd banip 123.456.789.012
Fail2Ban in Action
As a note: In about a year, Fail2ban blocked 2753 IP addresses trying to access my home server:
Status for the jail: sshd |- Filter | |- Currently failed: 0 | |- Total failed: 127 | `- File list: /var/log/auth.log `- Actions |- Currently banned: 2753 |- Total banned: 2753