Secure Ubuntu 18.04 server setup

🍪 9 min read

This note contains a basic Ubuntu 18.04 server setup. The server will be installed on a encrypted LVM volume which can be unlocked via SSH. This are the settings I found most useful for me, I can’t guaranty a completely secure system by using this notes. If you find any issues or you have suggestions on what I could improve in this setup, just message me.

Installation

For advanced options, such as encrypted LVM volumes, to be available during installation, the image must be loaded from the alternative Ubuntu download page and not from the main website - which provides a image which uses the Subiquity installer. The ISO file with the classic installer, which offers more options, can be found on the Ubuntu alternative download page. Select the image for your type of computer, usually the ubuntu-18.04-server-amd64.iso. The installation is straightforward, just follow the installation instructions. Make sure to select a encrypted LVM volume, the installer will ask you for a security key afterwards. You can also select to install the OpenSSH server, because we’ll use it to access the server.

After a reboot you have to unlock the LVM volume with the security key you selected during the installation.

Updates

First of all update the server.

sudo apt update
sudo apt upgrade

Language / Region settings

I sometimes have problems with language and region settings after installation. To fix this, I set the locales to en_US.UTF-8.

sudo locale-gen en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
sudo locale-gen en_US.UTF-8
sudo dpkg-reconfigure locales

Network setup

This step is optional and depends on how and where you host your server. I need to setup a static IP, which I do in the network settings like follows:

sudo vim /etc/netplan/01-netcfg.yaml

Here’s an example file, the IPs must be changed according to your setup.

network:
  version: 2
  renderer: networkd
  ethernets:
    ens3:
      dhcp4: no
      dhcp6: no
      addresses: [192.168.14.2/24, "2001:4::1/64"]
      gateway4: 192.168.14.1
      gateway6: 2001:4::1
      nameservers:
        addresses: [84.200.69.80, 84.200.70.40, "2001:1608:10:25::1c04:b12f", "2001:1608:10:25::9249:d69b"]

addresses contain the IPs of the server. The rest of the settings should be self-explanatory. After changing the config, apply it by:

netplan apply

IP hardening

We also want to setup some IP settings for security. This can be done in:

sudo vim /etc/sysctl.d/50-ip-sec.conf

I’ll just post my configuration file, which disables ICMP redirects, enables IP spoofing protection, ignores broadcast requests and so on. I also disable IPv6 auto configuration, because it’s manually configured in the step before. Some more information about the various options can be found in the redhat docs - Securing Network Access .

# Disable Source Routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0 
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0

# Disable acceptance of all ICMP redirected packets on all interfaces
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0 
net.ipv6.conf.default.accept_redirects = 0

# Disable send IPv4 redirect packets
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Set Reverse Path Forwarding to strict mode as defined in RFC 3704
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Block pings
net.ipv4.icmp_echo_ignore_all = 1

# Syn flood help
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5

# Log suspicious martian packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians=1
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Disable IPv6 auto config
net.ipv6.conf.default.accept_ra=0
net.ipv6.conf.default.autoconf=0
net.ipv6.conf.all.accept_ra=0
net.ipv6.conf.all.autoconf=0
net.ipv6.conf.eth0.accept_ra=0
net.ipv6.conf.eth0.autoconf=0

# Disable TCP Timestamps
net.ipv4.tcp_timestamps=0

After you’ve created / changed this file, apply the settings:

sudo sysctl --system

Copy your SSH key

If you haven’t selected the option to install the OpenSSH server during the installation process do it now:

sudo apt install openssh-server

We will setup the server to allow SSH access only via SSH keys. Therefore, the public key to be used should be copied to the server at this point. If you don’t have an SSH key yet, it has to be generated - I made a note for that. Here is an example, which needs to be executed on your client system, not on the server, of how a key can be copied to the server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub dennis@dennisnotes.com

I copy my Ed25519 public key for the user dennis to the server dennisnotes.com. Make sure to use the public key (.pub) and not your private key! You’ll be asked to provide your password to copy the key to the server. This is because we still use the default SSH settings. We’ll change this in the next step.

In the last step you should test if the access to the server with the SSH key works. Do this from your client machine. It should open up a SSH connection using your SSH key. Maybe you need to unlock the key by entering the keys password.

ssh dennis@dennisnotes.com

In addition to the Ed25519 key, I copy an RSA key to the server, which will later be used for the Dropbear SSH server to unlock the encrypted LVM volume via SSH:

ssh-copy-id -i ~/.ssh/id_rsa.pub dennis@dennisnotes.com

SSH server configuration

The OpenSSH configuration is located at /etc/ssh/sshd_config.

sudo vim /etc/ssh/sshd_config

I just post the content of my config here. In short I forbid the root login and password logins, I allow the login only for the user dennis, I change the default port 22 to another (e.g. 1234), I use protocol 2 and I allow the login via SSH key. Some more tips and details about the settings can be found in the SSH Docs as well as in the Debian Doc.

Port 1234 # Security by obscurity doesn't work, but it leads to smaller fail2ban logs etc.
AllowUsers dennis

Protocol 2

HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

# Logging
SyslogFacility AUTH
LogLevel INFO

# Authentication:
LoginGraceTime 120
PermitRootLogin no
UsePrivilegeSeparation yes
StrictModes yes
MaxAuthTries 3
MaxSessions 10

PubkeyAuthentication yes
RSAAuthentication yes
AuthorizedKeysFile %h/.ssh/authorized_keys

IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no

PasswordAuthentication no
PermitEmptyPasswords no

ChallengeResponseAuthentication no

UsePAM yes

# Additional settings
X11Forwarding no
PrintMotd no
Banner none
DebianBanner no

AcceptEnv LANG LC_*

Subsystem       sftp    /usr/lib/openssh/sftp-server

After you’ve changed the config file restart the ssh service.

sudo systemctl restart sshd

Firewall

After setting up the SSH server a firewall should be activated to secure the system. We’ll use the uncomplicated firewall (UFW). UFW should be installed by default, if not install it now. afterwards enable the SSH port, which you’ve set in your sshd_config. In the example above this would be port 1234. Afterwards you need to enable the firewall.

sudo apt install ufw
sudo ufw allow 1234/tcp
sudo ufw enable

You should still be able to connect to your system via the ssh port. Test it by executing the following command on your client system:

ssh -p 1234 dennis@dennisnotes.com

Note the -p 1234, with the option -p we set the ssh port of the remote system.

Restrict su

To allow only users in a given admin group to switch users - su - execute the following steps in which you create a admin group, add a user dennis to this group and restrict the access to /bin/su to the admin group.

sudo groupadd admin
sudo usermod -a -G admin dennis
sudo dpkg-statoverride --update --add root admin 4750 /bin/su

fail2ban

Fail2ban is an intrusion prevention system that basically monitors log files and searches for certain patterns corresponding to a failed login. If a certain number of failed login attempts are detected from an IP address within a certain time, fail2ban blocks access for this IP address by creating a corresponding firewall rule. First of all install fail2ban:

sudo apt install fail2ban

Fail2Ban can be configured via configuration files in /etc/fail2ban/jail.d. Further filters can be created in /etc/fail2ban/filter.d. Currently our system is only accessible via SSH, so we should fail2ban watch the SSH access. To do so create a new configuration file like follows:

sudo vim /etc/fail2ban/jail.d/ssh.conf

Here is a example configuration file.

[sshd]

enabled  = true
port     = 1234
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3

It is a relatively simple configuration that specifies that we monitor SSH access to port 1234, whose log files are located at /var/log/auth.log. To check the log file for failed logins, the filter sshd, which is included in the installation, is used. After three failed login attempts the corresponding IP address will be banned. It is also possible to notify the administrator by e-mail if IP addresses have been banned, etc. There are several sources on the Internet, such as the official fail2ban documentation.

After a new configuration has been added, the fail2ban service must be restarted. After the restart, the new configuration should appear in the status query from the fail2ban client, which can then also be viewed in detail.

sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd

LVM Volume - SSH unlock via Dropbear

To unlock the LVM volume via SSH on a reboot we need a little SSH server which is only used during the boot process. This is where Dropbear comes in. Install it via:

sudo apt install dropbear-initramfs

Next we need to set the port we want to use, as well as some other options:

sudo vim /etc/dropbear-initramfs/config

Here is a example of how my configuration looks like:

DROPBEAR_OPTIONS="-p 1234 -s -j -k -I 60"

In the next step you have to specify the RSA key you want to use later. I already copied my id_rsa.pub key to the server in a previous step, it can be found at ~/.ssh/authorized_keys. The key, e.g. ssh-rsa blablakey comment, must now be copied. At the end it must be added to the authorized_keys of dropbear:

sudo vim /etc/dropbear-initramfs/authorized_keys

We add some more options like no X11 forwarding etc. for security, my file looks like this:

no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="/bin/cryptroot-unlock" ssh-rsa blablakey comment

Finally you have to update the initramfs:

sudo update-initramfs -u

Setup the IP Address in grub

I need a static IP which I setup in a step before, which is only loaded after I decrypted the LVM volume and booted Ubuntu. To set the IP address before I add the following flag in the GRUB config.

sudo vim /etc/default/grub 

Heres an example of my GRUB_CMDLINE_LUNX_DEFAULT setting in which 192.168.14.2 is the servers IP, 192.168.14.1 the Gateway and dennisnotes the hostname.

GRUB_CMDLINE_LINUX_DEFAULT="ip=192.168.14.2::192.168.14.1:255.255.0.0:dennisnotes:ens3:none:"

After you’ve added this you need to recreate the grub.cfg file like follows:

sudo update-grub

Test the SSH LVM Volume unlock

  • Reboot
  • ssh root@dennisnotes.com -p 1234
  • type pw
  • done

This is my base server setup. In further notes I will explain how to set up nginx, Nextcloud via Docker images with a nginx proxy and so on.