Configuring a fresh machine with Ubuntu Server 16.04 LTS

Let’s say you’ve just purchased a new VPS to play with. There are a few things you should consider configuring if you want your server to be secure and ready to run your development experiments.

In this post I’ll go step-by-step through:

  • configuring a secure SSH access on a custom port, with password authentication turned off
  • configuring a basic firewall with iptables
  • updating the distribution to the newest kernel with LTS Enablement Stack
  • verifying if automatic security updates are enabled
  • enabling system load information in MOTD (previously enabled by default in Ubuntu 14.04 LTS)
  • adding your server’s ASCII art to MOTD 🙂
  • installing newest Docker

This guide is based on configuring a fresh instance of the cheapest VPS purchased on Aruba Cloud (€1,00/month). Hopefully, there will be another post comparing the cheapest VPSs I know of.

VM installation

This guide assumes that you have your fresh VM already created and running.

In Aruba Cloud, I’ve chosen the datacenter in Germany – “DC5-DE”, because it seemed to be most stable and with good latency, based on opinions found on the internet. I clicked to create a new server “Cloud Smart” and chose “Ubuntu Server 16.04 LTS 64bit” as the server’s template. I’ve generated a long, random root password, but found out that maximum allowed length was 20.

The server was available a few minutes later. After getting it’s IP, I’ve added it to my local hosts file, so that I don’t have to remember the IP address:

jakub@local:~$ echo "123.45.56.78 bernard" >> /etc/hosts

I like to name my servers with a distinct name, which I get attached to. Since it’s a VPS I don’t plan to automatically provision (for now at least), it falls into the “pets” category as described in the “Pets vs. Cattle” analogy. In this case, I’ve chosen to name it after one of the characters from the Westworld TV Series – Bernard. There are many more characters to choose from, so that my servers are named consistently.

Configuring a secure SSH access

The first thing worth starting with, is making SSH access more secure. I’ll disable logging in by password and only allow to login with a key. I’ll also make SSH listen on a port different than 80. Those two things will greatly reduce the amount of brute-forcing bots hammering the server and even if they hit me, they won’t be able to break the key. You may also consider enabling fail2ban or even port knocking if you’re slightly paranoid 🙂

I assume that you already have your public key for SSH, if not, you can generate w new one like this:

jakub@local:~$ ssh-keygen -t rsa -b 4096

Now add your public key to authorized_keys on the server. Copy it to your server by running:

jakub@local:~$ cat ~/.ssh/id_rsa.pub | ssh root@bernard "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Remember to check if your SSH key is working by running:

jakub@local:~$ ssh root@bernard

You should be logged in with your public key, without entering the password.

Now let’s edit SSH server settings, I’ll use vim:

root@bernard:~$ vim /etc/ssh/sshd_config

Make sure to set:

Port 6123
Protocol 2
PubkeyAuthentication yes
PasswordAuthentication no

This will make SSH daemon run on port 6123 instead of 22, force Protocol version 2 and disable logging in by password.

Reload the daemon with:

root@bernard:~$ systemctl reload sshd.service

You can now get into the server by running:

jakub@local:~$ ssh root@bernard -p 6123

Configuring a basic firewall with iptables

We’ll block most of the incoming traffic and allow only HTTP, HTTPS and SSH.

You can see default iptables rules by running:

root@bernard:~$ iptables -L

or as commands to run by running:

root@bernard:~$ iptables -S

Let’s now configure allowed connections (6123 – our custom SSH port!) and drop everything else:

root@bernard:~$ iptables -P INPUT ACCEPT && \
  iptables -P FORWARD DROP && \
  iptables -P OUTPUT ACCEPT && \
  iptables -A INPUT -i lo -j ACCEPT && \
  iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT && \
  iptables -A INPUT -p tcp --dport 6123 -j ACCEPT && \
  iptables -A INPUT -p tcp --dport 80 -j ACCEPT && \
  iptables -A INPUT -p tcp --dport 443 -j ACCEPT && \
  iptables -A INPUT -j DROP

If you messed something up, you might get locked out of SSH access, in which case you can use recovery console from your VPS provider to fix things.

If everything is ok, you need to make sure that your changes persists through a system reboot. By default iptables rules are volatile. Let’s use iptables-persistent by running:

root@bernard:~$ apt-get update
root@bernard:~$ apt-get install iptables-persistent

During the installation, you will be asked if you want to save your current rules. Say “yes” and you’re done. After next reboot, your iptables rules will be present.

Updating to the newest kernel (4.8 for 16.04.2 LTS)

If you check your kernel version, you’ll see that it’s a few versions old. Luckily, thanks to LTS Enablement Stack (also called HWE or Hardware Enablement Stack) you can run with kernel 4.8 instead.

root@bernard:~$ uname -a
Linux bernard 4.4.0-62-generic #83-Ubuntu SMP Wed Jan 18 14:10:15 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

Installing HWE to get newer kernel is easy:

root@bernard:~$ apt-get install --install-recommends linux-generic-hwe-16.04

After that upgrade all packages as well:

root@bernard:~$ apt-get upgrade

Now reboot:

root@bernard:~$ reboot

After SSH-ing again you should see:

root@bernard:~$ uname -a
Linux bernard 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 17:11:16 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

When running apt-get update you might get an info saying that some packages “have been kept back”. In that case just run apt-get install on them, for example:

root@bernard:~$ apt-get install linux-generic linux-headers-generic linux-image-generic
root@bernard:~$ apt-get install autoremove

Verifying if automatic security updates are enabled

It’s a good idea to have security updates installed automatically. You should probably have those on (as was the case for me), but it depends on the VM images that your VPS vendor installs.

Check if you have the following file:

root@bernard:~$ cat /etc/apt/apt.conf.d/20auto-upgrades

with:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

And this file:

root@bernard:~$ cat /etc/apt/apt.conf.d/50unattended-upgrades

with something along the lines at the beginning:

// Automatically upgrade packages from these (origin:archive) pairs
Unattended-Upgrade::Allowed-Origins {
 "${distro_id}:${distro_codename}";
 "${distro_id}:${distro_codename}-security";
// "${distro_id}:${distro_codename}-updates";
// "${distro_id}:${distro_codename}-proposed";
// "${distro_id}:${distro_codename}-backports";
};

If yes, then everything is all right. If not, you may need to run:

root@bernard:~$ apt-get install unattended-upgrades
root@bernard:~$ dpkg-reconfigure --priority=low unattended-upgrades

Enabling system load information in MOTD

A few years ago the default Ubuntu Server configuration was that after SSHing, you’d be greeted with a message similiar to:

Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.8.0-41-generic x86_64)

 * Documentation: https://help.ubuntu.com
 * Management: https://landscape.canonical.com
 * Support: https://ubuntu.com/advantage

 System information as of Thu Mar 23 22:32:10 CET 2017

 System load: 0.0 Processes: 140
 Usage of /: 14.7% of 18.18GB Users logged in: 1
 Memory usage: 23% IP address for eth0: 123.45.67.89
 Swap usage: 0% IP address for docker0: 172.17.0.1

 Graph this data and manage this system at:
 https://landscape.canonical.com/

0 packages can be updated.
0 updates are security updates.

Right now, you won’t see those stats. If you like them just as I do, then re-enable them by running:

root@bernard:~$ apt-get install landscape-common

Adding your server’s ASCII art to MOTD

Since we’re tweaking the MOTD (message of the day), let’s add our server’s name as well! 🙂 A nice little app for generating various ASCII-art can be found here.

 ______   _______  _______  _        _______  _______  ______  
(  ___ \ (  ____ \(  ____ )( (    /|(  ___  )(  ____ )(  __  \ 
| (   ) )| (    \/| (    )||  \  ( || (   ) || (    )|| (  \  )
| (__/ / | (__    | (____)||   \ | || (___) || (____)|| |   ) |
|  __ (  |  __)   |     __)| (\ \) ||  ___  ||     __)| |   | |
| (  \ \ | (      | (\ (   | | \   || (   ) || (\ (   | |   ) |
| )___) )| (____/\| ) \ \__| )  \  || )   ( || ) \ \__| (__/  )
|/ \___/ (_______/|/   \__/|/    )_)|/     \||/   \__/(______/ 

Paste the output here:

/etc/motd

You can check the full greeting with:

root@bernard:~$ run-parts /etc/update-motd.d/

Installing the newest Docker

Finally, let’s install Docker to make our devops life easier. Just follow along the official guide – which likes to change URLs, so I’ll dump the commands below:

root@bernard:~$ apt-get install apt-transport-https ca-certificates curl software-properties-common && \
  curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - && \
  add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" && \
  apt-get update && \
  apt-get install -y docker-ce && \
  docker run hello-world