How to secure an Ubuntu server using Tailscale and UFW

Any server on the public internet is bound to be attacked by bots looking for weak or leaked passwords and unsafely configured services. Even security experts can misconfigure a database, or an unwitting member of the team can accidentally open up a vulnerability, leaving your devices or network open to attack.

Note

If you have an existing server, you can view this bot traffic by running sudo less /var/log/auth.log. If your server is like many on the web, you’ll see lots of “invalid user admin” or “invalid user test”.

Tailscale simplifies network security by letting you keep your servers away from the public web, while keeping it easy to connect.

The best way to secure a server with Tailscale is to accept connections from Tailscale, and ignore any public internet traffic. Since your Tailscale network is invisible, except to those in your network, attackers won’t even be able to find it.

Prerequites

Before you begin this guide, you’ll need an Ubuntu server to secure. This guide assumes you’re setting up a DigitalOcean Ubuntu 18.04 server, but the steps should be similar for most hosting providers and versions of Ubuntu.

You’ll also need a Tailscale account. You can make a free solo account using a @gmail.com address.

Step 1: ssh into your new server

After spinning up a new server, ssh into it with your account details.

ssh <username>@<server host ip>

Step 2: Install Tailscale on your server

You can find instructions at the link below.

Once Tailscale is installed, and you’ve run tailscale up on your server, continue to the next step.

Step 3: ssh over Tailscale

An important step — since we’re about to restrict ssh access to be only over Tailscale, we’ll exit the machine and re-ssh with our Tailscale IP.

First, find and copy your machine’s Tailscale IP. The easiest way to do this is to run

ip addr show tailscale0

And copy the 100.x.y.z address as shown below.

Find your Tailscale IP with ip addr show tailscale0

Once you’ve found it, exit your ssh session, and start a new one with your newly copied Tailscale IP.

ssh <username>@<copied 100.x.y.z address>

Step 3: Allow access over Tailscale

For this guide, we’ll use UFW (Uncomplicated Firewall) to restrict traffic to our server. It comes pre-installed on Ubuntu 18.04, so no installation is needed.

First, we’ll set a rule to accept any incoming ssh connections over Tailscale. Tailscale uses the tailscale0 interface and port 41641 for connections, so we’ll instruct ufw to allow any traffic over those

sudo ufw allow in on tailscale0 to any port 22
sudo ufw allow 41641/udp

Step 4: Enable UFW

Before we continue editing rules, you’ll need to enable UFW if it isn’t already.

sudo ufw enable

Step 5: Restrict all other traffic

Next, we’ll set up rules to reject all incoming traffic, and allow all outgoing traffic by default.

sudo ufw default deny incoming
sudo ufw default allow outgoing

Now that we’ve set these defaults check your existing firewall rules you might need to keep.

sudo ufw status

You’ll see a list of firewall rules, like this:

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere
22 on tailscale0           ALLOW IN    Anywhere
41641/udp                  ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)
443/tcp (v6)               ALLOW IN    Anywhere (v6)
22 (v6) on tailscale0      ALLOW IN    Anywhere (v6)
41641/udp (v6)             ALLOW IN    Anywhere (v6)

We want to limit this list to the minimum set needed.

To completely lock down your server while retaining ssh access, you could delete every rule except for the “22 on tailscale0” and “41641/udp” rules.

For the example above, I’ll delete all 22/tcp rules, which will remove the ability to ssh over regular connections:

sudo ufw delete 22/tcp

Now, only “22 on tailscale0” remains, meaning ssh can only occur over Tailscale.

To                         Action      From
--                         ------      ----
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
22 on tailscale0           ALLOW       Anywhere
41641/udp                  ALLOW       Anywhere
80/tcp (v6)                ALLOW       Anywhere (v6)
443/tcp (v6)               ALLOW       Anywhere (v6)
22 (v6) on tailscale0      ALLOW       Anywhere (v6)
41641/udp (v6)             ALLOW       Anywhere (v6)

If you expose a web service (80/tcp, 443/tcp), you’ll want to keep those rules around. For less public services like FTP (21/tcp) or a database, consider connecting devices that rely on those services over Tailscale too.

Note

This guide assumes ssh is running on the default port, 22. If you’ve changed your ssh port, you’ll need to change these instructions as well.

Step 6: Restart ufw and ssh

Once you’ve set up firewall rules to restrict all non-Tailscale connections, restart ufw and ssh

sudo ufw reload
sudo service ssh restart

Done! Now your server will ignore any ssh requests, except for users who you have invited to your private Tailscale network.

Optional: enable MFA for all ssh connections

Now that your server can only be accessed via Tailscale, you can enforce login rules in using your Tailscale network’s identity provider, knowing they will apply to all your ssh connections too.

For example, you may want to configure your identity provider to require multi-factor authentication (MFA) for every sign-in.

Note

Thanks to /u/mgozmovies whose experimentation and write-up on /r/tailscale inspired this article.

Last updated