Aus Linux-Magazin 05/2025

Das nächste Level – Patching mit Ansible Playbooks

© Kittipong Jirasukhanont / 123rf.com

Das Vertrauen gegenüber Big Tech ist von Rissen durchzogen. Der Drang, persönliche oder die eigenen Unternehmensdaten wieder On-Premise zu haben, hat ein neues Momentum bekommen. Wichtige Services selbst zu betreiben muss dabei nicht notwendigerweise mit großem Aufwand einhergehen.

Im ersten Teil [1] meiner kleinen Ansible-Serie habe ich gezeigt, wie Sie einerseits Betriebssysteme und andererseits installierte Third-Party-Dienste mit Ansible patchen. Die politischen Entwicklungen in den USA säen derzeit nicht nur Zweifel an Big Tech, zahlreichen Unternehmern dürfte inzwischen auch dämmern, dass allzu großes Vertrauen in einstige Allianzen vielleicht naiv war. Das Bedürfnis, Daten wieder lokal vorzuhalten, und damit Unabhängigkeit zu gewinnen, verstärkt sich zusehends. Gleichzeitig fehlt es hierzulande nach wie vor massiv an IT-Fachkräften.

Entgegen der landläufigen Ansicht bedeutet das Betreiben wichtiger Services allerdings nicht zwingend, immensen Aufwand stemmen zu müssen. Auch komplexe Services lassen sich mit Ansible aktualisieren. In diesem Artikel möchte ich Ihnen zeigen, wie Sie per Ansible eine On-Premise-Installation von Gitea, einer mächtigen Plattform zur Softwareentwicklung, mit wenig Mühe aktuell halten.

Vorbedingungen

Selbstverständlich erfordern solch mächtige Werkzeuge wie Gitea gewisse Voraussetzungen. Zunächst benötigen Sie ein Linux-System, auf dem Gitea [2] installiert ist. Ich gehe im Beispiel von einem Setup auf Basis von nginx als Reverse Proxy [3], PostgreSQL als Datenbank und Postfix als lokalem E-Mail-Relay aus. Prinzipiell lässt sich Gitea ebenfalls als Standalone-Service betreiben. Allerdings stehen Ihnen mit einem Reverse Proxy wesentlich mehr Möglichkeiten zur Einflussnahme offen, auch hinsichtlich Security.

Listing 1

Beispielhafte Nginx-Konfiguration

server {
    listen 80;
    server_name gitea.domain.tld;
    return 302 https://$server_name$request_uri;
}
server {
    listen 443 ssl;
    server_name gitea.domain.tld;
    ssl_certificate /etc/letsencrypt/live/gitea.domain.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/gitea.domain.tld/privkey.pem;
    location / {
        proxy_set_header        Host $host;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;
        rewrite                 ^/(.*) /$1 break;
        rewrite                 ^/$ /$1 break;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://127.0.0.1:3000;
    }
}

In Listing 1 finden Sie eine exemplarische Konfiguration, die für den TLS-gesicherten Zugriff LetsEncrypt-Zertifikate einsetzt (Zeilen 9 bis 10). Außerdem sorgt sie dafür, dass sämtliche HTTP-Verbindungen im ersten »server«-Block automatisch auf https umgeleitet werden (Zeile 4). Die Konfiguration im »location«-Block stammt aus der Gitea-Dokumentation.

Für den Betrieb von Gitea ist der Versand von E-Mails unverzichtbar. Im Unternehmen können Sie in den Einstellungen von Gitea Ihren internen SMTP-Server nutzen, ich behelfe mir in meinem heimischen Setup mit einem Smart-Host. Postfix nimmt lokal E-Mails entgegen und verschickt sie dann nach Anmeldung über meinen E-Mail-Provider. Das bringt den Vorteil, dass Postfix ausgehende E-Mails in der Queue behält. Sollte der Versand fehlschlagen, lassen sie sich so anschließend nochmals verschicken.

Wider die Langeweile

Wer sich mit Robotik beschäftigt, begegnet häufig den drei Ds: Dull, Dangerous and Dirty. Schmutzig wird es in der Systemadministration selten, abgesehen von verdreckten, nicht sachgemäß eingesetzten Endgeräten. Gefährlich wird es unter bestimmten Umständen, beispielsweise wenn jemand auf der ISS die Schleusensoftware am Produktivsystem patcht. Das große Stichwort in meiner Admin-Welt lautet Dumpfheit, also Dullness.

Niemand möchte auf Hunderten Maschinen manuell Dinge tun. Das wäre eine arg stupide Arbeit und obendrein extrem fehlerbehaftet. Wer es nicht glaubt, der öffne einmal 50 SSH-Sessions und reboote dann die richtige Maschine darunter. Das macht keinen Spaß. Viel mehr Freude bereitet es mir dagegen, wenn ich mich wie ein Puppenspieler fühlen darf und Ansible dabei zusehe, wie sich die stumpfsinnigen Tätigkeiten ohne mein Zutun quasi von alleine erledigen.

Im Falle von Gitea zählen dazu unterschiedliche Schritte: Ich möchte im Wesentlichen auf einer bestehenden Maschine ein neues Binary von Gitea herunterladen. Das wirkt im ersten Moment trivial. Allerdings möchte ich auch einen Download-Link, für den ich nicht bei jeder Version meine Ansible-Rolle anpassen will. Idealerweise sollte es sich um einen Link handeln, der sich bis auf die Version nicht verändert.

Anschließend möchte ich freilich sicherstellen, dass das, was ich da aus dem Internet beziehe, sauber ist. Das heißt, ich möchte einerseits geprüft wissen, dass auf dem Übertragungsweg nichts schiefgegangen ist. Andererseits möchte ich mindestens genauso sichergestellt haben, dass die Datei nicht schon vorab manipuliert worden ist. Dementsprechend lade ich nicht nur die Binary selbst herunter, sondern auch die zugehörige SHA256-Prüfsumme und zu beiden Dateien zusätzlich die abgetrennte digitale Signatur.

Ersteres lässt sich einfach herausbekommen. Gitea verwendet ein wohldefiniertes Schema für Veröffentlichungen. Die passenden Downloads schematisch herauszufinden gestaltet sich simpel. Zudem liegen Prüfsummendateien bereit, deren Download-Link ich über Konkatenieren von ».sha256« erzeugen kann. Dasselbe gilt für die Signaturdateien. Ihre Download-Links erfordern ein zusätzliches Suffix ».asc«.

Die Basis-URL zu den Gitea-Downloads [4] hinterlege ich als Ansible-Gruppenvariable. Eine Gruppenvariable definieren Sie in einer eigenen Datei. Sie bezieht sich stets auf eine Gruppe von Servern, in diesem Fall »gitea_servers« [1]. Der Dateiname entspricht dem Namen der Hostgruppe aus meinem Inventory, also »gitea_servers«. Sämtliche Maschinen hinter der Variablen erhalten dieselben Download-Links, also auch dieselbe Gitea-Version. Entwicklungs- und Testumgebungen ließen sich ebenfalls auf diese Weise konfigurieren.

Neben »GITEA_BASE_URL« definiere ich die Variable »GITEA_CURRENT_VERSION«. Letztere bestücke ich immer wieder mit der aktuellen Version von Gitea. Damit ergeben sich der Inhalt der Gruppenvariablendatei »group_vars/gitea_servers« in Listing 2 sowie die Muster aus Listing 3 für die erforderlichen Download-URLs.

Listing 2

Gruppenvariablen für Gitea

GITEA_BASE_URL: https://dl.gitea.com/gitea
GITEA_CURRENT_VERSION: 1.23.5

Listing 3

Gitea-Download-Links

{{ GITEA_BASE_URL }}/{{ GITEA_CURRENT_VERSION }}/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz
{{ GITEA_BASE_URL }}/{{ GITEA_CURRENT_VERSION }}/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.asc
{{ GITEA_BASE_URL }}/{{ GITEA_CURRENT_VERSION }}/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.sha256
{{ GITEA_BASE_URL }}/{{ GITEA_CURRENT_VERSION }}/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.sha256.asc

Ansible ersetzt schlicht die Gruppenvariablen »GITEA_BASE_URL« und »GITEA_CURRENT_VERSION« an passender Stelle durch ihre Werte. Am Ende stehen dann gültige Links, die Ansible per Builtin »get_url« herunterladen kann. Mir ist bewusst, dass Ansible über das Builtin »stat« und dessen Variablen »checksum_algorithm« und »get_checksum« sehr elegant Prüfsummen auswerten kann. Das würde aber dazu führen, dass ich auch Checksums als Gruppenvariablen speichern muss. Damit müsste ich zusätzlich zur Version die Prüfsummen bei jedem Update aktualisieren. Das gilt es zu vermeiden. Prinzipiell würde ich es bevorzugen, Gitea lieferte die aktuellste Version stets mit dem Alias »current« aus, so wie es viele FLOSS-Projekte mit ihren Nightly-Builds tun. Dann bräuchte ich nicht einmal die Version manuell anpassen. Ich sehe das als Jammern auf hohem Niveau.

Task-Datei

Fragt sich noch, wie die passende Ansible Task-Datei (Listing 4) aussieht. Zunächst lade ich das Binary, komprimiert per »XZ«, herunter. Darauf folgt die zugehörige Signatur-Datei, zu erkennen am Suffix ».asc«, dann die Prüfsumme, ».sha256« und schließlich die elektronische Signatur zur Prüfsumme ».sha256.asc«. Im nächsten Schritt berechne ich die SHA256-Prüfsumme der ».xz«-Datei und vergleiche sie mit der aus dem dazugehörigen Download. Ein Unterschied an dieser Stelle lässt das Playbook scheitern. Statt das Rad neu zu erfinden, löse ich »fail« mit der zugehörigen Ausgabe von »sha256Sum« aus. Ist der Download in Ordnung, geht es an die Signaturdatei: Auch sie prüfe ich auf dieselbe Weise. Eine nicht passende Prüfsumme führt einmal mehr zu »fail«.

Listing 4

Ansible Task-Datei

- name: gitea herunterladen
  ansible.builtin.get_url:
    url: "{{ GITEA_BASE_URL }}/{{ GITEA_CURRENT_VERSION }}/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz"
    dest: /opt/gitea/
    owner: root
    group: root
    mode: '0644'
  become: true
  become_method: sudo
- name: Signatur herunterladen
  ansible.builtin.get_url:
    url: "{{ GITEA_BASE_URL }}/{{ GITEA_CURRENT_VERSION }}/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.asc"
    dest: /opt/gitea/
    owner: root
    group: root
    mode: '0644'
  become: true
  become_method: sudo
- name: Checksum-Datei herunterladen
  ansible.builtin.get_url:
    url: "{{
 GITEA_BASE_URL }}/{{ GITEA_CURRENT_VERSION }}/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.sha256"
    dest: /opt/gitea/
    owner: root
    group: root
    mode: '0644'
  become: true
  become_method: sudo
- name: Signatur zur Checksum-Datei herunterladen
  ansible.builtin.get_url:
    url: "{{ GITEA_BASE_URL }}/{{ GITEA_CURRENT_VERSION }}/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.sha256.asc"
    dest: /opt/gitea/
    owner: root
    group: root
    mode: '0644'
  become: true
  become_method: sudo
- name: SHA256-Summe von XZ-Archiv berechnen
  ansible.builtin.command: "sha256sum --check gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.sha256"
  args:
    chdir: /opt/gitea/
  register: sha265sum_check_result
  changed_when: False
  become: true
  become_method: sudo
- name: Berechnung Checksum auswerten
  fail:
    msg: "{{ sha265sum_check_result.stdout }}"
  when: sha265sum_check_result.rc != 0
- name: Signatur des XZ-Archivs überprüfen
  ansible.builtin.command: "gpg --verify gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.asc gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz"
  args:
    chdir: /opt/gitea/
  register: gpg_verify_result_xz
  changed_when: False
  become: true
  become_method: sudo
- name: Signaturprüfung auswerten
  fail:
    msg: "{{ sha265sum_check_result.stdout }}"
  when: gpg_verify_result_xz.rc != 0
- name: Signatur der SHA256-Sum-Datei prüfen
  ansible.builtin.command: "gpg --verify gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.sha256.asc gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz.sha256"
  args:
    chdir: /opt/gitea/
  register: gpg_verify_result_sha256
  changed_when: False
  become: true
  become_method: sudo
- name: Signaturprüfung auswerten
  fail:
    msg: "{{ sha265sum_check_result.stdout }}"
  when: gpg_verify_result_sha256.rc != 0
- name: XZ-Dekompression
  ansible.builtin.command: "xz -f -d -k /opt/gitea/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64.xz"
  become: true
  become_method: sudo
- name: Datei ausführbar machen
  ansible.builtin.file:
    path: "/opt/gitea/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64"
    owner: root
    group: root
    mode: '0755'
  become: true
  become_method: sudo
- name: Symbolischen Link anlegen
  ansible.builtin.file:
    src: "/opt/gitea/gitea-{{ GITEA_CURRENT_VERSION }}-linux-arm64"
    dest: /opt/gitea/gitea
    owner: root
    group: root
    state: link
  become: true
  become_method: sudo
- name: gitea-Service neu starten
  ansible.builtin.systemd_service:
    state: restarted
    daemon_reload: true
    name: gitea

Beim Checken der digitalen Signatur bedarf es weiterer Vorbereitungsmaßnamen. Um eine elektronische Signatur überprüfen zu können, benötigen Sie den passenden öffentlichen Schlüssel. Beim Vorgang der Signaturerzeugung handelt es sich quasi um das Gegenstück zur Verschlüsselung. Bei der asymmetrischen Verschlüsselung chiffriert Partei A den Klartext mit dem Public Key von Partei B. Partei B kann die Chiffre nun mit ihrem Private Key entschlüsseln.

Bei der elektronischen Signatur verschlüsselt Partei B den Klartext mit ihrem Private Key. Partei A kann anschließend die Authentizität per verfügbarem Public Key von Partei B entschlüsseln, also die Signatur überprüfen. Den Public Key von Gitea liefert das folgende Kommando:

gpg --recv-keys CC64B1DB67ABBEECAB24B6455FC346329753F4B0

Wer der Software Gitea und deren Website Vertrauen schenkt, tut das am besten auch beim dazugehörigen Public Key, zumindest marginal, über diesen Aufruf:

gpg --edit-trust CC64B1DB67ABBEECAB24B6455FC346329753F4B0

Im Anschluss meldet gpg dementsprechende Erfolge bei der Signaturprüfung.

Daraufhin ergibt es Sinn, das Binary dorthin zu entpacken, wo es ursprünglich installiert wurde, in meinem Fall »/opt/gitea/«. Da dieser Ordner »root« gehört und für andere User nicht beschreibbar ist, muss ich per »become« und »become_method« sudo verwenden, ansonsten hätte ich keinen Schreibzugriff. Die Datei erhält beim Entpacken Ausführungsrechte, abzulesen an »755«, was dem Eigentümer root die Rechte »rwx« und allen anderen »r-x« verleiht.

Das Binary enthält eine Versionsnummer im Dateinamen, was mir entgegenkommt. Ich belasse die Namen so und halte stets noch mindestens eine ältere Version vor, falls ein Release sich als fehlerhaft erweisen sollte. Was ich dagegen sehr wohl tue: einen symbolischen Link anlegen. Er heißt »gitea« und liegt unter »/opt/gitea/«. Damit muss ich nach Updates nie den zugehörigen SystemD-Service anpassen, der nach der Aktualisierung ebenfalls neu gestartet werden muss.

Mit dem Restart des Gitea-Service hat meine Task-Liste ihr Ende erreicht. Man könnte darüber nachdenken, außerdem in einer Art Post-Update-Phase die Downloads zu bereinigen – das ist zwar eher eine kosmetische Tätigkeit, aber mit dem Modul »ansible.builtin.file« und »state: absent« sowie den bekannten Dateinamen flott umgesetzt.

Fazit

Erneut gelingt es Ansible problemlos, mir lästige Arbeiten abzunehmen. Einzig die korrekte Version muss ich dem RSS-Feed der Gitea-Release Notes [5] entnehmen und die »group_vars« anpassen. Ein Aufruf des entsprechenden Playbooks erledigt den Rest. Aus der beschriebenen Vorgehensweise ergibt sich ein ausgesprochen verlässlicher Prozess. Ist das Playbook [6] einmal getestet, wird es solang funktionieren, bis zum Beispiel Schlüsselpaare ablaufen oder das Projekt umzieht. Obendrein lässt sich YAML gut lesen und beschert dem User eine eigens geschriebene Projektdokumentation. Solches Know-how findet sich oft in der Hand von nur wenigen Kollegen. Wohl dem also, der eine standardisierte Vorgehensweise parat hat. (csi)

Infos

  1. Updates mit Ansible: Thomas Reuß, “Spielplan”, LM 04/2025, S. 32, https://www.lm-online.de/51885
  2. Gitea Installations-Doku: https://docs.gitea.com/category/installation
  3. Gitea mit Nginx als Reverse Proxy: https://docs.gitea.com/administration/reverse-proxies#nginx
  4. Gitea Download-Site: https://dl.gitea.com/gitea
  5. Release notes of gitea: https://github.com/go-gitea/gitea/releases.atom
  6. GitHub-Repository zur Artikelserie: https://github.com/treuss/ansible-lm
DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben