Mach mich Wurzel

Ziel/Problem

Ich habe ein Ansible-Playbook welches ich beim Aufsetzen von Servern (oder auch anderen Maschinen) nutze.

Nutzt man ein Standard-Debian-Image legt man einen Nicht-Root-User bei der Installation an der sich per ssh einglogen kann.
Allerdings ist per Default kein sudo installiert und der Nicht-Root-Nutzer ist nicht in den sudoer-Dateien, kann also keine Befehle per sudo ausführen.

Man kann also nicht per ansible root-Befehle ausführen ohne ein Passwort hinterlegen/angeben zu müssen.

Ziel ist es das zu ändern:

  • Sicherstellen das sudo installiert ist

  • Festellen ob sudo (ohne Passwort) für den Nicht-Root-Nutzer funktioniert

    • ggf. ihn in die sudoers einzutragen

    • wenn notwendig interaktiv einmalig das root-Passwort abfragen

Lösung

Es muss ggf. das community.general-Modul nachinstalliert werden

ansible-galaxy -vv collection install community.general
Ansible Playbook
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- name: Checking/configure sudo
  hosts: all
  tasks:
    - name: Test if sudo works - refreshing the apt cache
      become: true
      become_method: sudo
      block:
      - name: Refresh apt repo
        ansible.builtin.apt:
          update_cache: true
      rescue:
        - pause:
            prompt: "Enter root password"
            echo: no
          register: prompt_data
        - ansible.builtin.set_fact:
            ansible_become_pass: "{{ prompt_data.user_input }}"
        - name: Install sudo
          become: true
          become_method: su
          ansible.builtin.apt:
            update_cache: true
            name: sudo
            state: present
        - name: Add login user to sudoers
          become: true
          become_method: su
          community.general.sudoers:
            commands: ALL
            name: "{{ ansible_user }}_ALL"
            user: "{{ ansible_user }}"
            nopassword: true

Check-Check

In Zeile 4 beginnt ein Task der nur dazu dient festzustellen das per sudo ohne Passwort root-Rechte erlangt werden können.

    - name: Test if sudo works - refreshing the apt cache
      become: true
      become_method: sudo
      block:
      - name: Refresh apt repo
        ansible.builtin.apt:
          update_cache: true

become_method: sudo versucht die höheren Rechte zu bekommen
Der eigentliche Task ist das auffrischen des apt-Caches, man könnte hier auch irgendetwas anderes ausführen.

Der block dient dazu weiter unten (Zeile 11) eine Fehlerbehandlung per rescue machen zu können.

To the Rescue

Ab Zeile 11, gekürzt
      rescue:
        - pause:
            prompt: "Enter root password"
            echo: no
          register: prompt_data

Zeile 11 definiert per rescue Aktionen die ausgeführt werden wenn der block fehlschlägt.
Das wäre dann der Fall wenn entweder sudo nicht installiert ist oder der Nutzer keine sudo-Rechte hat oder kein sudo ohne Passwort ausführen kann (oder wenn apt update fehlschlagen würde).
Das wäre der else-Block einer if-else-Anweisung.

Mach mal Pause
Ab Zeile 12
        - pause:
            prompt: "Enter root password"
            echo: no
          register: prompt_data

Der Pause-Block unterbricht die Ausführung und fragt das root-Passwort ab.

  • prompt-Parameter führt zu einer Eingabeaufforderung für den Benutzer

  • echo: no verhindert das das eingegebene Passwort auf der Kommandozeile sichtbar wird

  • register: prompt_data speichert das Ergebnis des Modul-Aufrufs pause in der Variable prompt_data

📝
Module können mehr als einen Rückgabewert haben → siehe Dokumentation für Modul Sektion Return Values .
Das pause-Modul z.B. gibt eine Reihe von Daten zurück. prompt_data enthält eine Hash-Map/Dictionary mit den Werten, u.a. user_input (die Benutzereingabe), delta (die Zeit die gewartet wurde) usw.
Fakten schaffen
Ab Zeile 16
        - ansible.builtin.set_fact:
            ansible_become_pass: "{{ prompt_data.user_input }}"

Setzt die Variable ansible_become_pass auf den Inhalt von prompt_data.user_input .
prompt_data waren die Rückgabewerte des pause-Moduls und user_input ist der Index an dem sich die Benutzereingabe befindet aka. das eingegeben Passwort.
ansible_become_pass ist eine interne Variable die das Passwort enthält sollte sudo oder su eines verlangen, kann auch per ansible-playbook -k angegeben werden (wird dann interaktiv abgefragt).

Werkzeug zur Macht installieren
Ab Zeile 18
        - name: Install sudo
          become: true
          become_method: su
          ansible.builtin.apt:
            update_cache: true
            name: sudo
            state: present

become_method wird explizit auf su gesetzt, da sudo möglicherweise nicht installiert ist.
Anschließend installiert ansible.builtin.apt sudo

Allmächtig werden
Ab Zeile 25
        - name: Add login user to sudoers
          become: true
          become_method: su
          community.general.sudoers:
            commands: ALL
            name: "{{ ansible_user }}_ALL"
            user: "{{ ansible_user }}"
            nopassword: true

become_method ist explizit auf su gesetzt, da der Benutzer wahrscheinlich noch keine sudo-Rechte hat und sudo damit fehlschlagen würde.
community.general.sudoers erzeugt in /etc/sudoers.d/ eine neue Datei mit den folgenden Parametern:

  • commands: ALL → der Benutzer kann per sudo alles ausführen, sudo ist also nicht auf bestimmte Programme/Befehle beschränkt

  • name: "{{ ansible_user }}_ALL" → Name der Datei.

    • ansible_user ist die Variable die den Namen des Login-Nutzers enthält

    • Datei würde dann also shellkraut_ALL heißen, wenn shellkraut der Login-User war

  • user: "{{ ansible_user }}" → Benutzer für den die Erlaubnis/Regel sein soll. Hier für den Login-Nutzer

  • nopassword: true → es wird kein Passwort abgefragt wenn sudo ausgeführt wird

In Summe hat der Nutzer sobald er eingelogt ist damit root-Rechte via sudo.