# Ansible + PatchMon — zarządzanie flotą serwerów Inwentarz jest pobierany dynamicznie z PatchMon (`patchmon_inventory.yml`, plugin `patchmon.dynamic_inventory`). Grupy hostów pochodzą wprost z grup przypisanych w PatchMon — filtrowanie na potrzeby playbooków (`--limit`) odbywa się przez te grupy, nie przez ręczne listy hostów. ## Pierwsze uruchomienie (świeży klon repozytorium) Sekrety nie są trzymane w gicie — trzeba je uzupełnić lokalnie: ```bash cp patchmon_inventory.yml.example patchmon_inventory.yml # uzupełnij api_url / api_key / api_secret w patchmon_inventory.yml mkdir -p ssh_keys && chmod 700 ssh_keys cp /ścieżka/do/twojego/klucza ssh_keys/id_rsa && chmod 600 ssh_keys/id_rsa ``` ## Grupy PatchMon używane w tym projekcie | Grupa | Znaczenie | |---|---| | `root` | Hosty logowane bezpośrednio na konto `root`. | | `mgmt` | Hosty logowane na konto `mgmt` (z `sudo`). | | `Update-now` | Wszystkie hosty dopuszczone do automatycznej aktualizacji (`root` ∪ `mgmt`). Domyślny cel dla crona/autopilota. | | `Type-HOST` | Fizyczne hosty Proxmox — traktuj ostrożniej (restart hosta restartuje też jego VM/CT). | | `Type-VM` / `Type-CT` | Maszyny wirtualne / kontenery LXC. | | `Site-nni` / `Site-on` | Lokalizacje. | Konto logowania (`ansible_user`) **nie** jest już podawane ręcznie przez `-e` — jest ustawiane automatycznie przez `group_vars/root.yml` i `group_vars/mgmt.yml` na podstawie grupy PatchMon danego hosta. Dzięki temu jedno uruchomienie playbooka na grupie mieszanej (np. `Update-now`) obsłuży poprawnie oba typy kont. ## Struktura projektu ``` ssh_keys/ id_rsa # klucz prywatny SSH (poza gitem, patrz sekcja "Sekrety") group_vars/ all.yml # klucz SSH, become, próg dysku, lista kluczy admina root.yml / mgmt.yml # ansible_user per grupa PatchMon playbooks/ playbook_upgrade.yml # apt update + dist-upgrade playbook_cleanup.yml # autoremove/autoclean, czyszczenie journala, alarm dysku playbook_docker_cleanup.yml # czyszczenie nieużywanych obrazów Docker (tylko hosty z Dockerem) playbook_fleet_report.yml # raport: OS, uptime, dysk, liczba aktualizacji, reboot-required playbook_security_baseline.yml # unattended-upgrades + wyłączenie logowania hasłem SSH playbook_ssh.yml # blokada pustych haseł SSH playbook_ssh_keys.yml # zarządzanie kluczami publicznymi na koncie logowania playbook_autopilot.yml # orkiestracja pod cron: upgrade -> cleanup -> fleet_report templates/ 50unattended-upgrades.j2 fleet_report.j2 cron/ run-autopilot.sh # wrapper do crona (flock + log) crontab.example ansible-autopilot.logrotate reports/ # tu trafiają wygenerowane raporty floty ``` ## Użycie ```bash # Aktualizacja pakietów na całej flocie dopuszczonej do auto-update ansible-playbook -i patchmon_inventory.yml playbooks/playbook_upgrade.yml --limit "Update-now" # Sprzątanie/kondycja systemu (autoremove, journal, alarm zajętości dysku) ansible-playbook -i patchmon_inventory.yml playbooks/playbook_cleanup.yml --limit "Update-now" # Czyszczenie nieużywanych obrazów Docker (tylko hosty, na których jest Docker) ansible-playbook -i patchmon_inventory.yml playbooks/playbook_docker_cleanup.yml --limit "Update-now" # Raport stanu floty -> plik w reports/ # WAŻNE: dopisz ",localhost" do --limit, inaczej faza renderowania raportu # zostanie pominięta ("skipping: no hosts matched"). ansible-playbook -i patchmon_inventory.yml playbooks/playbook_fleet_report.yml --limit "Update-now,localhost" # Baza bezpieczeństwa: unattended-upgrades + wyłączenie logowania hasłem SSH # (dotyka sshd_config — uruchamiaj świadomie, ręcznie, nie z crona) ansible-playbook -i patchmon_inventory.yml playbooks/playbook_security_baseline.yml --limit "Update-now" # Blokada logowania na puste hasła ansible-playbook -i patchmon_inventory.yml playbooks/playbook_ssh.yml --limit "Update-now" # Zarządzanie kluczami SSH konta logowania (uzupełnij admin_ssh_keys w group_vars/all.yml) ansible-playbook -i patchmon_inventory.yml playbooks/playbook_ssh_keys.yml --limit "Update-now" # Pełny przebieg autopilot ręcznie (to samo co robi cron) ansible-playbook -i patchmon_inventory.yml playbooks/playbook_autopilot.yml --limit "Update-now,localhost" ``` Można też celować w węższe grupy, np. `--limit "Site-on"`, `--limit "Type-CT"`, albo pojedynczy host: `--limit "pve"`. ## Reboot — tylko raportowanie, nigdy automatycznie `playbook_fleet_report.yml` sprawdza `/var/run/reboot-required` na każdym hoście i wypisuje listę hostów wymagających restartu w raporcie (`reports/fleet_report_*.txt`). **Żaden playbook w tym projekcie nie restartuje hostów automatycznie** — decyzję i moment restartu (szczególnie dla `Type-HOST`, czyli fizycznego Proxmoksa) podejmuje operator ręcznie, po przejrzeniu raportu. ## Bezobsługowe uruchamianie (cron) `cron/run-autopilot.sh` to wrapper na `playbook_autopilot.yml`: - blokuje się przez `flock`, żeby nie nakładały się dwa równoległe przebiegi, - loguje przebieg (start/koniec/kod wyjścia) do `/var/log/ansible-autopilot.log`, - domyślnie celuje w grupę `Update-now` (nadpisywalne zmienną `TARGET_GROUP`). Instalacja: ```bash chmod +x cron/run-autopilot.sh crontab -e # wklej zawartość cron/crontab.example sudo cp cron/ansible-autopilot.logrotate /etc/logrotate.d/ansible-autopilot ``` Każdy przebieg `playbook_fleet_report.yml` (w tym z crona) dokłada nowy plik do `reports/` — warto od czasu do czasu posprzątać stare raporty, np. `find reports/ -name 'fleet_report_*.txt' -mtime +30 -delete`. ## Sekrety - **Klucz prywatny SSH** leży w `ssh_keys/id_rsa` (katalog `chmod 700`, plik `chmod 600`). Katalog `ssh_keys/` jest w `.gitignore` — klucz nigdy nie trafia do repozytorium. Ścieżka do niego jest ustawiona centralnie w `group_vars/all.yml` (`ansible_ssh_private_key_file: "{{ playbook_dir }}/../ssh_keys/id_rsa"`), więc wszystkie playbooki i `cron/run-autopilot.sh` korzystają z niej automatycznie — nie trzeba nic dodatkowo konfigurować. - **`api_key`/`api_secret`/`api_url` PatchMon** leżą w `patchmon_inventory.yml`, który jest w `.gitignore` — nigdy nie trafia do repozytorium. Wersja do repo to `patchmon_inventory.yml.example` (same placeholdery, bez sekretów). Przy zakładaniu projektu od zera: `cp patchmon_inventory.yml.example patchmon_inventory.yml` i uzupełnij `api_url`/`api_key`/`api_secret`. (Plugin `patchmon.dynamic_inventory` wczytuje ten plik bezpośrednio, bez templatowania Jinja2/Vault, dlatego to rozdzielenie na plik realny + `.example`, a nie np. `{{ lookup('env', ...) }}`, jest tu jedynym niezawodnym sposobem.)