Securing your servers using Ansible

There are some basic security related stuff we need to take care of before deploying any webapp on any Linux server. I learnt these the hard way, googled around, stole it from the pros and consolidated it into an Ansible script so that it can be readily executed on your production machines.

These are specific to Debian based/Ubuntu systems, as that's what I mostly use to deploy my apps. But it wouldn't be too hard to adopt to your OS.

In plain english, these are, in no order,

  1. Don't allow root access into the machine, create a dedicated sudo user who is NOT root.
  2. Disallow root ssh access. If we don't do this, then the attacker might make a brute force attempt to get the root password.
  3. Delete the root password.
  4. Install unattended upgrades.(again, this is very ubuntu specific).
  5. Install a firewall(ufw) and open up your ssh port, HTTP(80) and SSL(443) ports. This is a poor man's version of AWS security group configuration for your servers. You can skip this if you're using AWS security groups. I still include this step so that this deployment process works across as many cloud vendors as possible.
  6. Install and configure fail2ban. It monitors your log files for any brute-force attacks and bans IPs which make too many login attempts.

You could potentially add more to this, like running SSH in a different port etc. but this is the bare minium to get started. NOTE that this is a one-time activity for every server you create.

Now, deploying these security measures to any server would involve secrets, like API keys, passwords and stuff. This would need to be readable and editable by us and Ansible but not for anyone else. Ansible has a feature called vault to facilitate this. You can add secrets to a plain text file and encrypt it, so that it can be part of version control and consumed by Ansible. The encryption/decryption process requires a preset password which can be set at the environment.

The first time you encrypt it, you create and set a password of your choice.

$ export ANSIBLE_VAULT_PASSWORD=yourSuper$ecr3tp@ssword

You then indicate to Ansible to pick the password from the above environment variable for encryption/decryption purposes. Here's the command to encrypt your secrets file:

$ ansible-vault encrypt secrets.yml --vault-password-file=./vault-env

You will have to make sure to encrypt your file before checking in, and every time you want to modify a secret, you have to decrypt it, edit and encrypt back again.

For the first time while running the playbook, make sure that root has SSH access to the target machine. This is provided by default with most cloud vendors.

Let's run this playbook on our newly created server.

$ ansible-playbook -i "example.com," --key-file=/path/to/my/private/key security.yml  --vault-password-file=./vault-env

Let's break this command apart. The -i option mentions the inventory, i.e. the remote machine where the tasks mentioned in security.yml will run. The --vault-password-file argument tells Ansible where to look into for getting the password to decrypt secrets. I'd also recommend that you create an exclusive pair of SSH keys for each server.

Hope this playbook was useful to you. I'll be reusing this playbook for upcoming tutorials and doing stack specific changes wherever needed.

Subscribe and download code

Want to download the code used in this blog post? Please subscribe to my newsletter in which I send my latest posts and tutorials.
Subscribe