Securing a Server

A common use for an automated testing tool is to test the security of a public-facing server.

Automated testing may seem redundant when using automated provisioning software, such as Ansible, that can run sets of commands on multiple hosts to ensure they all are in sync. However, testing has its own advantages: not only is it much faster to test than to provision, but the tests form a specification for the provisioning rules, letting you modify or refactor the commands that get run while still being able to tell they have the same effects.

For now, you will have to install Specsheet on the machine you wish to test.

Disabling root SSH Access

One of the first things that should be done on a new server is to make sure that the root user cannot log in over SSH.

We can test for this by using the fs check to examine the /etc/ssh/sshd_config file, making sure it contains the relevant configuration line.

path = '/etc/ssh/sshd_config'
kind = 'file'
contents = { regex = '^PermitRootLogin no' }
$ specsheet expr.toml

  File ‘/etc/ssh/sshd_config’ is a regular file that matches regex ‘/^PermitRootLogin no/’
   1/1 successful
$ specsheet expr.toml --successes=expand

  File ‘/etc/ssh/sshd_config’ is a regular file that matches regex ‘/^PermitRootLogin no/’
    it exists
    it is a regular file
    its contents matches regex

Checking UFW

ufw, Uncomplicated Firewall, is a simple way to limit which ports are reachable from other machines.

Here, we use the cmd check to run the ufw status verbose command, and checks that it returns the correct string signifying that the firewall is enabled.

name = 'ufw is enabled'
shell = 'sudo ufw status verbose'
status = 0
stdout = { regex = '^Status: active' }

The ufw check can be used to check individual ufw rules. Here, we want to check that even though the firewall is enabled, TCP port 22 is still reachable from the outside world so we can SSH in.

port = 22
protocol = 'tcp'
allow = 'Anywhere'

Checking that services are running

Another thing that can be automatically checked is whether services have started themselves successfully.

The systemd check can be used to check systemd services, and the tcp check can be used to check network services in particular.

In this case, we want to make sure that the nginx service is running, and that TCP port 80 is open.

service = 'nginx'
port = 80