4 easy steps to make your server less dangerously insecure

There are lots of “very correct” ways to make your server “very secure.” Most of them rely on paid services, complicated agent-manager topologies, and cool buzzwords like “zero trust”.

However, as they say, perfection is the enemy of progress. Many are discouraged by this absolutist approach to server safety, and forget the very basics. Obviously, the expensive and complex solutions exist for a reason, but at the same time a little goes a long way.

So, these are the simple steps that get you about 90% of the way there, and eliminate about 99.5% of common attack vectors. This stuff is so simple there’s really no excuse to not do it.

In order of appearance (and ease of configuration):

  • Setting up your firewall correctly
  • Setting up SSH correctly
  • Using fail2ban
  • Setting up OSSEC
  • Configuring your webserver to reject scans

Set up your firewall correctly

This is by far the most effective, but also the most restrictive. You can’t be brute-forced if port 22 is closed. It’s honestly a no-brainer, just go into your VPS’s control panel and add an ACL to tcp/22 allowing only your own public IP.

Of course, if your public IP changes you’ll need to update this firewall rule, so be careful with this. In my own case, my IP hasn’t changed in over a year, but I know of some others who change frequently. I would advise most people to skip this step unless they know they have a stable public address.

Set up SSH correctly

By using public key authentication only, the attack surface of your server shrinks from “anybody, if they guess enough passwords” down to “only if there’s a zero-day in sshd” - those are good odds, especially with the excellent track record of the OpenSSH project.

If you don’t have an ssh key already, create one:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519

This key uses the newer and faster ed25519 format, which I recommend over RSA.

Then, you simply copy your key to the server and reconfigure the ssh daemon to only accept public key authentication.

ssh-copy-id ongo@srv-01.gablogian.org

After testing passwordless login using the key, disable password login:

/etc/ssh/sshd_config

PasswordAuthentication no

And restart sshd.

Install Fail2ban

Fail2ban is a small script that continuously monitors the auth log, and when multiple failed logins are detected blocks that IP for a certain amount of time. It’s very simple, and also very effective and blocking the badguys.

Installing is easy:

sudo apt install -y fail2ban

That’s it! The daemon will immediately start protecting sshd.

Install OSSEC

OSSEC is a security daemon which acts as a simple IDS/IPS. While it is more commonly used in a client/server model, it can also run in a standalone mode to monitor a single server and common attacks.

As it is not in the software repositories for most distributions, it must be installed from a seperate repository:

wget -q -O - https://www.atomicorp.com/RPM-GPG-KEY.atomicorp.txt  | sudo apt-key add -
source /etc/lsb-release
echo "deb https://updates.atomicorp.com/channels/atomic/ubuntu $DISTRIB_CODENAME main" | \
  sudo tee -a /etc/apt/sources.list.d/atomic.list
sudo apt update

Then, the package itself can be installed:

sudo apt install -y ossec-hids-server

Most of the defaults are fine, but make sure the active-responses are enabled:

/var/ossec/etc/ossec.conf

  <command>
    <name>firewall-drop</name>
    <executable>firewall-drop.sh</executable>
    <expect>srcip</expect>
    <timeout_allowed>yes</timeout_allowed>
  </command>

  <command>
    <name>disable-account</name>
    <executable>disable-account.sh</executable>
    <expect>user</expect>
    <timeout_allowed>yes</timeout_allowed>
  </command>

  <active-response>
    <command>host-deny</command>
    <location>local</location>
    <level>9</level>
    <timeout>3600</timeout>
  </active-response>

  <active-response>
    <command>firewall-drop</command>
    <location>local</location>
    <level>9</level>
    <timeout>3600</timeout>
  </active-response>

Also be sure to check the log file locations:

/var/ossec/etc/ossec.conf


  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/syslog</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/auth.log</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/secure</location>
  </localfile>

It may also be configured to send alerts by email, if your server has a relay configured:

/var/ossec/etc/ossec.conf

<global>
    <email_notification>yes</email_notification>
    <email_to>root</email_to>
    <smtp_server>localhost</smtp_server>
    <email_from>ossec@localhost</email_from>
    <email_maxperhour>6</email_maxperhour>
</global>

Rejecting Scans

If you use a web server like Nginx or Apache, you know what it’s like to see obvious scanners in your logs. While most of these are fairly benign, they can also be a very easy exploit vector. It’s trivial to make a scanner that hits every IPv4 address, but not so simple to scan every subdomain of every domain. Therefore, it’s a great idea to reject all traffic on your default virtualhost to avoid scanners from being able to reach your applications without knowing their hostnames ahead of time.

This nginx config will bounce any non-specified hosts, and can be added to nginx.conf or another config file.

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    server_name_in_redirect off;
    return 301 http://$remote_addr;
    access_log /var/log/nginx/spam.log; 
}
server {
    listen 443 ssl  default_server;
    listen [::]:443 ssl default_server;
    server_name _;
    ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
    ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
    ssl_protocols TLSv1.3 TLSv1.2;
    server_name_in_redirect off;
    return 301 http://$remote_addr;
    access_log /var/log/nginx/spam.log; 
}

This also puts all scanner traffic into the spam.log file, keeping your actual access logs clean. It also makes the job of OSSEC much easier, as it doesn’t need to sift through false attacks, only the real ones targeting your applications.

More advanced configurations such as Modsecurity or NAXSI, or adding a rate limiter are also very good improvements, though the simple step of rejecting scanners is much more effective.


After these simple steps, most servers can become immediately more secure. Of course, it’s still very important to keep up with updates, and be aware of the risks of putting any machine on the internet.

And, as always, it depends entirely on your threat model. If you’re running a simple VPS with WordPress, this will be a very good start. A more widely used (and known) site will need much more complex safeguards.