Skip to content
Basic server hardening using Ansible, for Ubuntu
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
roles/harden
LICENSE
README.md
harden_instances.yml
hosts

README.md

ansible-server-hardening

Complementary repository to blog post at https://able.bio/jibiabraham/basic-server-hardening-using-ansible--127r833

Instructions on how to use this repository.

Prerequisites

Ensure that you have ansible installed. Highly recommended that you install ansible inside a virtualenv.

Resources:

  1. Setup Ansible with Python Virtualenv

Steps to run the ansible playbook

  1. Edit the hosts file to include ip addresses of all your instances
  2. Within ./roles/harden/tasks/main.yml, set the hashed password for the deploy user
  3. Within ./roles/harden/tasks/main.yml, set the proper path to your public SSH key, under the task named Set authorized key taken from file
  4. From the root of the project, run ansible-playbook -i hosts harden_instances.yml

Note: all the commands are run as root user.

FAQ

How do I use the key-pair generated by AWS, on launching the instance, to login to the machine using ansible

In your hosts file, specify the path to the private key file you downloaded from AWS. Assuming the default login user is ubuntu (which for AWS is the case)

<ip_address> ansible_user=ubuntu ansible_ssh_private_key_file=/home/<...>.private_key.pem

How do I create a hashed password for the deploy user

You can use the mkpasswd utility in Ubuntu for the same.

You will be prompted to enter a new password. Once you hit return, the hash for the password you entered will be printed on screen.

mkpasswd --method=SHA-512 --stdin

Verbose explanations for each step in the process

Things we want done on a newly spun up server instance running Ubuntu. TLDR version

  1. Update and upgrade apt packages
  2. Create a new user (deploy) for SSH access and deployments
  3. Setup logging in as deploy user using your local SSH public key
  4. Add deploy user to sudoers
  5. Setup unattended upgrades for the OS
  6. Setup firewall (UFW) for the OS

Update and upgrade apt packages

This one is fairly self explanatory. We want to ensure that all the packages on the system are updated to their latest version.

- name: Update and upgrade apt packages
  apt:
  upgrade: yes
  update_cache: yes
  cache_valid_time: 86400 # Update the apt cache if its older than cache_valid_time.

Ansible apt module documentation

Create a new user for SSH access and deployments

We want to create a new user with sudo access (done later on in the process). This will be the user we use to login to the instance and perform any administrative actions once the hardening process is done.

- name: Create a deploy user
  user:
  name: deploy
  shell: /bin/bash
  password: $6$kcambP/ZjEfvk0d\$0iBhHo9C0jUG2x2GaftkKEvCoffgv2Krb1MQSXlgwH3718pT7GaxZsXkmb0puMa.z3zQAzWxiYtPLVO9gYIXn0

Note that the password for the user must be hashed. There are several ways in which you can create a password hash. I personally prefer using the mkpasswd utility like so

$ mkpasswd --method=SHA-512 --stdin

You will be prompted to enter a new password. Once you hit return, the hash for the password you entered will be printed on screen.

Setup logging in as deploy user using your local SSH public key

Once we finish the hardening process, we want to be able to login to the instance using our local SSH public key. Like so

$ ssh deploy@<instance_ip_address>

As a prerequisite, setup a new SSH key-pair that you want to be able to use with all of your newly created instances. You can follow the steps oultined in this GitHub help page to do so. Assuming you generated a new key-pair as remote_machines and remote_machines.pub,

- name: Set authorized key taken from file
  authorized_key:
  user: deploy
  state: present
  key: "{{ lookup('file', '/home/<your_username>/.ssh/remote_machines.pub') }}"

Note, replace <your_username> with the appropriate value.

Set permissions on the authorized_keys file (on the remote machine), to allow only the file owner to read the file.

- name: Set file permissions on /home/deploy/.ssh/authorized_keys
  file:
  path: /home/deploy/.ssh/authorized_keys
  owner: deploy
  group: deploy
  mode: 0400

Ansible authorized_key module documentation

Ansible lookups documentation

Ansible file module documentation

Linux file permissions documentation

Add deploy user to sudoers

To allow deploy user, created above, to have sudo privileges, we must add the user to the sudoers list

- name: Add deploy to sudoers
  lineinfile:
  path: /etc/sudoers
  state: present
  regexp: "^%deploy"
  line: "%deploy ALL=(ALL) ALL"
  validate: "/usr/sbin/visudo -cf %s"

For additional security, we remove sudo privileges granted by default to certain groups

- name: Disable admin group in sudoers
  lineinfile:
  path: /etc/sudoers
  state: absent
  regexp: "^%admin"
  line: "#%admin ALL=(ALL) ALL"
  validate: "/usr/sbin/visudo -cf %s"

- name: Disable sudo group in sudoers
  lineinfile:
  path: /etc/sudoers
  state: absent
  regexp: "^%sudo"
  line: "#%sudo ALL=(ALL) ALL"
  validate: "/usr/sbin/visudo -cf %s"

And disallow login as root using password

- name: Disable root login via password
  lineinfile:
  path: /etc/ssh/sshd_config
  state: present
  regexp: "^PermitRootLogin"
  line: "PermitRootLogin prohibit-password"

And allow logins only using valid SSH keys

- name: Disable PasswordAuthentication
  lineinfile:
  path: /etc/ssh/sshd_config
  state: present
  regexp: "^PasswordAuthentication"
  line: "PasswordAuthentication no"

Ansible lineinfile module documentation

Setup unattended upgrades for the OS

We want all security updates to be installed automatically. We use the Automatic Updates package from Ubuntu to do this for us.

- name: Install unattended-upgrades package
  apt:
  name: unattended-upgrades
  update_cache: yes

- name: Enable periodic updates
  copy:
  src: 10periodic
  dest: /etc/apt/apt.conf.d/10periodic
  owner: root
  group: root
  mode: 0644

We’re basically copying a file with our required configuration to the remote system. The contents of the file are as such

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

By default, only security updates are configured to be automatically applied. We can also control how frequently apt checks for updates. Read more about the available configuration options on the official Ubuntu help page for the same.

Setup firewall (UFW) for the OS

We want to be able to completely control how other remote machines can talk to our instance. On a fresh machine, the only port that needs to opened is port 22, that allows SSH access. When you install other dependencies later on (perhaps Nginx), make sure to open ports required by those applications.

- name: Enable ufw access for OpenSSH
  ufw:
  rule: allow
  name: OpenSSH

- name: Enable ufw
  ufw:
  state: enabled

Note: UFW is disabled by default and we must explicitly enable it.

Ansible UFW module documentation UFW help page on Ubuntu Community Wiki

Bonus points

You can setup the instance to be able to pull your private Git repositories. The way to go about this is to first create a new SSH key that will have access (granted using the UI on GItHub/GitLab) to your GitHub/GitLab/other projects. And then copy said SSH key-pair to this new instance.

Note, refer to this GitHub help page for creating SSH Keys. The post assumes that the new key-pair created are named as deploy and deploy.pub.

- name: Copy deploy ssh private key
  copy:
  src: deploy
  dest: /home/deploy/.ssh/deploy
  owner: deploy
  group: deploy
  mode: 0600
- name: Copy deploy ssh public key
  copy:
  src: deploy.pub
  dest: /home/deploy/.ssh/deploy.pub
  owner: deploy
  group: deploy
  mode: 0644

You can also configure the ssh program on the instance to use this specific key, when dealing with your GitHub/GitLab projects, by creating a config file for the same.

- name: Copy ssh config file
  copy:
  src: ssh_config
  dest: /home/deploy/.ssh/config
  owner: deploy
  group: deploy
  mode: 0644

The contents of the file specify which key-pair to use and when

Host gitlab.com
    HostName gitlab.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/deploy

And that’s it. Moving to an automated workflow is hard in the beginning. Start small, and gradually move on to more complicated automated flows. I will be posting examples for automating deployments of postgres, elasticsearch, docker and for obtaining SSL certificates (Let’s Encrypt) for your machines, in future posts.

You can’t perform that action at this time.