ansible Vault
Bei der Nutzung von ansible muss man ab und an auch sensible Daten (z.B. Passwörter) in Variablen hinterlegen. Diese werden dann in einem Repository zum Beispiel dem gesamten Team zur Verfügung gestellt. Da will man die sensiblen Daten natürlich nicht im Klartext ablegen, sondern man muss sie verschlüsselt speichern können. Und genau hier kommt ansible vault zum Einsatz.
Methoden
Es gibt zwei Möglichkeiten, Daten in ansible zu verschlüsseln. Entweder man verschlüsselt einzelne Werte und hinterlegt diese in einer Datei zusammen mit anderen Variablen die im Klartext erfasst sind oder man verschlüsselt den gesamten Inhalt einer Datei. Beide Methoden können natürlich auch gleichzeitig verwendet werden. Unter Umständen ist es manchmal sogar sinnvoll, zwei oder mehr Vaults zu verwenden. Ein denkbares Szenario wäre zum Beispiel, das man einen Vault für die Production-Umgebung, einen anderen Vault für die Development-Umgebung verwendet. So können unterschiedliche Teams mit verschiedenen Passwörtern arbeiten. Oder aber jede Rolle enthält einen eigenen Vault. Viele Möglichkeiten mit verschiedenen Vor- und Nachteilen, dass sollte bei der Konzeption des ansible-Repositories vorab berückstichtig werden.
Vault-IDs
Wenn man mehrere Vaults mit verschiedenen Passwörtern verwendet, dann muss man die Vaults irgendwie unterscheiden können. Hierzu verwendet man IDs. Diese IDs sind in Vaults nicht verschlüsselt hinterlegt und im Klartext einsehbar. Sie können daher auch als eine Art "Hinweis" verstanden werden, der angibt, welches Kennwort hier verwendet werden muss. Bei jedem Vorgang (erstellen, ansehen oder bearbeiten) eines Vaults gibt man die ID mit dem Parameter --vault-id an. Es folgt als erstes die ID (z.B. prod) gefolgt von der Angabe, wo das Passwort zu finden ist (z.B. der Pfad zur Datei oder einem Skript). Es kann statt der Datei auch prompt verwendet werden, dann wird man aufgefordert, ein Passwort einzugeben.
Natürlich kann man sich die Passwörter einfach merken, aber das will ja keiner. Daher speichere ich meine Passwörter in einer Datei (diese Datei wird NIE in das Repository eingecheckt und darüber verteilt). Hier gibt es zwei Optionen:
- Die Datei enthält nur ein Passwort für ein Vault. Jeder Vault nutzt eine eigene Datei.
- Die Datei enthält mehrere Passwörter zu verschiedenen Vaults. Dann muss jedem Passwort die ID des zugehörigen Vaults vorangestellt werden.
How to
So viel zur Theorie, nun zur Praxis. Erstell dir einen Ordner ansible-vault und darin einen Unterordner repository. Im Unterordner repository erstellst du noch einen Ordner group_vars. Damit hast du die grundlegende Ordnerstruktur für dieses Beispiel erstellt.
Nun erstellst du die Passwort-Datei. Dazu legst du im Ordner ansible-vault eine Datei namens ansible.pass an und hinterlegst dort zuerst die Vault-IDs und dann, durch einen Tabulator getrennt, das jeweilige Passwort.
prod OhM4aiHe
dev Oogh4Ach
Erstellen eines neuen Vaults
Jetzt erstellst du dir den ersten Vault prod_vault.yml im Ordner group_vars mit dem unten folgenden Befehl. Zur Verschlüsselung verwenden wir das von uns in der Datei ansible.pass vorher festgelegte Passwort zu der Vault-ID prod:
ansible-vault create --vault-id prod@ansible.pass --encrypt-vault-id prod repository/group_vars/prod_vault.yml
Der Parameter --vault-id gibt an, welche ID und welches zugehörige Passwort aus der Datei geladen werden sollen, der Parameter --encrypt-vault-id legt fest, welches Passwort für die Verschlüsselung verwendet wird und abschließend wird der Pfad zur Vault-Datei angegeben.
Es öffnet sich ein Editor, in dem wir die folgenden Einträge vornehmen:
vault_db_password: 'MeinDatenbankPasswort'
vault_auth_password: 'MeinAuthPasswort'
Nach dem Schließen des Editors befindet sich im Ordner group_vars die neu erstellte Datei prod_vault.yml. Diese sieht in etwa so aus:
$ANSIBLE_VAULT;1.2;AES256;prod
36613635356566643962353836303433353464643862633234376338373961613733316166613336
3861306462333964633938383762343737653239353837350a363138333037323862656661643366
34373564613233613235626439316538323737363433313865613639366536396536303466613161
6436663666643066370a373662326438303038363366353538663361646632353362653130386438
34633661626138333534626538383637643630303730626134643162623665343933386562613066
63623138353533383062663866653334393731653736386331323939393537333338323731383432
32653263316230613666396162373338363030366362653038313561666135663531343938396237
34323237353265396465386434306264633536636134386463666238363633303536333434363431
3434
Verschlüsseln einer bestehenden Datei
Es gibt noch die zweite Option, eine bereits vorhandene Datei zu verschlüsseln. Dazu legst du dir im Ordner group_vars die Datei dev_vault.yml an und trägst die folgenden Zeilen ein:
vault_db_password: 'MeinDevDatenbankPasswort'
vault_auth_password: 'MeinDevAuthPasswort'
Jetzt verschlüsseln wir den Inhalt dieser Datei.
ansible-vault encrypt --vault-id dev@ansible.pass --encrypt-vault-id dev repository/group_vars/dev_vault.yml
Dieser Befehl lädt aus der Datei ansible.pass die ID dev sowie das zugehörige Passwort. Dieses Passwort wird zur Verschlüsselung verwendet. Die erfolgreiche Verarbeitung der Datei wird mit dem Hinweis "encryption successful" quitiert und der gesamte Inhalt der Datei ist nun verschlüsselt.
Verwendung der Vaults
Jetzt wechseln wir in den Ordner repository und legen dort die folgenden Dateien an.
- ansible.cfg
[defaults]
inventory = inventory.ini
host_key_checking = False
retry_files_enabled = False
vault_password_file = ../ansible.pass
- inventory.ini
[prod]
127.0.0.1 ansible_connection=local
[dev]
127.0.0.1 ansible_connection=local
- provision-prod.yml
- hosts: prod
pre_tasks:
- name: include vault
include_vars: "group_vars/prod_vault.yml"
tags: ["always"]
tasks:
- name: "Print db password from vault"
debug:
msg: "DB: {{ vault_db_password }}"
- name: "Print auth password from vault"
debug:
msg: "DB: {{ vault_auth_password }}"
- provision-dev.yml
- hosts: dev
pre_tasks:
- name: include vault
include_vars: "group_vars/dev_vault.yml"
tags: ["always"]
tasks:
- name: "Print db password from vault"
debug:
msg: "DB: {{ vault_db_password }}"
- name: "Print auth password from vault"
debug:
msg: "DB: {{ vault_auth_password }}"
Damit haben wir das Inventar inventory.yml angelegt mit zwei Hosts, eine Konfigurationsdatei ansible.cfg, über die die Passwort-Datei eingebunden wird und jeweils ein Playbook für die beiden Hosts prod und dev.
Jetzt lassen wir jedes Playbooks einmal durchlaufen.
ansible-playbook provision-prod.yml
ansible-playbook provision-dev.yml
Bei der Ausgabe der beiden Playbooks sollten euch zuerst die Passwörter aus dem Vault prod_vault.yml ausgegeben werden, beim Provisionieren des Dev-Systems die Kennwörter aus dem Vault dev_vault.yml.
Optimierung
Ein Manko des o.a. Setups ist, dass kein Editor die Vault-Datei auslesen kann und somit die Variablen vault_db_password und vault_auth_password bei der automatischen Code-Vervollständigung nicht verfügbar sind. Um das Problem zu lösen, erstellt man im Ordner group_vars eine Datei all.yml. Dort trägt man die folgenden Zeilen ein.
db_password: "{{ vault_db_password }}"
auth_password: "{{ vault_auth_password }}"
Nun nimmt man sich die Playbooks und ändert dort die Variable von vault_db_password auf db_password und von vault_auth_password auf auth_password. Die beiden neuen Variablen aus der Datei all.yml sind nun in der Autovervollständigung des Editors vorhanden.
Ansehen und bearbeiten von verschlüsselten Dateien
Um die verschlüsselten Werte im Klartext ansehen zu können gibt es bei ansible-vault das Kommando view, zum Bearbeiten verwendet man das Kommando edit.
# Ansehen der verschlüselten Daten im Klartext
ansible-vault view --vault-id dev@ansible.pass repository/group_vars/dev_vault.yml
# Bearbeiten der verschlüsselten Daten
ansible-vault edit --vault-id dev@ansible.pass repository/group_vars/dev_vault.yml
Verschlüsselung einer einzelnen Variable
Nur der Vollständigkeit halber möchte ich noch kurz die Verschlüsselung einer einzelnen Variablen aufzeigen. Ich verwende hier wieder die bereits angelegte Datei ansible.pass. Die Variable soll support_user_password heißen und das Passwort lautet Ki3lahTh.
ansible-vault encrypt_string --vault-id dev@../ansible.pass --encrypt-vault-id dev 'Ki3lahTh' --name 'support_user_password'
Den so erzeugten Eintrag hinterlegt man in der Datei all.yml im Ordner group_vars.
db_password: "{{ vault_db_password }}"
auth_password: "{{ vault_auth_password }}"
support_user_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
30643232643939363937643366666664623132313337313231363465653631316563316465613766
3237363165333663646434633733333130643232386235660a616266343461666231383438346137
64623435353765343064663630613035643437353565353931336262303134363062306238303363
3436653965366230360a383361666230323231396230646230396135356432626133373661646631
3839
Im Playbook provision-dev.yaml fügt man folgenden Task hinzu:
- name: "Print password for support user"
debug:
msg: "Support user: {{ support_user_password }}"
Bei einem erneuten Lauf des Playbooks wird das Kennwort des Support-Benutzers ausgegeben.