As mentioned in my last post, I’ve decided to try Fedora Linux because of its better compatibility with Podman.
What best way to try it than reinstalling your home server?
But this time I want to do it right. I want to do it with automation.
My home server runs several services:
- it’s my home router
- it’s my dhcp server
- it’s a vpn client, with a vpn to my cloud services (this website lol)
- the vpn is routed with bird through BGP (I’ve first learned it with Quagga, then on Cisco)
- It hosts a RIPE Atlas probe
I plan to set up a series of Ansible playbooks and run all of this on a virtual machine. If the deploy succeeds, I’ll reinstall the actual, physical box.
Part 1 – WTF is a role
I have written ONE ansible playbook so far. From what I could gather, the building blocks are:
- Inventory: a list of hosts
- Playbook: a list of tasks to run
- Roles: …collections.. of playbooks?
Eh, I’ll figure it out.
I started by creating an inventory.yml file:
home: hosts: fedora-server: ansible_host: 192.168.1.102
and an install_htop.yml file
--- # This playbook tries to run yum install htop - name: Playbook to install htop hosts: home become: true tasks: - name: Install htop ansible.builtin.yum: name: htop state: installed
Moment of truth…
Success! What if I run it again?
Exactly as expected, Ansible won’t redo what doesn’t need to be done.
Now can I turn this into a role? Htop is base tooling for all my servers, I want this to be part of a new role called base_install.
Part 2 – Oh, Roles!
Turns out there’s a command to create the base skeleton of a new role
ansible-galaxy role init base_install
This creates the following folder structure:
base_install/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
I’ll move the tasks:
block under tasks/main.yml, like so:
--- # tasks file for base_install - name: Install htop ansible.builtin.yum: name: htop state: installed
And the main playbook is updated like so:
--- # This playbook tries to run yum install htop - name: Playbook to install htop hosts: home become: true roles: - base_install
Moment of truth…
Hey this is easy!
I have configured ssh login with keys, can I tell it to use my private key and my password for sudo so I don’t need to pass them every time interactively with the -kK flags?
Of course you can. You put them in inventory.yml:
home: hosts: fedora-server: ansible_host: 192.168.1.102 vars: ansible_ssh_private_key_file: ~/.id_rsa ansible_become_password: labpassword
This is great and all, but if we check this out to version control everyone will know my password is “labpassword”
Part 3 – Vars and Secrets
We have two different kinds of data to keep:
- The path to the primary key is something we can safely store unencrypted, as it’s just a path to a file
- Our password should be saved encrypted, even at rest
Vars
A variable is a value stored in plaintext in another file that can be recalled somewhere else.
Unfortunately it doesn’t seem possible to load variables from other files from inside an inventory file, so undo the previous step and change install_htop.yml as follows:
--- # This playbook tries to run yum install htop - name: Playbook to install htop hosts: home vars_files: - secret.yml become: true roles: - base_install
This allows us to save the ssh_key_file path in another file (secret.yml) like so:
ansible_ssh_private_key_file: ~/.id_rsa ansible_become_password: labpassword
But this still isn’t safe! Our private key is protected by password on its own, but the become (sudo) password is in plain text!
Secrets
Ansible has a powerful encryption mechanism built-in called Ansible Vault.
We’ll use it to store one value only, for now.
ansible-vault encrypt_string labpassword
New Vault password:
Confirm New Vault password:
Encryption successful
!vault |
$ANSIBLE_VAULT;1.1;AES256
46133653061386437373132386264633537
[...]
Copy the entire value it spewed out in your secrets.yml file in place of “labpassword”
Next time we run our playbook we just need to pass the –ask-vault-password flag and that’s all it will take to decrypt our secrets