Ansible

Theorie (Folien)

Einstiegsfragen

Wie viele Server verwaltet Ihr Lehrbetrieb?

Wie kontrollieren Sie…

  • …welche Software auf welchen Servern installiert ist?
  • …welche Dienste auf welchen Servern laufen?
  • …welche Benutzer auf welchen Servern welche Berechtigungen haben?

Was ist Konfigurationsmanagement?

Die Konfiguration von Systemen wird…

  • zentral verwaltet (i.d.R. in einem Git-Repository),
    • Infrastructure as Code (IaC)
  • automatisch angewendet (zeitlich oder manuell getriggert),
  • …durch Änderungen mitdokumentiert.

Dank einem Konfigurationsmanagementsystem kann man…

  • …lokal eine realitätsnahe Testumgebung betreiben,
  • …Systeme bei Bedarf schnell neu aufsetzen.

Die Systemkonfiguration sollte aber nicht mehr manuell angepasst werden!

Was ist Ansible?

Ansible…

  • …ist ein Konfigurationsmanagementsystem (Configuration Management, CM),
  • …wird von Red Hat betreut,
  • …ist unter der GPLv3 lizenziert,
  • …ist modular aufgebaut,
  • …arbeitet agentless (erfordert SSH und Python 3 auf dem verwalteten System).

Begriffe

  • Control Node: Unix-System, welches andere Systeme steuert.
  • Managed Host: System, welches gesteuert wird.
  • Inventory: Datei mit (gruppierter) Auflistung von Managed Hosts.
  • Task: Auszuführender Konfigurationsschritt.
    • z.B. Software installieren
  • Role: Gruppierung von Tasks für eine bestimmte Art von System.
    • z.B. Datenbankserver, Web-Server, FTP-Server
  • Playbook: Sammlung sortierter Tasks/Roles zur Konfiguration eines Hosts.

Control Nodes und Managed Hosts

<em>Control Nodes</em> und <em>Managed Hosts</em> <em>Control Nodes</em> und <em>Managed Hosts</em>

Idempotenz

Interessiert mich die Handlung oder der Zustand?

  • imperativ: «Schliessen Sie das Fenster!»
  • deklarativ: «Das Fenster soll geschlossen sein!»
if window.is_open():
    window.close()

Nur nötige Änderungen sollen vorgenommen werden:

  • idempotent: Gleiches Ergebnis nach mehreren Ausführungen
  • Mathematik: $x + 0$, $x \times 1$, $x^1$

Ansible-Tasks sind idempotent; Ansible arbeitet deklarativ.

Setup & Tools

Installation (unter Debian):

sudo apt install -y ansible

Werkzeuge:

  • ansible: Ausführung einzelner Task
  • ansible-playbook: Ausführung ganzer Playbooks
  • ansible-vault: Verwaltung von Secrets
  • ansible-galaxy: Rollen verwalten

Einstiegsbeispiel (I): Playbook

Playbook (apache.yaml):

- name: Setup Apache Web Server
  hosts: webservers
  become: yes
  become_method: sudo
  remote_user: user
  tasks:
  - name: Install Apache 2
    package:
      name: apache2
      state: present

Einstiegsbeispiel (II): Inventory & Ausführung

Inventory (hosts.ini):

[webservers]
localhost

Ausführung (Apache installieren):

ansible-playbook -i hosts.ini -c local apache.yaml

Einmalige Tasks (Service neu starten):

ansible -i hosts.ini \
    -m service -a "name=apache2 state=restarted" \
    -b -c local webservers

Literatur

Bücher zu Ansible: <em>DevOps for the Desperate</em>, <em>Ansible for DevOps</em>, <em>Ansible Up & Running</em> Bücher zu Ansible: <em>DevOps for the Desperate</em>, <em>Ansible for DevOps</em>, <em>Ansible Up & Running</em>

Praxis (Übungen)

Aufgabe 0: Vorbereitung und Test

Installieren Sie Ansible:

sudo apt install -y ansible

Laden Sie sich die folgenden Dateien mit wget auf Ihre VM herunter:

Führen Sie nun das Playbook zur Installation des Apache-Webservers aus:

ansible-playbook -i hosts.ini -c local apache.yaml

Aufgabe 1: Eine Apache-Testseite erstellen

Erweitern Sie das Playbook apache.yaml, sodass unter http://IP-ADRESSE eine PHP-Infoseite angezeigt wird. Hierzu sind folgende Schritte nötig, für welche Sie den Abschnitt tasks im Playbook erweitern:

  1. Installieren Sie das PHP-Paket php8.2.
  2. Erstellen Sie das Verzeichnis /var/www/phpinfo, das dem Benutzer und der Gruppe www-data gehört.
  3. Erstellen Sie eine Apache-Konfiguration /etc/apache2/sites-available/phpinfo.conf.
  4. Erstellen Sie eine PHP-Seite /var/www/phpinfo/index.php.
  5. Aktivieren Sie die PHP-Infoseite, indem Sie einen symbolischen Link von /etc/apache2/sites-enabled/phpinfo.conf auf /etc/apache2/sites-available/phpinfo.conf erstellen.
  6. Deaktivieren Sie die Debian-Infoseite, indem Sie den symbolischen Link /etc/apache2/sites-enabled/000-default.conf entfernen.
  7. Laden Sie die Apache-Konfiguration neu.

Verwenden Sie hierzu die folgenden Ansible-Module:

  • package zur Installation von PHP (analog zu Apache).
  • file zum Erstellen von Verzeichnissen und symbolischer Links.
    • Beachten Sie die Parameter state und src (für symbolische Links).
  • copy zum Kopieren vorgegebener Dateien auf das Zielsystem.
  • service zum Verwalten von Diensten.
    • Der Zielzustand soll reloaded lautet.

Tipp: Gehen Sie in einzelnen Schritten vor, d.h. erweitern Sie das Playbook nur um jeweils einen Tasks und führen Sie es anschliessend aus:

ansible-playbook -i hosts.ini -c local apache.yaml

Verwenden Sie sprechende Bezeichnungen für den name der einzelnen tasks.

Da Ansible idempotent arbeitet, werden nur jeweils die noch ausstehenden Schritte durchgeführt.

Überprüfen Sie anschliessend, ob die PHP-Infoseite im Browser erscheint.

Nehmen Sie anschliessend das erweiterte Playbook apache.yaml in dieses Repository auf.

Aufgabe 2: PHP-FPM konfigurieren

Erstellen Sie ein neues Playbook namens php-fpm.yaml, wobei Sie (bis auf name, welchen Sie anpassen) die ersten Zeilen vom Playbook apache.yaml verwenden können (bis zu den Tasks). Erweitern Sie das Playbook um folgende Tasks:

  1. Installieren Sie die Pakete php8.2-fpm und libapache2-mod-fcgid.
  2. Deaktivieren Sie das Apache-Modul php8.2.
  3. Aktivieren Sie das Apache-Modul proxy_fcgi.
  4. Aktivieren Sie die Apache-Konfiguration php-fpm.
  5. Laden Sie die Apache-Konfiguration neu, damit die Änderungen aktiv werden.
  6. Erhöhen Sie das memory_limit in der PHP-FPM-Konfiguration /etc/php/8.2/fpm/php.ini von 128 auf 256 Megabyte.
  7. Starten sie den PHP-FPM-Service neu, damit die Änderungen aktiv werden.

Verwenden Sie hierzu die folgenden Ansible-Module:

  • package zur Installation von PHP-FPM.
  • command zum Deaktivieren/Aktivieren von Apache-Modulen bzw. -Konfigurationen mit a2dismod, a2enconf usw.
    • Der cmd-Parameter definiert den auszuführenden Befehl.
    • Achtung: Durch die Verwendung von command wird das Playbook imperativ («tue das!») und ist nicht mehr deklarativ («sorge für diesen Zustand»)! Damit das Playbook trotzdem idempotent bleibt, kann die Ausführung des Befehls mit den Parametern creates bzw. removes auf bestimmte Situationen beschränkt werden.
      • Aktivieren Sie ein Modul nur, wenn die entsprechende *.load-Datei noch nicht im Verzeichnis /etc/apache2/mods-enabled besteht.
      • Deaktivieren Sie ein Modul nur, wenn die entsprechende *.load-Datei noch im Verzeichnis /etc/apache2/mods-enabled besteht.
  • lineinfile zum Ersetzen einer einzelnen Zeile in der PHP-Konfiguration.
    • Der path-Parameter gibt die zu verändernde Datei an.
    • Der line-Parameter definiert die neue Zeile.
    • Der regexp-Parameter definiert ein Muster (regulärer Ausdruck) für die zu ersetzende Zeile.
  • service zum Neustarten bzw. Neuladen von einem systemd-Service.

Tipp: Der name-Parameter unterstützt nicht nur einen einzelnen Wert, sondern Listen von Werten:

name:
  - foo
  - bar

Kontrollieren Sie auf PHP-Infoseite zwei Sachen:

  1. Die Server API heisst FPM/FastCGI und nicht mehr Apache 2.0 Handler.
  2. Das memory_limit beträgt 256M und nicht mehr 128M.

Nehmen Sie anschliessend das neue Playbook php-fpm.yaml in dieses Repository auf.

Aufgabe 3: Minio in Betrieb nehmen

Erstellen Sie ein neues Playbook namens minio.yaml analog zu den beiden vorherigen. Vor dem Abschnitt tasks soll ein neuer Abschnitt namens vars eingefügt werden, in welchem die folgenden wiederverwendbaren Variablen definiert werden:

vars:
  minio_bin: /usr/local/bin/minio
  minio_data_dir: TODO
  minio_user: TODO
  minio_group: TODO
  minio_password: TODO
  environment_file: TODO
tasks:
  

Ersetzen Sie dabei den Platzhalter TODO durch die Werte, die gleich in der Aufgabenstellung folgen. Die oben definierten Variablen können folgendermassen verwendet werden:

# old
dest: /usr/local/bin/minio

# new
dest: '{{ minio_bin }}'

Dank Variablen können die gleichen Werte sowohl im Playbook als auch in Templates verwendet werden. Dadurch können Änderungen nun zentral vorgenommen ‒ und die hartkodierten Passwörter ausgelagert werden.

Ergänzen Sie das Playbook um folgende tasks, um Minio in Betrieb zu nehmen:

  1. Laden Sie den Minio-Server minio von der Minio-Downloadseite herunter ins lokale Verzeichnis /usr/local/bin/ und machen Sie die Datei ausführbar.
  2. Erstellen Sie einen Benutzergruppe namens minio und einen Benutzer gleichen Namens mit dem Home-Verzeichnis /home/minio.
  3. Erstellen Sie das Minio-Datenverzeichnis /home/minio/minio-data, welches dem Benutzer minio gehört.
  4. Erstellen Sie eine Datei /home/minio/environment mit den beiden Umgebungsvariablen MINIO_ROOT_USER=minio und MINIO_ROOT_PASSWORD=topsecret.
  5. Erstellen Sie ein systemd-Unitfile unter /etc/systemd/system/minio.service auf Basis des Templates minio.service.jinja2.

Verwenden Sie hierzu die folgenden Ansible-Module:

  • get_url zum Herunterladen von Dateien von einer bestimmten Quelle (url) an einen bestimmten Ort (dest). Damit minio ausführbar ist, sollte der mode-Parameter auf 755 gesetzt werden. Optional kann auch auf eine bestimmte checksum geprüft werden.
  • group zum Erstellen einer Benutzergruppe.
  • user zum Erstellen eines Benutzers. Verwenden Sie die Parameter create_home, home, group, und shell.
  • template zum Erstellen von Konfigurationsdateien aus Vorlagen. Geben Sie das Template als src und die Zieldatei mit dest an.
  • systemd_service zum Verwalten von systemd-Services. Setzen Sie daemon_reload: true, damit das neu erstellte Service-Unit-File nachgeladen wird. Verwenden Sie weiter die Parameter enabled und state, um den Service (automatisch) zu starten.

Wenn Sie alles richtig gemacht haben, steht Minio unter http://IP-ADRESSE:9090 zur Verfügung.

Tipp: Schauen Sie bei Unklarheiten im systemd-Repository nach, wie wir Minio manuell in Betrieb genommen haben.

Aufgabe 4: Passwörter mit Ansible Vault verschlüsseln

Passwörter im Klartext zu speichern ist ein Sicherheitsrisiko, zumal Playbooks und Template-Dateien oftmals in einem (öffentlichen) Git-Repository zu liegen kommen. Aus diesem Grund sollten Passwörter und andere Secrets (wie z.B. Token) nur verschlüsselt abgelegt werden.

In dieser Übung soll das Minio-Passwort aus dem Playbook entfernt, durch ein stärkeres ersetzt und verschlüsselt in eine separate Datei ausgelagert werden.

Zunächst soll die Variable minio_password aus dem Playbook minio.yaml entfernt werden. Unmittelbar nach vars soll ein Abschnitt namens vars_files hinzugefügt werden:

vars:
  
vars_files:
  - minio-secrets.yaml
tasks:
  

Das Werkzeug pwgen soll installiert werden, womit sich sichere zufällige Passwörter generieren lassen:

sudo apt install -y pwgen

Ein zufälliges Passwort der Länge 36 lässt sich folgendermassen generieren:

pwgen 36 1

Mögliche Ausgabe:

Hie6sheim2FiefahWoo4vu7shisoozeeboka

Das Newline-Zeichen \n am Ende soll jedoch entfernt werden, da dieses nicht mitverschlüsselt werden soll:

pwgen 36 1 | tr -d '\n'

Als nächstes wird die Datei minio-secrets.yaml mit der verschlüsselten Variablen minio_password mithilfe von ansible-vault erstellt:

pwgen 36 1 | tr -d '\n' | ansible-vault encrypt_string --stdin-name minio_password > minio-secrets.yaml

Es wird ein Vault-Passwort interaktiv abgefragt, das man sich merken muss. (Mit diesem Passwort werden die Secrets bei der neuerlichen Ausführung des Playbooks entschlüsselt.)

Mit --stdin-name wird angegeben, wie die Variable heissen soll, deren Wert per stdin mitgegeben werden soll. Die Datei minio-secrets.yaml wurde nun erstellt und enthält ein zufälliges, verschlüsseltes Secret.

Nun soll das angepasste Playbook mit dem angepassten und ausgelagerten Passwort ausgeführt werden, wozu zusätzlich der Parameter --ask-vault-pass angegeben wird, damit das Passwort zum Entschlüsseln der verwendeten Secrets interaktiv eingegeben werden kann:

ansible-playbook -i hosts.ini -c local --ask-vault-pass minio.yaml

Das neue Passwort sollte jetzt in /home/minio/environment gesetzt sein und unter http://IP-ADRESSE:9090 verwendet werden können. Die Datei minio-secrets.yaml kann nun ins Repository aufgenommen werden.