mit Ansible automatisch angepasste Dateien zurück ins Gitlab pushen

Datum

Wenn man bestimmte Prozesse automatisieren möchte kommt es vor, dass z.B. Konfigurationsdateien aus einem Git(lab)-Repo ausgecheckt, angepasst und anschließend automatisch wieder zurück gepusht werden sollen.

Deploy-Keys erstellen

Deploy im Gitlab sind nichts anderes als SSH-Keys, welche dediziert für ein bestimmtes Projeklt verwendet werden können.
Daher ist der erste Schritt die Erstellung neuer SSH-Keys, vorzugsweise mit einem Algorithmus welcher Elliptische Kurven verwendet (z.B. ecdsa)

Beispiel:

ssh-keygen -t ecdsa -b 521 Hierbei sollte ein entsprechendes Schlüsselpaar erstellt worden sein.
  • id_ecdsa ist der private Schlüssel
  • id_ecdsa.pub ist der öffentliche Schlüssel

Deploykey im Gitlab hinterlegen

Im entsprechenden Projekt kann man unter Einstellungen —> Repository —> Deploy Keys den öffentlichen Schlüssel unter einen eindeutigen Namen hinterlegen und auch Schreibzugriff gewähren. Da man damit seine Änderungen in das Gitlab pushen möchte sollte also Schreibzugriff gewährt werden.

Diesen neuen Deploy Key findet man nun in der entsprechenden Liste.

der private Schlüssel im Playbook?

Es empfiehlt sich nie den privaten Schlüssel als Klartext im Ansible-Playbook einzutragen, da dieser volle Rechte über das entsprechende Repository gewährt.
Von daher sollte der private Schlüssel mit ansible-vault verschlüsselt werden.
Zu dieser Thematik haben wir bereits einen passenden Artikel verfasst: Sensible Daten verschlüsseln mit Ansible-Vault

Das Passwort um den String wieder zu entschlüsseln kann z.B. beim Jenkins im Jenkins-eigenen Passwort-Store abgelegt werden und die ID mit vaultCredentialsId an das Playbook übergeben werden.

git Verbindung per Ansible-Playbook

Hierfür müssen verschiedene Sachen festgelegt werden. Ich verwende z.B. für die verschiedenen Variablen eine eigene Datei (group_vars/all.yml), in welcher die notwendigen Parameter für git festgelegt sind.

Beipsiel:

---
GIT_URL_PREFIX: "git@git.domain.tld"
GIT_AUTHOR_USER: 'automatic_job'
GIT_AUTHOR_EMAIL: 'admin@domain.tld'
GIT_SSH_WRAPPER: "{{ playbook_dir }}/files/ssh_wrapper.sh"
GIT_PRIVATE_KEYFILE: "{{ playbook_dir }}/files/git_dinge_tun_key.key"
GIT_PRIVATE_KEY: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  36343666353839373266613665363666373862353333383532626134363137646139303335666638
  3236623239636261303639356638343564623463356436320a336261663563643737613933386533
  64313630386430666136313339656161356536366238333830653236373566363836343835643531
  3031646231333131390a363336373264623839363066346339303933353839376132333463313866
  62623435656561636132313731303261316533353561383462326234613838353339393462303065
  66613963623634356262373735363164303164373139396432393062326662383937323637666531
  31636166383933393864623963623934303963653036316132653866333065373163626461626163
  32303234616463333161303461653031323038353034366633643565303761646438626139643665
  61323836386663353564383435366636393161636261656363356663373866303538636135333166
  39303831363635653165343231376436633331616234346336313635666532326166613233633666
  36353266383135373437326633653861386135336466633431376137356137643866666239633162
  62326633623133333839336536613561313930626236326532663763353630373163376332313037
  38653436613733376533636333323138616232363063353262336135393230653834666232306235
  36613638376134623761386137353939333232663034333230386637383136333132363034623931
  32626564646434343365383661613161356237626531306333373334346662336164313035326465
  36376638663333323365303062356566623934666261636463643033626638616135303235353730
  64316432613137386339366365313561353366346463613462346234366637663238643532383334
  65623264343939613863323361633439353463666563666430653762363863313366306134313231
  37393539313532306235656466383266633731633033343033393263376164303435663737376630
  66346166356231373064633962363030386564346531363336646237626464366539326364333037
  37353062303332666661363135363666636461333361323538376537616531356365353832386666
  32386233373364316465336131636131363839393530646163666662326463623338393461656533
  35353265333934313634303465396266393534373934623838613262633562363163316233363736
  31653532326630333433

Hier werden die notwendigen Angaben für Author und Mail angegeben, welche git benötigt, aber auch den Namen des privaten Schlüssels (git_dinge_tun_key.key), welcher noch per ansible-vault verschlüsselt ist und temporär bei der Ausführung des Jobs auf die Platte geschrieben wird.

Als weiteres sollte man entsprechende Enviroment Variablen für git direkt setzen…

- name: "Konfiguration auslesen"
  hosts: localhost
  connection: local
  gather_facts: False
  environment:
    # as environment because the 'git' executable reads it
    GIT_SSH: "{{ GIT_SSH_WRAPPER }}"
    # as environment because ssh_wrapper reads it
    GIT_KEY: "{{ GIT_PRIVATE_KEYFILE}}"

Aufgrund der etwas älteren git Version verwende ich noch einen SSH-Wrapper (/files/ssh_wrapper.sh), welcher folgenden Inhalt hat:

#!/usr/bin/env bash
#
# This wrapper is needed for git versions prior to 2.3, as they
# do not read the GIT_KEY, or GIT_SSH_COMMAND environment variables.
# They only read the GIT_SSH environment variable, with an executable
# for ssh
#
ssh -i "${GIT_KEY}" -o IdentitiesOnly=yes "$@"

Der nun folgende Task erstellt temporär den privaten SSH-Key:

- name: git commit/push"
    block:
      - name: "Erstelle git ssh keyfile"
        copy:
          content: "{{ GIT_PRIVATE_KEY }}"
          dest: "{{ GIT_PRIVATE_KEYFILE }}"
          mode: '0400'

Nun kann z.B. das gewünschte Repo geklont und bearbeitet werden. Ich habe das in diesem Beispiel bereits getan und das Repo in ein temporäres Verzeichnis geklont, welcher Ansible unter der Variable tmpdir.path bekannt ist.
Anschließend kommt das git add mit anschließender Ausgabe:

- name: "adding changes to local repository"
        shell:
          cmd: "git add files/*"
          chdir: "{{ tmpdir.path }}/testprojekt"
        register: git_add
      - name: "Ausgabe git add"
        debug:
          msg: "{{ git_add.stdout }}"

per loop werden weitere Vorbereitungen getroffen:

      - name: "prepare repository for commit and push"
        git_config:
          scope: local
          repo: "{{ tmpdir.path }}/testprojekt"
          name: "{{ item.key }}"
          value: "{{ item.value }}"
        with_dict: {
          "user.name": "{{ GIT_AUTHOR_USER }}",
          "user.email": "{{ GIT_AUTHOR_EMAIL }}",
        }
        loop_control:
          label: "{{ item.key }}"

Die Änderungen commited:

- name: "commiting changes"
        shell:
          cmd: "git commit -m 'changes caused by testprojekt: {{ date }}' "
          chdir: "{{ tmpdir.path }}/testprojekt"
        register: "git_commit"
      - debug:
          msg: "{{ git_commit }}"

… und gepusht:

      - name: "pushing changes to remote"
        shell:
          cmd: "git push -u origin master"
          chdir: "{{ tmpdir.path }}/testprojekt"
        register: "git_push"
        when: git_add.rc == 0
      - debug:
          msg: "{{ git_push }}"

da ich diese Tasks als block zusammengefasst habe, kann ich per rescue bestimmen was im Fehlerfall passieren soll:

Beispiel:

rescue:
      - debug:
          msg: "Push ins Git fehlgeschlagen"

Des weiteren kann mit der always Anweisung festlgen, dass der temporär erstellte private SSH-Key wieder gelöscht wird und dabei ist es egal ob die Tasks erfolgreich waren oder nicht.

always:
    - name: "cleanup git private key"
      file:
        state: absent
        path: "{{ GIT_PRIVATE_KEYFILE }}" 

Autor
Kategorien Automatisierung, Ansible

PRTG Map