Przejdź do treści
Automatyzacja i DevOps·~12 min czytania

Centralne logowanie zdarzeń Linux: Ansible, RELP i OpenSearch

Pokazujemy mały, świadomie ograniczony stack dla Debian/Ubuntu: bez rozbudowanej platformy SIEM, ale z transportem mTLS, wyszukiwaniem, raw spool i alertami.

Autor: Zespół LinuxLab

To nie jest opis logowania użytkowników. Chodzi o centralne zbieranie zdarzeń i logów z kilku serwerów Linux. Przykładowy układ ma jeden collector na Debianie 13 oraz klientów Debian/Ubuntu. Jest dobrym punktem startu dla małej floty, lecz nie udaje wysokiej dostępności ani pełnoprawnego SIEM.

Cel wdrożenia jest konkretny: administrator wysyła rozpoznawalny wpis z hosta web-01, znajduje go w Dashboards razem z nazwą hosta i czasem, a przy serii błędów HTTP 5xx otrzymuje sygnał bez ręcznego przeglądania każdego serwera.

Spis treści

  1. Kiedy taki układ wystarcza
  2. Przepływ jednego zdarzenia
  3. Jak ograniczyć zbierane dane
  4. Co przygotować przed wdrożeniem
  5. Wdrożenie przez Ansible
  6. Minimalny access log nginx
  7. Test end-to-end i alerty
  8. Retencja, rollback i granice rozwiązania

Kiedy mały collector jest właściwym wyborem?

Przy kilku lub kilkunastu hostach problem zwykle nie polega na braku kolejnej platformy, lecz na tym, że wpisy są rozproszone po journalctl, /var/log i panelach aplikacji. Szukanie zdarzenia wymaga wtedy kolejnych połączeń SSH, a relacja między błędem HTTP, restartem usługi i problemem z dyskiem jest trudna do odtworzenia.

SIEM (Security Information and Event Management) to platforma bezpieczeństwa, która nie tylko centralnie zbiera logi, ale też normalizuje i koreluje zdarzenia z wielu źródeł, uruchamia reguły detekcji, obsługuje alerty oraz zwykle wspiera analizę incydentów i raportowanie audytowe. Ten projekt ma tylko część tych możliwości: przechowuje i przeszukuje logi, a także ma proste alerty HTTP 5xx. Nie zastępuje pełnego SIEM z korelacją zdarzeń bezpieczeństwa, obsługą incydentów i długą retencją audytową.

W tym wariancie rsyslog pozostaje agentem na kliencie. Nie dodajemy oddzielnego kontenera ani osobnego agenta tylko po to, aby wysłać syslog. Collector przyjmuje wpisy przez RELP (Reliable Event Logging Protocol) z wzajemnym TLS, zachowuje surową kopię na dysku, a następnie indeksuje dane w OpenSearch. Dashboards działa na tym samym hoście i domyślnie słucha wyłącznie na loopbacku, więc dostęp administracyjny prowadzi przez tunel SSH.

To ma sens, gdy jedna osoba lub mały zespół świadomie utrzymuje pojedynczy collector, ma backup VM i potrafi oszacować zajętość dysku. Gdy wymagane są dwa niezależne centra danych, nieprzerwane przyjmowanie wpisów podczas awarii collectora, długi audyt regulacyjny albo korelacja z wieloma źródłami bezpieczeństwa, potrzebny jest osobny projekt architektury - a nie dokładanie kolejnych ról do tego labu.

Jak przebiega jedno zdarzenie?

aplikacja / journald / nginx
          |
          v
rsyslog na web-01 -- RELP + mTLS --> collector
                                      |-- raw spool na dysku
                                      '-- OpenSearch --> Dashboards / alerty

Przykładowo nginx zapisuje linię access logu do dedykowanego pliku JSON. Rsyslog czyta go jako plik, dołącza informacje syslogowe i wysyła do collectora. TLS weryfikuje certyfikat klienta, a Ansible wymusza zgodność systemowego hostname z inventory i nazwą certyfikatu. Pole hostname w samej wiadomości syslog nie jest osobnym podpisem kryptograficznym. API OpenSearch pozostaje lokalne, a port RELP 6514/tcp trzeba przepuścić w firewallu tylko z sieci klientów.

Na collectorze najpierw powstaje raw spool per host. To praktyczna warstwa diagnostyczna: pozwala sprawdzić, czy zdarzenie doszło do collectora, gdy problem leży dalej w pipeline lub indeksie. Nie jest to kopia poza hostem i nie zastępuje backupu. Potem ingest pipeline przypisuje pola, między innymi czas zdarzenia, nazwę hosta i typ źródła, do data streamów OpenSearch.

Synchronizacja czasu jest warunkiem sensownego wyszukiwania. Przed testem sprawdzamy, czy klient i collector mają działający NTP albo chrony. Bez tego wpis może być poprawnie wysłany, lecz pojawić się poza wybranym zakresem czasu w Dashboards.

Co centralnie zapisujemy, a czego celowo nie?

Centralizacja nie powinna być pretekstem do zbierania każdej dostępnej informacji HTTP. Opcjonalny format nginx w tym pakiecie zapisuje tylko adres bezpośredniego peera, metodę, kod odpowiedzi, ścieżkę bez query stringa, vhost i czas odpowiedzi. Nie zapisuje treści POST, query stringów, nagłówków, ciasteczek, User-Agentu ani danych upstreamu.

Adres w polu source.ip jest adresem, który nginx widzi jako $remote_addr. Za CDN albo reverse proxy nie jest automatycznie końcowym adresem użytkownika. Ustawienie real_ip wymaga osobnej, świadomej listy zaufanych proxy; ślepe zaufanie do X-Forwarded-For pozwala klientowi podać dowolny adres.

Centralizacja nginx error logów jest domyślnie wyłączona, ponieważ błędy mogą zawierać cały URL z query stringiem. To samo dotyczy logów aplikacyjnych: jeśli aplikacja sama wpisze sekret do sysloga, nie da się bezpiecznie naprawić tego po fakcie ogólnym regexem. Zasady logowania i redakcja danych muszą zaczynać się w aplikacji.

Co przygotować przed pierwszym przebiegiem?

Najpierw wybieramy jeden host collector i jednego niekrytycznego klienta testowego. W inventory kluczem hosta jest jego rzeczywisty, krótki systemowy hostname, na przykład web-01; FQDN lub adres trafia do ansible_host. Dzięki temu hostname systemu, nazwa w indeksie i nazwa certyfikatu RELP pozostają zgodne.

Na kontrolerze przygotowujemy prywatne inventory oraz vault. Hasła OpenSearch i ich hashe bcrypt nie trafiają do repozytorium ani do --extra-vars. Pushover można zostawić pusty podczas testu: lokalna bramka zwróci wtedy stan not-configured, zamiast wysyłać prawdziwe powiadomienie.

Collector w tym przykładzie jest testowany na Debianie 13 x86_64; klienci są przeznaczeni dla Debian/Ubuntu z rsyslog i systemd. Przed wdrożeniem sprawdzamy co najmniej 8 GiB RAM dla collectora, miejsce na indeksy i spool oraz regułę firewalla dla portu 6514. OpenSearch nie powinien być wystawiony publicznie na :9200.

Wdrożenie: najpierw collector, potem klienci

Referencyjny kod jest publiczny w katalogu ansible-logging-lab. Poniższe polecenia uruchamiamy na kontrolerze Ansible, po utworzeniu własnych plików inventory/local.yml oraz playbooks/vars/logging.vault.yml według dołączonych przykładów.

cd ansible-logging-lab
python3 -m venv .venv
. .venv/bin/activate
python3 -m pip install -r requirements-python.txt
ansible-galaxy collection install -r requirements.yml

ansible-playbook -i inventory/local.yml --syntax-check \
  playbooks/logging-collector.yml -e target_hosts=log_collectors --ask-vault-pass
ansible-playbook -i inventory/local.yml playbooks/logging-collector.yml \
  -e target_hosts=log_collectors --ask-vault-pass

Po pierwszym poleceniu oczekujemy wyłącznie potwierdzenia składni. Drugie tworzy rzeczywiste CA i certyfikaty oraz instaluje usługi, więc uruchamiamy je najpierw tylko dla collectora testowego. Pakiet nie deklaruje pełnej obsługi trybu --check, ponieważ część ról tworzy materiał PKI, a część uzgadnia obiekty przez API OpenSearch. Test na labie i kopia danych są uczciwszym zabezpieczeniem niż pozorny dry-run.

ansible-playbook -i inventory/local.yml --syntax-check \
  playbooks/logging-clients.yml -e target_hosts=log_clients
ansible-playbook -i inventory/local.yml playbooks/logging-clients.yml \
  -e target_hosts=log_clients

Te polecenia uruchamiamy dopiero po działającym collectorze. Klient używa kolejki dyskowej; przy niedostępnym collectorze wpisy czekają lokalnie i są wysyłane po jego powrocie. Nie jest to jednak formalna gwarancja bezstratności - wariant retryfailures dla omelasticsearch został odrzucony w testach, a raw spool służy przede wszystkim do diagnozy i odzyskania danych.

Nginx: jawne włączenie minimalnego access logu

Rola nginx instaluje definicję formatu i snippet, ale celowo nie zmienia automatycznie vhostów aplikacji. Dzięki temu wdrożenie nie zaczyna zbierać ruchu z każdego serwera przez przypadek. Po uruchomieniu dedykowanego playbooka administrator dodaje do wybranego bloku server jedną linię:

include /etc/nginx/snippets/logging-sg-json-access.conf;

Następnie wykonujemy nginx -t i kontrolowany reload. Dopiero wtedy w prywatnym inventory ustawiamy logging_client_nginx_enabled: true oraz uruchamiamy playbook klientów. Rola sprawdza aktywną konfigurację przez nginx -T; jeśli nie ma dedykowanego access logu, przerywa przebieg zamiast po cichu czytać nieistniejący plik.

Test end-to-end oraz alerty

Na kliencie testowym wysyłamy marker, którego nie ma w zwykłych logach. Polecenie poniżej nie zmienia konfiguracji; po kilku chwilach wyszukujemy pełny tekst markera w Dashboards w ostatnich 15 minutach. Powodzenie oznacza widoczny wpis z hostname web-01 i bieżącym czasem.

logger -p user.notice -t logging-lab-test \
  "event=central-logging-probe-20261004"

Jeżeli wpisu nie ma, sprawdzamy po kolei: czas hostów, status rsyslog na kliencie, lokalną kolejkę, połączenie TLS do portu 6514 oraz raw spool na collectorze. To zawęża błąd bez zgadywania, czy problem powstał w aplikacji, transporcie czy indeksie.

Pakiet zawiera progi startowe dla nginx HTTP 5xx oraz wykrywanie anomalii. Przykładowy alert ostrzega przy co najmniej 10 błędach 5xx i ponad 2% odpowiedzi w pięciu minutach; wartości trzeba dostroić do rzeczywistego ruchu. Powiadomienia mają tłumienie, aby pojedyncza awaria nie wysłała setek pushy. Bez tokenów Pushover mechanizm pozostaje w kontrolowanym trybie testowym.

Retencja, rollback i granice rozwiązania

Domyślne wartości są laboratoryjne: siedem dni retencji, rollover po dniu lub 5 GiB oraz heap OpenSearch 2 GiB. Po kilku dniach typowego ruchu mierzymy przyrost indeksów i spoolu, a dopiero potem ustalamy retencję i rozmiar dysku. Lokalny snapshot OpenSearch może pomóc po błędzie logicznym, lecz nadal jest na tym samym hoście - nie chroni przed utratą całej VM.

Przed zmianą pipeline, retencji albo heapu robimy snapshot lub backup, zapisujemy bieżące wartości i testujemy zmianę na labie. Rollback konfiguracji nginx zaczyna się od usunięcia jawnego include, nginx -t i reloadu. Ten mały pakiet nie ma automatycznego playbooka teardown dla klienta logów; ewentualne wycofanie planujemy osobno, z kopią konfiguracji i walidacją rsyslog przed restartem.

Najważniejsze ograniczenia pozostają proste: jeden collector to pojedynczy punkt awarii, nie ma CRL/OCSP ani HA, a logi mogą ujawnić dane, które aplikacja wcześniej zapisała. Ten projekt jest punktem startowym do poznania własnego wolumenu i potrzeb, nie obietnicą kompletnego bezpieczeństwa lub nieograniczonej retencji.

Checklista przed włączeniem kolejnych hostów

  1. Collector przyjmuje wpis z jednego klienta, a marker jest widoczny w Dashboards.
  2. Port 6514 jest dostępny tylko z właściwej sieci klientów, a API OpenSearch nie jest publiczne.
  3. Hostname inventory, systemowy hostname i certyfikat klienta są zgodne.
  4. Włączony nginx zapisuje tylko uzgodnione pola; query stringi, body i nagłówki nie trafiają do centralnego feedu.
  5. Sprawdzono raw spool, zajętość dysku, retencję i odtworzenie snapshotu poza produkcją.
  6. Alert 5xx został wywołany kontrolowanie albo przynajmniej zweryfikowano kanał w trybie not-configured.

Logi są rozproszone między serwerami?

Projektujemy prosty zakres centralnego logowania, ograniczamy dane w access logach i wdrażamy go etapami z testem oraz planem wycofania.