Inhaltsverzeichnis

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

Bild: Ansible Logo

Ausgangssituation

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!

Lösungsmöglichkeit

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.

Host-Beispiele

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:

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.

Ansible-Playbook zum Erstellen der SSH-Client-Konfigurationsdatei

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.

 $ vim ~/ansible/roles/ssh_client/templates/ssh_client_config.j2
ssh_client_config.j2
# Generiert mit Hilfe von Ansible am {{ ansible_date_time.date }} - diese Datei nicht manuell bearbeiten!
# Clientkonfigurationsbeispiel für unterschiedliche Zielsysteme
 
## statische Konfiguration
# localhost
Host localhost
  Hostname  127.0.0.1
  IdentityFile ~/.ssh/id_intra
 
# externer Einwahl-Hosts
Host example
  Hostname 93.184.216.34
  Port 12345
  Protocol 2
  ForwardX11 yes
  ForwardAgent yes
  IdentityFile ~/.ssh/id_example
 
## dynamisch aus dem Inventory generierte Konfiguration
# interne Systeme - DMZ
{% for host in groups['DMZ'] %}
Host {{ host }}
  Hostname {{ hostvars[host]['host_ipv4'] }}
  User {{ hostvars[host]['ssh_user'] }}
  Port {{ hostvars[host]['ssh_port'] }}
  Protocol {{ hostvars[host]['ssh_protocol'] }}
  IdentityFile {{ hostvars[host]['ssh_keyfile'] }}
 
Host {{ hostvars[host]['host_alias'] }}
  Hostname {{ hostvars[host]['host_ipv4'] }}
  User {{ hostvars[host]['ssh_user'] }}
  Port {{ hostvars[host]['ssh_port'] }}
  Protocol {{ hostvars[host]['ssh_protocol'] }}
  IdentityFile {{ hostvars[host]['ssh_keyfile'] }}
 
{% endfor %}
# interne Systeme - Intranet
{% for host in groups['intranet'] %}
Host {{ host }}
  Hostname {{ hostvars[host]['host_ipv4'] }}
  User {{ hostvars[host]['ssh_user'] }}
  Port {{ hostvars[host]['ssh_port'] }}
  Protocol {{ hostvars[host]['ssh_protocol'] }}
  IdentityFile {{ hostvars[host]['ssh_keyfile'] }}
 
Host {{ hostvars[host]['host_alias'] }}
  Hostname {{ hostvars[host]['host_ipv4'] }}
  User {{ hostvars[host]['ssh_user'] }}
  Port {{ hostvars[host]['ssh_port'] }}
  Protocol {{ hostvars[host]['ssh_protocol'] }}
  IdentityFile {{ hostvars[host]['ssh_keyfile'] }}
 
{% endfor %}
# externe System
{% for host in groups['freifunk'] %}
Host {{ host }}
  Hostname [{{ hostvars[host]['host_ipv6'] }}]
  User {{ hostvars[host]['ssh_user'] }}
  Port {{ hostvars[host]['ssh_port'] }}
  Protocol {{ hostvars[host]['ssh_protocol'] }}
  IdentityFile {{ hostvars[host]['ssh_keyfile'] }}
  ProxyCommand  ssh -q -W %h:%p {{ hostvars[host]['host_sshjump'] }}
 
Host {{ host }}-extern
  Hostname {{ hostvars[host]['host_ipv6'] }}
  User {{ hostvars[host]['ssh_user'] }}
  Port {{ hostvars[host]['ssh_port'] }}
  Protocol {{ hostvars[host]['ssh_protocol'] }}
  IdentityFile {{ hostvars[host]['ssh_keyfile'] }}
 
{% endfor %}
{% for host in groups['raspbian'] %}
Host {{ host }}
  Hostname {{ hostvars[host]['host_ipv4'] }}
  User {{ hostvars[host]['ssh_user'] }}
  Port {{ hostvars[host]['ssh_port'] }}
  Protocol {{ hostvars[host]['ssh_protocol'] }}
  IdentityFile {{ hostvars[host]['ssh_keyfile'] }}
  ProxyCommand  ssh -q -W %h:%p {{ hostvars[host]['host_sshjump'] }}
 
Host {{ host }}-intern
  Hostname {{ hostvars[host]['host_ipv4'] }}
  User {{ hostvars[host]['ssh_user'] }}
  Port {{ hostvars[host]['ssh_port'] }}
  Protocol {{ hostvars[host]['ssh_protocol'] }}
  IdentityFile {{ hostvars[host]['ssh_keyfile'] }}
 
{% endfor %}

Das Beispiel passen wir natürlich unserer Umgebung entsprechend an. Einzelne Netzsegmente haben wir in unserem Inventory in Hostgruppen unterteilt. Basierend auf den Host-Definitionen im Inventory werden die Variablen entsprechend mit den spezifischen Hostdaten gefüllt um damit dann beim Lauf des Playbooks die passende SSH-Client-Konfigurationsdatei zu schreiben. Mit Hilfe der Schleifen, welche jeweils durch {% for host in groups['<--HOST-GROUP-NAME-->'] %} und {% endfor %} eingeschlossen sind, werden beim Abarbeiten des Playbooks für jeden Host aus der spezifischen Gruppe (repräsentiert durch den Namen der Hostgruppe <--HOST-GROUP-NAME-->) Abschnitte in der ~/.ssh/config-Datei hinterlegt.

Inventory

Die Konfigruationsdaten unserer Host halten wir in unserem Inventory vor. Hierbei spielt es grundlegend keine Frage ob dies manuell gepflegt, oder scriptiert aus einer CMBD exportiert und aufbereitet wurde.

Für unser Playbook-Beispiel hier greifen wir auf das exemplarische Inventory, mit welchem wir uns im Kapitel Ansible - Erweiterte Konfigurationsbeispiel: Inventory bereits intensiv beschäftigt hatten, zurück. Nachfolgend sehen wir, wie solch eine Inventory Datei aufgebaut und strukturiert sein könnte. Das exemplarische Beispiel ist hier entsprechend gekürzt und die Stellen mit Hilfe von ... markiert wiedergegeben„

 $ less ~/ansible/inventories/production/hosts
hosts
# Generiert mit Hilfe von Ansible am 2022-09-20 - diese Datei nicht manuell bearbeiten!
# Inventory Datei für die System-Umgebung bei nausch.org
#
# Hinweise:
#           Kommentare beginnen mit einem '#'-Zeichen
#           leere Zeilen werden ignoriert
#           Host- und Gruppendefinitionen werden mit [] abgegrenzt
#           Hosts können über ihren Hostnamen, FQN oder ihrer IP-Adresse definiert
#           übergeordnete Gruppen werden durch [:children] abgegrenzt
#
# Host-Definitionen
 
# Hosts ohne Gruppenzuordnung
localhost
 
[intranet]
pml010002
pml010003
pml010004
...
...
pml010124
pml010125
pml010126
 
[IDMZ]
vml030010
vml030020
vml030030
vml030040
...
...
vml030230
vml030240
vml030250
 
[EDMZ]
vml050010
vml050020
vml050030
vml050040
vml050250
 
[TKDMZ]
vml070010
vml070020
vml070030
 
[external]
customer_no_001
customer_no_002
...
...
customer_no_042
 
[gluon]
ff_pliening_gbw__ug_
ff_pliening_gbw_egod
ff_pliening_gbw_ogod
ff_pliening_gbw_dgod
ff_pliening_gbw_cpod
ff_roding_fwg_nausch
 
[raspbian]
ff_pliening_rpb4_ol_v6
 
# Host-Gruppen-Definitionen 
# (zu welcher Gruppe gehören Untergruppen bzw. Hosts)
 
[freifunk:children]
gluon
raspbian
 
[linux:children]
intranet
IDMZ
EDMZ
TKDMZ
external

Die betreffenden Hostspezifischen Variablen halten wir hier in entsprechenden Dateien bzw. Unterverzeichnissen vor. Das nachfolgende Beispiel hier zeigt die Host-spezifischen Variablen eines Hosts im Intranet.

 $ less ~/ansible/inventories/production/host_vars/pml111002
vml030010
# Generated by Ansible on 2022-09-20, do not edit manually!
host_alias: fw1
host_mac: "84:3b:de:ad:be:ef"
host_ipv4: "10.30.30.2"
host_ipv6: "::1"
ssh_port: 22
ssh_protocol: 2
ssh_keyfile: ~/.ssh/id_idmz"

Als Beispiel für einen externen Host, der nur via Jump-Host erreichbar ist sehen wir uns die Inventory-Host-Definition des Hosts ff_pliening_gbw__ug_ näher an.

 $ less ~/ansible/inventories/production/host_vars/ff_pliening_gbw__ug_ 
ff_pliening_gbw__ug_
# Generated by Ansible on 2022-09-20, do not edit manually!
host_alias: 
host_ipv4:
host_ipv6: 2001:678:e68:102:32b5:c2ff:fe56:62b1
ssh_user: root
ssh_port: 22
ssh_protocol: 2
ssh_keyfile: ~/.ssh/id_freifunk
host_sshjump: vml070010
 
branch: "stable"
domain: "ffmuc_muc_ost"
director: "ffmuc_muc_ost"
node_contact_address: "Django [BOfH] | django@nausch.org | chat: @django"
node_hostname: "ff_pliening_gbw_antipode"
node_latitude: "-48.19861319429455"
node_longitude: "-168.2017571420684"
node_model: "TP-Link TL-WDR4300 v1"
node_share_location: "True"
node_ghostmode: "False"
node_release: "experimental"
node_autoupdate: "False"

Hier sehen wir nun dass unter anderem ein anderer SSH-User, ein anderes SSH-Keyfile sowie ein Jump-Host benutzt wird. Ferner finden sich im Anschluß noch weitere Host-spezische Variablen, für die Konfiguration des betreffenden Freifunk-Knotens.

Playbook-Lauf

Dank der Vorkonfiguration unseres Ansible-Controll-Nodes, welche wir im Kapitel Ansible - erweiterte Konfigurationsbeispiele: Ansible mit Hilfe von Ansible einrichten hier in Djangos WIKI vorgenommen hatten, reicht zum Starten des Playbooks folgender Befehlsaufruf:

 $ ansible-playbook ~/ansible/playbooks/ssh_client_config.yml


PLAY [ssh_client_config.yml] ******************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************
ok: [localhost]

TASK [ssh_client : include_tasks] *************************************************************************************************
included: /home/django/ansible/roles/ssh_client/tasks/client_config.yml for localhost

TASK [ssh_client : Generieren und kopieren der SSH Client Konfiguration ~/.ssh/config.] *******************************************
ok: [localhost]

PLAY RECAP *************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Ergebnis: ~/.ssh/config

Als Ergebnis erhalten wir dann quasi auf Knopfdruck immer eine aktuelle SSH-Client-Konfigurationsdatei ~/.ssh/config, basierend auf den Konfigurationsdaten aus unsere Inventory.

~/.ssh/config
# Generiert mit Hilfe von Ansible am 2022-09-24 - diese Datei nicht manuell bearbeiten!
# Clientkonfigurationsbeispiel für unterschiedliche Zielsysteme
 
## statische Konfiguration
# localhost
Host localhost
  Hostname  127.0.0.1
  IdentityFile ~/.ssh/id_intra
 
# externer Einwahl-Hosts
Host example
  Hostname 93.184.216.34
  Port 12345
  Protocol 2
  ForwardX11 yes
  ForwardAgent yes
  IdentityFile ~/.ssh/id_example
 
## dynamisch aus dem Inventory generierte Konfiguration
# interne Systeme - IDMZ
Host vml030010
  Hostname 10.30.30.10
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
 
Host fw1
  Hostname 10.30.30.10
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
 
Host vml030020
  Hostname 10.30.30.20
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
 
Host fw2
  Hostname 10.30.30.2
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
 
Host vml030030
  Hostname 10.30.30.30
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
 
Host 
  Hostname 10.30.30.30
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
 
...
...
 
Host vml030250
  Hostname 10.30.30.250
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
 
Host db_clusternode_3
  Hostname 10.30.30.250
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_idmz
 
# interne Systeme - Intranet
Host pml010002
  Hostname 10.10.10.2
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_intra
 
Host kvm_1
  Hostname 10.10.10.2
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_intra
 
...
...
 
Host pml010126
  Hostname 10.10.10.126
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_intra
 
Host feinstaubsensor
  Hostname 10.10.10.126
  User django
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_intra
 
# externe System
Host ff_pliening_gbw__ug_
  Hostname [2001:678:e68:102:32b5:c2ff:fe56:62b1]
  User root
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_freifunk
  ProxyCommand  ssh -q -W %h:%p vml070010
 
Host ff_pliening_gbw__ug_-extern
  Hostname 2001:678:e68:102:32b5:c2ff:fe56:62b1
  User root
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_freifunk
 
...
...
 
Host ff_roding_fwg_nausch
  Hostname [2001:678:e68:109:8e3b:adff:feeb:f2a6]
  User root
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_freifunk
  ProxyCommand  ssh -q -W %h:%p vml070010
 
Host ff_roding_fwg_nausch-extern
  Hostname 2001:678:e68:109:8e3b:adff:feeb:f2a6
  User root
  Port 22
  Protocol 2
  IdentityFile ~/.ssh/id_freifunk

Fazit und Ausblick

Die manuelle, zeitraubende und ggf. Fehlerbehaftete Pflege der SSH-Client-Konfigurations-Datei durch mehrere Admins auf verschiedenen Ansible-Kontroll-Knoten ist somit Geschichte. Ferner sind wir unabhängig und können so viele SSH-Jump-Hosts verwenden, die eben zum Erreichen der Zielhost von Nöten sind.

Die initiale Fragestellung Wie wird sicher gestellt, dass alle Ziele auch erreichbar sind?, die wir bei unseren Vorüberlegungen angestellt hatten, können wir also auch als erfolgreich erledigt abhaken und wir sind bei unserem Ziel von Automatisierung und Orchestrierung einen wesentlichen Schritt weiter gekommen. :UP:

Links