Dies ist eine alte Version des Dokuments!


Ansible - erweiterte Konfigurationsbeispiele: Ansible-Controll-Node und SSH-Jumphosts

Bild: Ansible Logo

In verteilten und getrennten Netz(werk)umgebungen ist es sehr oft nicht ohne Aufwand möglich zu Administrations- und Orchestrierungsaufgaben alle Hosts zu erreichen.

Schauen wir uns hierzu einfach mal nachstehende exemplarische Skizze an.

InternetDMZDMZ_HOSTsIntranetfremdgehostetes_SystemHost beim Housing-ProviderHostname <was-das-auch-immer-für-ein geiler-FQDN-sein-mag>IP-Adresse: aa-bb-cc-ddbredmzEDMZ-Netzwerkswitch (bredmz)Netz:10.0.0.0/24bridmzEDMZ-Netzwerkswitch (bridmz)Netz:10.10.0.0/24edmz_switchEDMZ-SwitchNetgear Typ: xFWBFQDN: vml000010.dmz.nausch.org--------Services: iptablesFWCFQDN: vml000020.dmz.nausch.org--------Services: iptablesIDMZ_Repository_HostFQDN: vml000050.dmz.nausch.orgCNAME : syslog, tftp, installIP (IDMZ) : eth0 - 52:54:00:19:08:67 - 10.0.0.50Services : syslog, httpd, rpm-repository, tftp/pxeEDMZ_Repository_HostFQDN: vml100080.dmz.nausch.orgCNAME : mailIP (EDMZ) : eth0 - 52:54:00:14:12:71 - 10.0.0.50Services : syslog, httpd, rpm-repository, tftp/pxeSwitchPhysikalischer Netzwerk-SwitchNetz 10.10.10.0/26WorkstationGerät: Djangos Admin-WorkstationHostname: pml010040CNAME: office-workMAC:IP:10.10.10.40Firewall_A---A-Firewall---

Von der Admin-Workstation bzw. dem Ansible-Controll-Node aus, wollen wir nun nicht nur zum nächstgelegenen Host erreichen, sondern auch zum übernächsten oder gar zu einem Host im Internet, den wir aber aus Sicherheitsgründen nicht direkt erreichen dürfen und auch können.

Die Komfortabelste Variante ist nun die Nutzung der Option ProxyCommand. Bei „nur“ einem Jump-Host kann man dies im Inventory noch recht bequem und einfach abbilden. Weitaus schwieriger wird die Sache, bei Umgebungen, bei denen mehrere Jumphost beteiligt sind. Hier bietet es sich an, die SSH-Client Konfiguration des Ansible Control- bzw Admin-Users zu verwenden!

Weitaus einfacher gestaltet sich o.g. Szenario in dem man auf die SSH-Clientkonfigurationsdatei ~/.ssh/config zurückgreift. Nachfolgendes Beispiel zeigt exemplarisch solch eine Clientspezifische Konfigurationsdatei:

 $ vim ~/.ssh/config
~/.ssh/config
# Default Werte
Host * 
    Port 22
    Protocol 2
    user admin
 
# Django : 2012-06-13
# ssh-jumps über mehrere Sprunghosts
 
# Erster Sprunghost (fwc) - direkt erreichbar
# Host -->  fwc
Host fwc
    Hostname firewall-c.idmz.nausch.org
    IdentityFile ~/.ssh/id_ed25519_idmz 
 
# Zweiter Sprunghost (fwb) - nur über fwc erreichbar
# Host -->  fwc --> fwb
Host fwb
    Hostname firewall-b.edmz.nausch.org
    IdentityFile ~/.ssh/id_ed25519_edmz
    ProxyCommand  ssh -A -q -W %h:%p fwc
 
# Dritter Sprunghost (fwa) - nur über fwb erreichbar
# Host --> fwc --> fwb --> fwa 
Host fwa
    Hostname firewall-a.nausch.org
    Port 22222
    user sysadmin
    IdentityFile ~/.ssh/id_ed25519_edmz
    ProxyCommand  ssh -A -q -W %h:%p fwc
 
# externer Server im Internet nur über externe Firewall "A" erreichbar
# also: Host --> fwc --> fwb --> fwa --> daxie
Host s1u7
    Hostname <was-das-auch-immer-für-ein geiler-FQDN-sein-mag>
    Port 42422
    user n3rd
    IdentityFile ~/.ssh/id_ed25519_n3rd
    ProxyCommand  ssh -A -q -W %h:%p fwa

Nun können wir ganz einfach direkt einen Tunnel zu unserem Zielhost aufspannen, genauso also würden wir den Zielhost direkt „sehen“.

 $ ssh fwa

Auch können wir nun ohne grossen Aufwand Dateien von einem Ende zum anderen Ende kopieren bzw. Ansible dies ermöglichen.

 $ scp ~/Downloads/enigmail-1.4-sm+tb.xpi 51u7:/tmp/
 $ scp 51u7:/home/8483/51lv14/04x.png .

Somit ist natürlich auch Ansible in der Lage die definierten Zielhost aus dem Inventory ohne Probleme zu erreichen, da hier die SSH-Client-Konfiguration unseres Admin-Accounts auf unserem Ansible-Controll-Node verwendet wird.

Die Pflege der Konfigurationsdatei ~/.ssh/config überlassen wir nun natürlich nicht jedem Admin einzeln, da dies viel zu fehleranfällig bzw. zeitaufwändig wäre.

Die aktuelle SSH-Clientconfigurationsdatei auf Djangos-Ansible-Controll-Node hat immerhin eine stattliche Anzahl an Konfigurationszeilen, Bsp.:

 $ cat ~/.ssh/config | wc -l
1860

Das will sicherlich niemand, auch wenn er noch so fleissig und gewissenhaft ist, per Hand erledigen! 8-)

Wir werden dies mit den Informationen aus dem zentral gepflegten Inventory und einem Ansible-Playbook bewerkstelligen. Somit ist sichergestellt, dass auf jedem Ansible-Controll-Node und jedem Admin die betreffende Datei zur Verfügung steht und somit die Ansible- Playbooks auch überall unter den gleichen (Labor-)Bedingungen laufen können!

Grundsätzlich alle möglichen Konfigurationsoptionen zur SSH-Clientkonfiguration finden wir in der zugehörigen man-page.

 $ man ssh_config

Sehen wir uns unsere bisher manuell gepflegte SSH-Clientkonfigurationsdatei mal etwas genauer an und blenden mal ggf. besondere Fälle mal aus, so stellen wir zwei Dinge fest.

Direkt erreichbare Hosts

Wenn wir uns einmal folgendes Beispiel betrachten, dann sehen wir folgende Dinge.

~/.ssh/config
...
 
Host pml010003
  Hostname 10.10.10.3
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_intranet
 
...

Wir haben demnach in Summe sechs gesetzte Parameter:

  • Host: Kurzname (alias) über den das Ziel erreichbar ist,
  • Hostname: FQDN oder IP-Adresse des Ziels,
  • User: User der zum Verbinden benötigt wird,
  • Port: SSH-Port des Zielsystems,
  • Protocol: verwendetes SSH-Protokoll sowie
  • IdentityFile: Datei des SSH-Keys, der bei der Verbindung verwendet werden muss.

Hosts erreichbar nur via Jump-Host

Bei einem Host, den wir nicht direkt erreichen können, sondern lediglich über einen Jump-Host stellen wir fest dass wir hier einen Parameter mehr haben.

~/.ssh/config
...
 
Host fwi
  Hostname 10.30.30.20
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
  ProxyCommand  ssh -q -W %h:%p vml030020
 
Host fw3
  Hostname 10.50.50.250
  User django
  Port 10022
  Protocol 2
  IdentityFile ~/.ssh/id_edmz
  ProxyCommand  ssh -q -W %h:%p fwi
 
...

Bei diesem Beispiel haben wir zwei Jumphost. Der erste Host fwi kann nicht direkt, sondern nur über den Jump-Host vml030020 erreicht werden. Der zweite Jump-Host fw3 wiederum ist nur über den ersten Jump-Host fwi erreichbar. Die entscheidende Konfigzeile mit ProxyCommand ist dabei jeweils:

ProxyCommand  ssh -q -W %h:%p <-Sprung-Host->

Der Wert <-Sprung-Host-> kennzeichnet nun, über welchen Jump-Host das Ziel erreichbar sein wird.

Zusätzlich zu den Parametern eines Standardhosts haben wir hier eine weitere Option ProxyCommand, der Definition wie und über welchen Jump-Host das Ziel erreicht werden kann. Wie Eingangs bereits erwähnt, werden wir nun die komplette SSH-Client-Konfigurationsdatei nicht manuell pflegen, sondern wir werden Ansible mit dieser Aufgabe betrauen, genauers hierzu finden wir in nachfolgenden Abschnitt dieses WIKI-Artikels.

Playbook: ssh_client_config.yml

Unser Playbook legen wir nun wie gewohnt in unserem Playbook-Verzeichnis ~/ansible/playbooks/ an.

 $ vim ~/ansible/playbooks/ssh_client_config.yml
ssh_client_config.yml
---                                         # YAML Start
                                            # Ansible Playbook zum automatisierten Anlegen der
                                            # SSH Client Konfigurationsdatei  ~/.ssh/config basierend
                                            # auf den Inhalten des Inventories.
                                            #
                                            # Aufruf aus dem entsprechenden Arbeits-Verzeichnis via:
                                            # ansible-playbook playbooks/ssh_client_config.yml
                                            #
- name: ssh_client_config.yml               # Name des Playbook
  hosts: localhost                          # Host die zur Anwendung kommen sollen
  vars:                                     #
    ssh_user: "{{ lookup('env','USER') }}"  # aktuellen Bneutzer ermitteln und Variable übergeben
                                            #
                                            #
  roles:                                    # Definition der zugehörigen Rollen
    - ssh_client                            # Prometheus Server: Installation und Konfiguration
...                                         # YML Ende

Dieses Playbook unterscheidet sich nun erst einmal gewaltig von denen der bisher hier gezeigten. Warum? Das werden wir uns nun im nachfolgendem Abschnitt ansehen.

Rolle: ssh_client

Im Gegensatz zu den bisherigen Playbook-Beispielen werden wir nun dem nächstem Ansible Kapitel "Rollen" hier etwas vorgreifen und das Playbook, welches zum Erzeugen der SSH-Client-Konfigurationsdatei verwendet wird, nicht mehr in einem flatfile schreiben, sondern dies entsprechend strukturiert aufbauen.

Die Grundvoraussetzungen in Form der betreffenden Verzeichnisstruktur haben wir ja bereits bei der initialen Ansible-Konfiguration erzeugt.

 $ tree ~/ansible/roles/common/ -d
/home/django/ansible/roles/common/
├── defaults
├── files
├── handlers
├── library
├── lookup_plugins
├── meta
├── module_utils
├── tasks
├── templates
└── vars

Dies nehmen wir nun als Kopiervorlage und erstellen eine neue Rolle ssh_client.

 $ cp -avr ~/ansible/roles/common/ ~/ansible/roles/ssh_client

In unserem Playbook haben wir bereits auf diese role verwiesen. Im Verzeichnis tasks legen wir nun die YML-Dateien mit den Aufgaben ab, die abgearbeitet werden sollen. Standardmäßig wird Ansible dort die Datei main.yml suchen und den Anweisungen dort folgen. Wir legen nun diese Datei an.

 $ vim ~/ansible/roles/ssh_client/tasks/main.yml
main.yml
---                                     # Playbook/Rollen zur Generierung der SSH Client-Config
- include_tasks: client_config.yml      # SSH Client Configdatei erzeugen und kopieren.
  tags: clientconfiguration             #
...                                     # YML Ende

Wie wir sehen, wird hier erst einmal nur ein weiterer task inkludiert, dessen YML-Datei noch fehlt. Diese Datei legen wir nun gleich noch als nächstes an.

 $ vim ~/ansible/roles/ssh_client/client_config.yml
client_config.yml
--- # YAML Start
    # SSH Client Configdatei erzeugen und kopieren.
- name: "Generieren und kopieren der SSH Client Konfiguration ~/.ssh/config."
  ansible.builtin.template:
    src: templates/ssh_client_config.j2
    dest: /home/{{ ssh_user }}/.ssh/config
    owner: '{{ ssh_user }}'
    group: '{{ ssh_user }}'
    mode: '0640'
... # YML Ende

Mit Hilfe dieses Tasks wird nun, mit Hilfe des Ansible Modules template, die Datei ~/.ssh/config im $HOME-Verzeichnis des aktuellen Admins angelegt. Die Variable ssh_user haben wir im Playbook definiert und füllen diese dann entsprechend beim Ausführen des Playbooks.

Man könte jetzt natürlich sagen, warum definiert man denn diesen Task nicht gleich direkt in der main.yml, ist doch eh' „nur eine“ Aufgabe. Ja, das könnte man durchaus so sagen. Aber wir gewöhnen uns am Besten gleich von Haus aus an, und strukturieren unsere Rollen entsprechend. Denn so hat man es später einfacher, wenn man bestimmte Teile nur bei der Ausführung haben möchte oder wenn man bestimmte Schritte z.B. ausschließen möchte.

Jinja2-Template: ssh_client_config.j2

In dem Task client_config hatten wir definiert, dass unsere SSH-Client-Konfiguration auf Basis eines Jinja2-Templates erstellt werden soll. Diese Datei legen wir nun als nächstes an.

FIXME … do geds weida! FIXME

:KRIT: FIXME :KRIT:

Diese Website verwendet Cookies. Durch die Nutzung der Website stimmen Sie dem Speichern von Cookies auf Ihrem Computer zu. Außerdem bestätigen Sie, dass Sie unsere Datenschutzbestimmungen gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information
  • linux/ansible/playbook_example_10.1664017381.txt.gz
  • Zuletzt geändert: 24.09.2022 11:03.
  • von django