Save yourself from a disaster #8: Manual Configurations

This is the eighth part of the series Save yourself from a disaster: Redundancy on a budget.

Snowflakes servers are our enemies, we should avoid them like the plague. Our best allied are fail-proof reproducible steps, even better if coded in an actionable code, like Infrastructure as Code.

We went through many steps, are you sure you remember all the steps? Yes, I know there this guide, but what if you want to add another server? What if you leave the company? What if you change provider? Too many “what if”.

Disclaimer
This guide won’t cover everything, it won’t be a comprehensive guide, and the steps that are shown need to be carefully reviewed and tested in your development/pre-production environment. I don’t take any responsibility for any damage, interruption of service nor leak/loss of data for the use of the instructions in the ebook (nor from any external website I’ve mentioned).

Tools

In our toolbox are necessary Ansible and Terraform, these two will be your best friends in documenting the infrastructure and make everything replicable to scale up/out easily.

Those 2 tools are vendor-agnostic, so they can work with any provider and avoid you to lock-in with a configuration management tool, like AWS CloudFormation / CDK.

Other tools for provisioning are Puppet, Chef and SaltStack.

Remember to keep the Infrastructure as Code always up-to-date, avoid any configuration drifting whatsoever.

Creating VMs

For creating the infrastructure we’ll use Terraform.

This is an example of how to create a new VM (or like they call it a Droplet to be precise):

# Create a web server
resource "digitalocean_droplet" "web" {
  image = "ubuntu-20-04-x64"
  name = "web-1"
  region = "fra1"
  size = "s-1vcpu-1gb"
  monitoring = "true"
  ssh_keys = [digitalocean_ssh_key.default.fingerprint]

  depends_on = [
    digitalocean_ssh_key.default,
  ]
}

Just like that we could simply do copy & paste and create many others (even though it is best practice to use the count argument).

Provisioning

---
- name: "Initial Provisioning"
  hosts: all
  become: true

  vars_files:
    - ../vars/init.yml

  roles:
    - oefenweb.swapfile
    - oefenweb.apt
    - ahuffman.resolv
    - ajsalminen.hosts
    - geerlingguy.ntp
    - geerlingguy.firewall
    - dev-sec.os-hardening
    - dev-sec.ssh-hardening
    - uzer.crontab

  tasks:

  - name: Add user manager
    ansible.builtin.user:
      name: "manager"
      shell: /bin/bash
      generate_ssh_key: yes
      ssh_key_type: rsa
      ssh_key_bits: 4096

  - name: Allow manager to have passwordless sudo
    lineinfile:
      dest: /etc/sudoers
      state: present
      insertafter: '^root'
      line: 'manager ALL=(ALL) NOPASSWD: ALL'
      validate: 'visudo -cf %s'

  - name: "Logrotate Configs"
    copy:
      src: "{{ item.src }}"
      dest: "{{ item.dst }}"
    with_items: "{{ app_logrotate_config_items }}"

  - name: Set the policy for the INPUT chain to DROP
    ansible.builtin.iptables:
     chain: INPUT
     policy: DROP

The next post will be about Disaster Recovery Plan, Stay Tuned.

Check out the whole version of this post in the ebook.