[ OK ]Initialisiere Kernel...
~/im/blog
Beauftragen

Lass uns reden

Gibt es ein Infrastrukturproblem oder wird Unterstützung bei einem Projekt benötigt? Ich stehe für neue Aufgaben zur Verfügung.

Kontakt aufnehmen

Netzwerke

Hier bin ich online zu finden.

© 2026 Irfan Miral. Alle Rechte vorbehalten.Entwickelt vonIrfan Miral
DatenschutzerklärungAGB
StartDiensteLebenslaufBlogKontaktTools
2026-02-05• 5 Min. Lesezeit

Das Ansible-Playbook für jeden neuen Server

DevOps Ansible Automation Linux Administration

Werbung

Ich habe bereits über meine Checkliste geschrieben, die ich bei jedem neuen Server abarbeite: ein Non-Root-User, Key-Only-SSH, eine restriktive Firewall (Default Deny), Fail2ban und automatische Sicherheitsupdates. Das alles manuell zu erledigen, dauert keine Stunde. Es ist wirklich nicht schwer.

Aber "keine Stunde, manuell, für jeden Server" summiert sich verdammt schnell, sobald man mehr als drei Server verwaltet. Und genau bei manuellen Schritten schleichen sich die kleinen Unstimmigkeiten ein. Der eine Server bekommt MaxAuthTries 3 gesetzt, der andere nicht – einfach, weil derjenige, der den Server an dem Tag eingerichtet hat, es eilig hatte.

Die Lösung ist simpel: Man gießt die Checkliste in ein Playbook. Gleiche Schritte, gleiche Reihenfolge, jedes Mal. Und es ist idempotent: Führt man es auf einem bereits konfigurierten Server erneut aus, ändert sich absolut nichts.

Das Playbook

---
- name: Baseline hardening for a new server
  hosts: new_servers
  become: true

  vars:
    admin_user: deploy
    ssh_public_key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"

  tasks:
    - name: Create admin user
      user:
        name: "{{ admin_user }}"
        groups: sudo
        shell: /bin/bash
        create_home: true

    - name: Add SSH key for admin user
      authorized_key:
        user: "{{ admin_user }}"
        key: "{{ ssh_public_key }}"

    - name: Harden SSH config (overrides cloud-init defaults)
      copy:
        dest: /etc/ssh/sshd_config.d/99-hardening.conf
        content: |
          PermitRootLogin no
          PasswordAuthentication no
          MaxAuthTries 3
        mode: '0644'
      notify: restart sshd

    - name: Install UFW and Fail2ban
      apt:
        name: [ufw, fail2ban]
        state: present
        update_cache: true

    - name: Configure UFW defaults
      ufw:
        direction: "{{ item.direction }}"
        policy: "{{ item.policy }}"
      loop:
        - { direction: incoming, policy: deny }
        - { direction: outgoing, policy: allow }

    - name: Allow required ports
      ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop: ['22', '80', '443']

    - name: Enable UFW
      ufw:
        state: enabled

    - name: Enable Fail2ban
      systemd:
        name: fail2ban
        enabled: true
        state: started

    - name: Install unattended-upgrades
      apt:
        name: unattended-upgrades
        state: present

  handlers:
    - name: restart sshd
      systemd:
        name: ssh
        state: restarted

Die Ausführung

Sobald ein neuer Server online ist, wird er dem Inventory hinzugefügt und das Playbook läuft exakt gegen diesen einen Host:

ansible-playbook -i inventory.ini baseline.yml -l new-server-01 -u root

Der allererste Lauf muss als root erfolgen – das ist auf einem frischen Server der einzig existierende Benutzer. Sobald das Playbook durchgelaufen ist, ist der Root-Login deaktiviert. Ab diesem Moment ist der User deploy der Einsprungpunkt:

ansible-playbook -i inventory.ini baseline.yml -l new-server-01 -u deploy

Der erste Lauf übernimmt die ganze Arbeit: Er erstellt den Benutzer, riegelt SSH ab und richtet die Firewall ein. Der Handler startet sshd nur dann neu, wenn sich die Config auch wirklich geändert hat. Jeder weitere Lauf ist im Grunde nur noch eine Nulloperation (No-Op), die mir bestätigt, dass die Konfiguration nicht abgedriftet ist.

Interaktiv ausprobieren: Mit dem Ansible Baseline Generator auf dieser Website kann dieses Playbook über ein UI konfiguriert werden. Man legt den Admin-Nutzernamen und den SSH-Port fest, wählt die gewünschten Module aus und kopiert dann einfach den exakten YAML-Output.

Warum Idempotenz das eigentliche Ziel ist

Der wahre Mehrwert liegt nicht in der gesparten Zeit am ersten Tag. Die Befehle manuell einzutippen, ist nicht so langsam. Der Wert zeigt sich sechs Monate später: Wenn ich mir nicht sicher bin, ob ein Server das volle Hardening-Programm bekommen hat oder ob er während eines Incidents in Hektik hochgezogen wurde, lasse ich das Playbook einfach noch einmal laufen.

Wenn alles passt, meldet Ansible null Änderungen und ich hake die Sache ab. Wenn etwas fehlt, wird es auf der Stelle repariert. Ich muss mich nicht mehr daran erinnern, welcher der fünf manuellen Schritte damals vielleicht übersprungen wurde.

Dieses Playbook ist absichtlich so kompakt gehalten. Es installiert keine Application-Stacks und konfiguriert keine projektspezifischen Dienste. Es ist das absolute Fundament, auf dem jeder Server stehen muss, bevor irgendetwas anderes hinzukommt. Indem man es von anwendungsspezifischen Playbooks trennt, bleibt es stabil. Und eine Baseline, die sich kaum verändert, ist eine, der man blind vertrauen kann.

Werbung

Brauchen Sie hierbei Hilfe?

Wenn Sie das Thema Containerisierung & Automatisierung lieber abgeben möchten – genau das mache ich beruflich.

Kontakt aufnehmen
VorherigerEine GitLab CI/CD-Pipeline, die für die meisten kleinen Projekte reichtNächster Warum Server-Configs in Git gehören und nicht in deinen Kopf