News zu Tech Portfolio

Ansible und Outbound-Proxy: Ein En­vi­ron­ment für alle Plays

Headergrafik Praxistipp Ansible - Proxy-Environment

Ein Ansible-Playbook lässt sich mit wenig Aufwand so schreiben, dass es un­ab­hän­gig von Be­triebs­sys­tem, Version und einem vor­han­de­nen HTTP-Proxy funk­tio­niert. Es läuft in der Lab-Umgebung ohne Proxy und im Kun­den­netz mit Outbound-HTTP-Proxy, ohne dass du es anpassen musst. Eine na­he­lie­gen­de Lösung – das Proxy-En­vi­ron­ment an jedem Task zu setzen – skaliert schlecht: Jeder neue Task braucht diese Angabe, und ein ver­ges­se­ner Task fällt erst im Netz mit Proxy auf.

Bisher haben wir in unseren Cheat Sheets Befehle und Tests unter die Lupe genommen. Diesmal geht es nicht um einen Befehl, sondern um ein Ansible-Muster, welches dir im Au­to­ma­ti­sie­rungs-Alltag helfen kann. Es gibt viele ver­schie­de Mög­lich­kei­ten, ein Proxy-En­vi­ron­ment mit­zu­ge­ben, die wir hier nicht alle be­leuch­ten können. Wir de­fi­nie­ren in diesem Beitrag das En­vi­ron­ment einmal als Variable und binden sie auf Play-Ebene ein. Ein En­vi­ron­ment an jedem einzelnen Task entfällt damit. Einen na­he­lie­gen­den Weg dorthin schauen wir uns zuerst an, denn er hat seine Tücken.

Profilbild Michael
Michael Decker
In­fra­struk­tur Ad­mi­nis­tra­tor
ASPICON

Ein Ansible-Playbook lässt sich mit wenig Aufwand so schreiben, dass es un­ab­hän­gig von Be­triebs­sys­tem, Version und einem vor­han­de­nen HTTP-Proxy funk­tio­niert. Es läuft in der Lab-Umgebung ohne Proxy und im Kun­den­netz mit Outbound-HTTP-Proxy, ohne dass du es anpassen musst. Eine na­he­lie­gen­de Lösung – das Proxy-En­vi­ron­ment an jedem Task zu setzen – skaliert schlecht: Jeder neue Task braucht diese Angabe, und ein ver­ges­se­ner Task fällt erst im Netz mit Proxy auf.

Bisher haben wir in unseren Cheat Sheets Befehle und Tests unter die Lupe genommen. Diesmal geht es nicht um einen Befehl, sondern um ein Ansible-Muster, welches dir im Au­to­ma­ti­sie­rungs-Alltag helfen kann. Es gibt viele ver­schie­de Mög­lich­kei­ten, ein Proxy-En­vi­ron­ment mit­zu­ge­ben, die wir hier nicht alle be­leuch­ten können. Wir de­fi­nie­ren in diesem Beitrag das En­vi­ron­ment einmal als Variable und binden sie auf Play-Ebene ein. Ein En­vi­ron­ment an jedem einzelnen Task entfällt damit. Einen na­he­lie­gen­den Weg dorthin schauen wir uns zuerst an, denn er hat seine Tücken.

Profilbild Michael
Michael Decker
In­fra­struk­tur Ad­mi­nis­tra­tor
ASPICON

Der Weg: erst mit set_fact

Der na­he­lie­gen­de erste Schritt ist, das En­vi­ron­ment in einem Task zu­sam­men­zu­bau­en und als Fact abzulegen:

- name: Mein Play
  hosts: all
  environment: "{{ proxy_environment | default({}) }}"
  tasks:
    - name: Build proxy environment
      ansible.builtin.set_fact:
        proxy_environment: >-
          {{
            {
              'http_proxy': proxy_http | default('', true),
              'HTTP_PROXY': proxy_http | default('', true),
              'https_proxy': proxy_https | default('', true),
              'HTTPS_PROXY': proxy_https | default('', true),
              'no_proxy': proxy_no_proxy | default('', true),
              'NO_PROXY': proxy_no_proxy | default('', true)
            }
            | dict2items
            | selectattr('value', 'truthy')
            | items2dict
          }}

Die Lösung funk­tio­niert, erzeugt aber eine unnötige Ab­hän­gig­keit zwischen der In­itia­li­sie­rung durch set_fact und der späteren Verwendung.

  • default('', true):
    Behandelt neben un­de­fi­nier­ten Variablen auch null und andere falsy Werte. Diese werden durch einen Leer­string ersetzt. Für die an­schlie­ßen­de Filterung mit truthy ist das nicht zwingend er­for­der­lich, da dieser Test auch null und andere falsy Werte entfernt.

  • selectattr('value', 'truthy'):
    Entfernt alle Einträge mit falsy Werten aus dem Dic­tion­a­ry. Sind keine Proxy-Variablen definiert be­zie­hungs­wei­se enthalten diese keine gültigen Werte, entsteht ein leeres Dic­tion­a­ry {}. Damit werden keine zu­sätz­li­chen Proxy-Um­ge­bungs­va­ria­blen gesetzt.

  • default({}) am Play:
    Das Play-Keyword en­vi­ron­ment wird im Kontext der je­wei­li­gen Task-Aus­füh­rung aus­ge­wer­tet. Das gilt auch für das au­to­ma­ti­sche gather_facts vor den ei­gent­li­chen Tasks. Solange proxy_environment durch set_fact noch nicht existiert, ver­hin­dert default({}) einen Fehler durch eine un­de­fi­nier­te Variable.

  • set_fact und mehrere Plays:
    set_fact erzeugt eine host­spe­zi­fi­sche Variable, die auch in nach­fol­gen­den Plays desselben Playbook-Laufs verfügbar bleibt. Al­ler­dings nur für Hosts, auf denen der Task tat­säch­lich aus­ge­führt wurde. Andere Hosts, über­sprun­ge­ne In­itia­li­sie­run­gen oder ein direkter Einstieg in ein späteres Play können dazu führen, dass die Variable fehlt. Außerdem wird eine einmal erzeugte Variable nicht au­to­ma­tisch neu berechnet, wenn sich ihre Ein­gangs­va­ria­blen ändern.

Die Lösung: En­vi­ron­ment als lazy Variable

Ansible wertet Variablen erst aus, wenn sie benutzt werden. Das nutzen wir: Das En­vi­ron­ment wird als Ausdruck definiert, nicht als Task-Ergebnis. Die De­fi­ni­ti­on kann z.B. in group_vars/all.yml oder im Play stehen: 
proxy_environment: >-
  {{
    {
      'http_proxy': proxy_http | default('', true),
      'HTTP_PROXY': proxy_http | default('', true),
      'https_proxy': proxy_https | default('', true),
      'HTTPS_PROXY': proxy_https | default('', true),
      'no_proxy': proxy_no_proxy | default('', true),
      'NO_PROXY': proxy_no_proxy | default('', true)
    }
    | dict2items
    | selectattr('value', 'truthy')
    | items2dict
  }}

Im Play genügt dann eine Zeile:

- name: Prepare the hosts user
  hosts: all
  environment: "{{ proxy_environment }}"
  tasks:

    - name: Ausgehende IP ermitteln (Proxy-Test)
      ansible.builtin.uri:
        url: https://icanhazip.com
        return_content: true
      register: proxy_test


    - name: Ergebnis anzeigen
      ansible.builtin.debug:
        msg: "Ausgehende IP: {{ proxy_test.content | trim }}"

Vorteile:

  • Lazy Eva­lua­ti­on:
    Ansible wertet den Jinja2-Ausdruck der Variable proxy_environment bei ihrer Ver­wen­dung aus. Die Variable muss daher nicht vorher durch einen Task erzeugt werden. Der Wert ergibt sich aus den zu diesem Zeitpunkt ver­füg­ba­ren Ein­gangs­va­ria­blen.

  • Keine In­itia­li­sie­rung er­for­der­lich:
    Da proxy_environment bereits über die Va­ria­blen­de­fi­ni­ti­on verfügbar ist, entfällt der set_fact-Task voll­stän­dig. Ebenso ist kein default({}) am Play er­for­der­lich, sofern die zentrale Va­ria­blen­de­fi­ni­ti­on für alle be­trof­fe­nen Hosts geladen wird.

  • Un­ab­hän­gig­keit von der Aus­füh­rungs­rei­hen­fol­ge:
    Jedes Play kann proxy_environment un­mit­tel­bar verwenden. Es spielt keine Rolle, ob andere Plays zuvor aus­ge­führt wurden, ob das Playbook über import_playbook ein­ge­bun­den oder einzeln gestartet wird.

  • Host­spe­zi­fi­sche Aus­wer­tung:
    Die De­fi­ni­ti­on wird im je­wei­li­gen Host­kon­text aus­ge­wer­tet. Un­ter­schied­li­che Hosts können damit un­ter­schied­li­che Proxy-Kon­fi­gu­ra­tio­nen erhalten, ohne dass separate In­itia­li­sie­rungs­tasks notwendig sind.

  • Dy­na­mi­sche Aus­wer­tung:
    Im Gegensatz zu set_fact wird kein einmalig be­rech­ne­tes Ergebnis ge­spei­chert. Ändern sich die zugrunde liegenden Variablen während des Playbook-Laufs, kann die nächste Aus­wer­tung von proxy_environment diese Än­de­run­gen be­rück­sich­ti­gen.

  • Zentrale Wartung:
    Die Proxy-Logik existiert nur an einer Stelle. Jedes Play benötigt lediglich environment: "{{ proxy_environment }}". Än­de­run­gen an der Filterung oder den un­ter­stütz­ten Proxy-Variablen müssen deshalb nicht in mehreren Tasks gepflegt werden.

Fazit

Ein Keyword auf Play-Ebene und eine lazy Variable ersetzen die Task-Lösung. Du pflegst das Proxy-En­vi­ron­ment an einer einzigen Stelle, jedes Play bindet es mit einer Zeile ein, und das Playbook läuft mit und ohne Proxy, ohne dass du etwas anpassen musst.

Der Vorteil zeigt sich vor allem im Alltag: Beim nächsten Playbook musst du nicht mehr überlegen, ob der Proxy schon gesetzt wurde. Und wenn du zwischen Lab und Kun­den­netz wechselst, bleibt die Proxy-Logik dort, wo sie hingehört.

Hier findest du weitere » Cheat Sheets die dir das Leben als Linux-Admin leichter machen.

icon-arrow_right_medium-violet-blue.svg

Share this article

Facebook 
Twitter 
LinkedIn 
XING 
WhatsApp 
Email