Objectives

This is Part 4 of a series called Hello, World: Blog.

If you’ve followed all the previous posts, then you have a fully-functioning blog online - congratulations! But before we charge ahead and start adding posts, we should take a moment to consider security. Any web-facing server is vulnerable to attack, and even small sites get hacked. Let’s take some steps to protect ourselves by reducing the attack surface of our server.

In this post, you’ll:

  • Set up public key authentication.
  • Disable password authentication.
  • Configure a basic firewall.
  • Restrict the Git user’s terminal capabilities.

Prerequisites

Public Key Authentication

Currently, our server is set up to use password authentication, which means that you provide a password each time you access the server. The problem is, passwords are only as strong as you make them, and even a strong password is susceptible to brute-force attacks over time. As an alternative, you can set up your server to use public key authentication.

On your local machine, generate a public / private key pair (if you don’t already have one). If you already have a key pair you’d like to use, skip down to ssh-copy-id.

$ ssh-keygen -t rsa

When prompted for a file to save the key, press Enter to accept the default. Then it will prompt for a passphrase (which is basically a password for your public key). It’s recommended to set a passphrase, but you can press Enter to proceed without one.

Once the key is created, install it as an authorized key on the server. Replace bannmoore with the username of your non-root user, and IP_ADDRESS with your server’s IP.

$ ssh-copy-id bannmoore@IP_ADDRESS

Provide your password when prompted. You should see this:

/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
bannmoore@ip_address's password: 

Number of key(s) added:        1

Now try logging into the machine, with:   "ssh 'bannmoore@IP_ADDRESS'"
and check to make sure that only the key(s) you wanted were added.

Note: If you want to use the same SSH key for multiple users, you’ll need to copy them individually. If you’ve followed this tutorial, make sure you also install the key for your Git user. Don’t install the ssh key on your root user (you’ll see why in a minute).

$ ssh-copy-id git@IP_ADDRESS

Once the key is installed, future logins from your local machine should use public key authentication. Test it out using ssh.

$ ssh bannmoore@IP_ADDRESS

Disable Password Authentication

After the SSH keys are installed, we should disable password authentication entirely. This will prevent anyone from accessing the server without your key, and is much more secure.

Note: This probably goes without saying, but don’t do this unless you’ve verified that public key authentication (the previous section) is working. If you disable password authentication without any keys installed, you’ll lock yourself out of the server.

Login to the server as your non-root user, then open the SSH daemon configuration in Nano.

bannmoore@blog:~$ sudo nano /etc/ssh/sshd_config

Change PasswordAuthentication to no. PubkeyAuthentication and ChallengeResponseAuthentication should be correctly set if you haven’t modified SSH settings, but double-check those as well.

PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no

Save and close the file (CTRL-X Y ENTER). Then, reload the SSH Daemon.

bannmoore@blog:~$ sudo systemctl reload sshd

That’s it! From now on, you cannot connect remotely to the server using password authentication. Any user account without an installed SSH key will be effectively inaccessible. If you need to re-enable it later, you can do so by logging in using a key and modifying the sshd_config file again.

Set up a Firewall

We should install a firewall on the server to restrict incoming connections. If you’re using DigitalOcean’s Ubuntu server, UFW will already be installed. If for some reason it isn’t, install it using apt-get.

bannmoore@blog:~$ apt-get install ufw

The firewall will restrict all incoming connections by default, but there are specific applications we want to allow through. Namely, Nginx and OpenSSH.

These applications automatically add UFW profiles when they are installed. To see a list of available UFW profiles, use the ufw app list command.

bannmoore@blog:~$ sudo ufw app list
Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH

The ufw allow command configures the firewall to allow a specific profile. Allow the “OpenSSH” and “Nginx HTTP” profiles. Note that we only need to enable one Nginx profile - for now, we’ll select HTTP since our server hasn’t been configured for HTTPS (yet).

bannmoore@blog:~$ sudo ufw allow OpenSSH
Rules updated
Rules updated (v6)
bannmoore@blog:~$ sudo ufw allow 'Nginx HTTP'
Rules updated
Rules updated (v6)

Now we can enable the firewall.

bannmoore@blog:~$ sudo ufw enable

Once the UFW firewall is running, you can check it using ufw status.

bannmoore@blog:~$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6) 

If everything is set up correctly, you should still be able to SSH into the server and access the IP address in the browser.

Secure the Git User

In a previous tutorial, we created a Git user to manage our code repository. This user cannot use sudo, so it’s less powerful than the non-root user, but it still has more access than it needs. For example, if you SSH in as the Git user, you get access to the full interactive shell. In this section, we’ll configure this user with a non-interactive git-shell so that it can only be used to perform Git operations.

Log into the server as the non-root user, then su into the Git user.

$ ssh bannmoore@IP_ADDRESS
bannmoore@blog:~$ su - git
Password: 
git@blog:~$ 

As the git user, create a git-shell-commands folder in the home directory.

git@blog:~$ mkdir ~/git-shell-commands

Open a file called no-interactive-login in Nano.

git@blog:~$ nano ~/git-shell-commands/no-interactive-login

Paste in this content:

#!/usr/bin/env bash

printf '%s\n' "You've successfully authenticated to the server as $USER user, but interactive sessions are disabled."

exit 128

Ensure that this file is executable.

git@blog:~$ chmod +x ~/git-shell-commands/no-interactive-login

Now the git user is ready to use git-shell. Logout of the Git user, back to the non-root user.

git@blog:~$ exit
logout
bannmoore@blog:~$ 

As the non-root user, modify the git user to use git-shell instead of the default Bash shell.

sudo usermod -s $(which git-shell) git

Now, if you try to access the git user (through su or ssh), the terminal will run the no-interactive-login script, which displays a message and closes the shell.

bannmoore@blog:~$ su - git
Password: 
You've successfully authenticated to the server as git user, but interactive sessions are disabled.

If necessary, you can use the usermod command to re-enable the git user’s interactive shell.

sudo usermod -s /bin/bash git

Summary

With that, our server is more secure! We’re almost done - in the next and final post), we’ll finalize our site with DNS and HTTPS.