I wanted to ensure that anything I did throughout this project was repeatable. I didn't want to simply install a base OS, install and configure the packages I need and then forget about it. I needed something scriptable that I could use to automatically re-creaete any part of the system in case of failure, or if I needed to add an additional host.
After searching for a solution, the obvious choice seemed to be Ansible. It was a platform that I had researched during lockdown after watching a YouTube series Ansible 101 by Jeff Geerling. This seemed like an ideal opportunity to put my learning into practice.
So, what is Ansible? Ansible is an open-source automation tool, used for infrastructure management tasks such as configuration management, application deployment, intraservice orchestration, and provisioning. It is a simple automation language that describes an IT application infrastructure in Ansible Playbooks. These playbooks can then be run to build and configure the infrastructure. Because Ansible is a scriptable language, it ensures that the builds are repeatable without manual intervention.
The playbook I used to build the host serving this site is available on my GitHub repository ansible-playbooks. It makes use of third party roles to install some of the required features (docker, nginx, certbot). The playbook itself is fairly self-explanatory, and if you want to learn more about how to configure and use Ansible yourself, I would recommend that you watch the Ansible 101 series by Jeff Geerling highlightd above.
There are a couple of points in the Ansible script where I need to deal with secrets. These are for access keys to assets like private github container registries, and a shared secret for accessing the WatchTower update endpoint. As I need the script to be repeatable, I want to store the secrets along with the Ansible script so they can be accessed when running the script
- name: Log into ghcf.io registry
community.general.docker_login:
registry: ghcr.io
username: "{{ secrets.GITHUB_ACTOR }}"
password: "{{ secrets.GITHUB_TOKEN }}"
The details are stored in a local file secrets.enc
in the form:
secrets:
GITHUB_ACTOR: "<your github username>"
GITHUB_TOGEN: "<your github PAT token>"
The contents of this file are then encrypted using ansible-vault
:
ansible-vault encrypt secrets.enc
The contents of the secrets.enc
file are now encryped (see below), and in a pre-quantum world, are safe to store alongside the Ansible scripts in the public GitHub repository.
$ANSIBLE_VAULT;1.1;AES256
32616464303463613337383265366162653864343765633437366264653731393564323364346561
6262303637326666613636313536613935326532303061320a663166333064633961643663616562
...
To make use of the secrets when executing the playbook, simply run the following command:
ansible-playbook -i inventory -e @secrets.enc --ask-vault-pass frontend.yaml
This will load and decrypt the secrets.enc
file and make the secrets available to the playbook without ever leaving the file un-encryped on the host machine.
Using Ansible to manage the building of the website infrastructure has enabled me to achieve the key objective of repeatability. I can now build the whole of the hosting infrastructure with a single command, enabling me to deploy the required system configuration to any host with minimal effort.