Fresh off the lab

The only difference between science and screwing around is writing it down

Migrating to Fedora – programmatically, part 1

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.

Ahh, that sense of freedom only a new project can give you

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


Posted

in

by

Tags: