Ansible-Playbook: SSH-Zugriff beschränken

Veröffentlicht am Kategorisiert in Ansible, FreeIPA, Linux Keine Kommentare zu Ansible-Playbook: SSH-Zugriff beschränken

SSH ist ein mächtiges Protokoll und eine der Grundlagen von Ansible. In einem Arbeitsumfeld möchte man aber nicht unbedingt jedermann den Zugriff auf fremde Rechner ermöglichen, sobald ein Passwort einmal bekannt wird, erst recht nicht, wenn es sich um das root-Passwort eines Rechners handelt.

In einem früheren Beitrag habe ich bereits den SSH-Zugang generell mittels Host-Based-Access-Control-Regeln von FreeIPA eingeschränkt und nur wenigen Benutzern erlaubt. Hier gehe ich einen Schritt weiter und schränke die konkreten Möglichkeiten der Authentifizierung für den SSH-Verbindungsaufbau ein, indem ich die leicht skriptbare Passwort-Authentifizierung (PasswordAuthentication) und die Verbindung als root-Benutzer (PermitRootLogin) ausschalte, aber die interaktive Passwort-Authentifizierung (ChallengeResponseAuthentication) erlaube, die dank HBAC-Regeln aber nur wenigen erlaubt ist.

1. Ansible-Playbook erstellen

Das folgende Ansible-Playbook, das im Wesentlichen dem sehr empfehlenswerten Beitrag Securing a Server with Ansible | ryaneschinger.com entnommen ist, wird der SSH-Zugriff auf allen Rechnern schnell auf Public-Key-Authentication eingeschränkt, und die entfernte Anmeldung als root-Benutzer wird gleich ganz unterbunden:

---
- hosts: "{{ targets }}"
  tasks:
  - name: Disable root SSH access.
    lineinfile:
      path=/etc/ssh/sshd_config
      regexp="^PermitRootLogin"
      line="PermitRootLogin no"
      state=present
    notify: restart sshd
  - name: Disable SSH password authentication.
    lineinfile:
      path=/etc/ssh/sshd_config
      regexp="^PasswordAuthentication"
      line="PasswordAuthentication no"
      state=present
  - name: Enable SSH keyboard-interactive authentication.
    lineinfile:
      path=/etc/ssh/sshd_config
      regexp="^ChallengeResponseAuthentication"
      line="ChallengeResponseAuthentication yes"
      state=present
    notify: restart sshd
  handlers:
  - name: restart sshd
    service: name=sshd state=restarted
...

Das YAML-Format der Ansible-Playbooks ist gewöhnungsbedürftig, aber es gibt alternative Formate, auf die ich hier allerdings nicht eingehe. Die meisten Playbooks, die man online findet, sind im YAML-Format geschrieben.

Ich speichere dieses Playbook auf dem Ansible-Server unter /opt/playbooks/restrict_ssh_access.yml. Dieses Playbook besteht aus zwei sogenannten Tasks, die zusammen ein Play bilden. Beide Tasks suchen (mithilfe von regexp, also einem regulären Ausdruck) und ersetzen eine bestimmte Zeile in der unter path angegebenen Datei durch die unter line angegebene Zeile. Danach verlangen Sie einen Neustart des SSH-Dienstes, der von einem entsprechenden Handler, welcher auf der Hierarchieebene der hosts-Zeile definiert ist, am Ende des Plays ausgeführt wird.

Im Playbook gebe ich ganz bewusst nicht an, mit wessen Benutzerrechten diese Aufgaben ausgeführt werden sollen, obwohl ich das problemlos sowohl für das ganze Playbook, für ein einzelnes Play oder für einzelne Tasks tun könnte. Letztendlich ist mir egal, wer diese Aufgaben ausführt, solange er bloß dazu berechtigt ist. Darum gebe ich die Anmedeldeinformationen erst beim Ausführen des Playbooks an.

2. Playbook ausführen

Das Playbook lässt sich mit der Option --check testen, ohne es auszuführen. Ansible meldet dann bloße Prognosen über den Ausgang. Da ich im Playbook in der hosts-Zeile keine Rechner oder Rechnergruppen aus dem Ansible-Inventory bestimme, sondern die Variable targets angebe, kann ich das Playbook für beliebige Rechner einsetzen, indem ich erst bei der Ausführung in der Option --extra-vars die Variable targets definiere (im unteren Beispiel schlicht all, also alle Rechener im Inventory).

Wenn ich bereits mit meinem Ansible-Benutzer angemeldet bin und keinen Benutzer für die Remote-Verbindung mit den entfernten Rechnern angebe, verbindet Ansible sich automatisch mit dem angemeldeten Benutzer, und da mein Ansible-Benutzer bereits SSH-Zugriff mit Public-Key-Authentication auf alle Rechner der Domäne hat, kann Ansible sich problemlos überallhin verbinden. Wenn der Ansible-Benutzer aber einmal verbunden ist, muss er die Befehle aktiv mit sudo-Rechten ausführen, weil das Playbook Systemdateien verändert. Darum füge ich die Optionen --become (für die Ausführung als anderer Benutzer, standardmäßig root) und --become-method (obwohl bereits standardmäßig sudo) sowie --ask-become-pass (zur Eingabe des Passworts des sudo-berechtigten Benutzers) hinzu.

ansible-playbook /opt/playbooks/restrict_ssh_access.yml --extra-vars="targets=all" --become --ask-become-pass --check

Damit andere Benutzer das Playbook ausführen können, müssten sie den Befehl um die Optionen --user und --ask-pass erweitern, da die FreeIPA-Richtlinien ihnen selber momentan keinen SSH-Zugriff gestatten. Die Option --ask-pass ist außerdem nötig, weil sie selber keine Public-Key-Authentication zu den Rechnern eingerichtet haben und sich darum mit dem Passwort des Ansible-Benutzers autorisieren müssen:

ansible-playbook /opt/playbooks/restrict_ssh_access.yml --extra-vars="targets=all" --user=ansible.ssh --ask-pass --become --ask-become-pass --check

So oder so könnte die Ausgabe des Befehls etwa folgendermaßen aussehen:

PLAY [all] *******************************************************************************

TASK [Gathering Facts] *******************************************************************
ok: [cgn-ansible01.ipa.animentor.de]
ok: [cgn-ipa01.ipa.animentor.de]
ok: [cgn-ipa02.ipa.animentor.de]

TASK [Disable root SSH access.] **********************************************************
changed: [cgn-ipa02.ipa.animentor.de]
changed: [cgn-ansible01.ipa.animentor.de]
changed: [cgn-ipa01.ipa.animentor.de]

TASK [Disable SSH password authentication.] **********************************************
changed: [cgn-ipa01.ipa.animentor.de]
changed: [cgn-ipa02.ipa.animentor.de]
changed: [cgn-ansible01.ipa.animentor.de]

TASK [Enable SSH keyboard-interactive authentication.] ***********************************
changed: [cgn-ipa02.ipa.animentor.de]
changed: [cgn-ipa01.ipa.animentor.de]
changed: [cgn-ansible01.ipa.animentor.de]

RUNNING HANDLER [restart sshd] ***********************************************************
changed: [cgn-ipa01.ipa.animentor.de]
changed: [cgn-ipa02.ipa.animentor.de]
changed: [cgn-ansible01.ipa.animentor.de]

PLAY RECAP *******************************************************************************
cgn-ansible01.ipa.animentor.de : ok=5    changed=4    unreachable=0    failed=0   
cgn-ipa01.ipa.animentor.de : ok=5    changed=4    unreachable=0    failed=0   
cgn-ipa02.ipa.animentor.de : ok=5    changed=4    unreachable=0    failed=0 

Wenn unerwartete Probleme auftreten, helfen übrigens die Optionen --verbose oder (noch wesentlich ausführlicher) -vvv mit Details zum Verbindungsaufbau, den eingesetzten Ansible-Modulen und dem Playbook-Ablauf weiter.

Warum benutzen wir nicht die Option --become-user, wenn wir doch den Ansible-Benutzer mit sudo-Rechten verwenden wollen? Dazu muss man das sudo-Konzept grundsätzlich verstehen: Tatsächlich wollen wir gerade nicht, dass die Aktionen mit den Rechten vom Ansible-Benutzer ausgeführt werden, sondern wir wollen seine sudo-Rechte nutzen, um die Aktionen mit root-Rechten auszuführen. Die Option --become-user ansible.ssh wäre also gleichbedeutend mit sudo -u ansible.ssh und gerade nicht mit sudo. Da der SSH-Zugriff bereits mit dem Ansible-Benutzer hergestellt wird, bezieht sich der sudo-Befehl auf diesen Benutzer und verlangt, da keine weiteren sudo-Optionen verwendet werden, root-Rechte – und die werden ihm auch gewährt, weil sudo auf dem entfernten Rechner dank der FreeIPA-Richtlinie für diesen Benutzer konfiguriert wurde.[

Nicht nur simuliert, sondern ausgeführt wird das Playbook ohne die Option --check:

ansible-playbook /opt/playbooks/restrict_ssh_access.yml --extra-vars="targets=all" --become --ask-become-pass

3. Ergebnisse prüfen

Ob das Playbook wie gewünscht den SSH-Zugriff restriktiver gemacht hat, kann man einmal über die Anzeige der SSHD-Konfiguration auf den entfernten Rechnern und dann natürlich über entsprechende Verbindungsversuche prüfen:

sudo grep ^PasswordAuthentication /etc/ssh/sshd_config
sudo grep ^ChallengeResponseAuthentication /etc/ssh/sshd_config
sudo grep ^PermitRootLogin /etc/ssh/sshd_config

Die Ausgaben sollten jeweils lauten:

PasswordAuthentication no
ChallengeResponseAuthentication yes
PermitRootLogin no

Damit ist das Ziel erreicht und geichzeitig Ansibles Funktionalität unter Beweis gestellt.

Von Pascal Korz

IT Systems Engineer in und aus dem schönen Köln

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert