Incydent: osierocony kontener mongo-drill z kopią produkcyjnej w3_db dostępny z internetu bez uwierzytelniania — 2026-10-01 → 2026-10-08

PoleWartość
IDINCIDENT-2026-10-08-MONGO-DRILL-EXPOSURE
SevernośćP1 Security — ekspozycja kopii danych produkcyjnych (nieuwierzytelniony MongoDB, port publiczny)
StatusZAMKNIĘTY (containment i usunięcie wykonane 2026-10-08); działania naprawcze w toku (#7609)
Okno ekspozycji2026-10-01 03:08 UTC → 2026-10-08 ok. 18:35 UTC (ok. 7 dni 15,5 h)
Wykryto2026-10-08, ok. 18:25–18:35 UTC, przy analizie alertu HostSwapNearlyExhausted (#7529)
Systembms-4 (OVH ns3101999, 54.36.123.110)
Decyzja o zgłoszeniuNie zgłaszamy — decyzja właściciela z 2026-10-08 (patrz „Decyzja”)
Issue#7609; wcześniejsza ta sama klasa: #6354

Notatka przygotowana dla właściciela i na potrzeby ewentualnego audytu/DPO. Zawiera fakty z logów i decyzje; wnioski prawne (czy zdarzenie jest naruszeniem ochrony danych podlegającym zgłoszeniu) są decyzją właściciela, nie autora notatki.


Streszczenie

Kwartalny test odtwarzania backupu MongoDB (p24-mongodb-restore-drill.service) uruchomił się 2026-10-01 03:05 UTC na bms-4. Skrypt scripts/mongodb-restore-drill.sh utworzył kontener mongo-drill-1439728 (mongo:7.0) z mongod --bind_ip_all, bez --auth, z opublikowanym portem 0.0.0.0:27019->27017, i przywrócił do niego kopię produkcyjnej bazy w3_db. O 04:05:49 UTC systemd zabił jednostkę po przekroczeniu TimeoutStartSec (odtwarzanie w3_db trwało 3397 s, w4_db jeszcze nie ruszyło). Pułapka EXIT skryptu nie zadziałała przy SIGTERM, więc kontener został osierocony i działał 7 dni, dostępny z internetu. Porty publikowane przez Dockera omijają reguły ufw (łańcuch iptables DOCKER), więc zapora hosta go nie chroniła.

Zakres danych

  • Kontener zawierał przywróconą kopię w3_db z backupu w3_db-2026-06-15.mongodump.gz (20 461 721 dokumentów). Nazwy kolekcji widoczne w logu odtwarzania sugerują dane osobowe klientów (m.in. users, businessaddressbooks, office-phone-numbers, office-billings). Zawartość nie była sprawdzana — to ocena z nazw kolekcji.
  • w4_db nie została odtworzona w kontenerze (przerwanie przez timeout), więc nie była w nim obecna.
  • Dodatkowo w /tmp bms-4 leżały dwa archiwa z uprawnieniami 0644 (czytelne dla wszystkich lokalnych użytkowników hosta): w3_db-2026-06-15.mongodump.gz (7,68 GB) i w4_db-2026-06-15.mongodump.gz (2,93 GB). To ekspozycja lokalna (konta na hoście, m.in. claude-runner, claude-runner-2), nie sieciowa. Oba pliki usunięto.

Oś czasu (UTC)

CzasZdarzenie
2026-10-01 03:05Start kwartalnego drilla (timer p24-mongodb-restore-drill.timer).
2026-10-01 03:07:45 / 03:08:12Utworzenie i start kontenera mongo-drill-1439728, port 27019 opublikowany na 0.0.0.0.
2026-10-01 04:04:56Zakończone odtwarzanie w3_db (0 błędów); start w4_db.
2026-10-01 04:05:49systemd zabija jednostkę (start operation timed out, Result=timeout). Kontener zostaje.
2026-10-08 ok. 18:25–18:35Analiza alertu swap na bms-4 wykazuje kontener 7 dni po starcie; test TCP z zewnątrz do 54.36.123.110:27019 kończy się sukcesem; lokalnie listDatabases bez logowania zwraca admin, config, local, w3_db (tylko nazwy baz).
2026-10-08 ok. 18:35Kontener zatrzymany przez właściciela (docker stop, dane zachowane). Wg logu workera okno ekspozycji kończy się 18:35; komentarz na #7609 podaje przybliżone „~18:50”. Port 27019 zamknięty — potwierdzone testem z zewnątrz i ss.
2026-10-08 19:54Worker infra-task (#7609): przegląd logu połączeń (tylko adresy IP), docker rm -v, usunięcie /tmp/mongo-drill-1439728 wraz z dwoma archiwami, wpis audytowy w infra_operations.

Dowody z logu połączeń

Źródło: docker logs mongo-drill-1439728 | grep -i 'connection accepted' wykonane przed usunięciem kontenera; analizowano wyłącznie adresy IP (bez odczytu treści).

  • 42 zaakceptowane połączenia: 16 lokalnych (127.0.0.1, kontrole integralności drilla) oraz 26 z 20 różnych publicznych adresów IP.
  • Adresy zewnętrzne (liczba połączeń × IP):
    • po 2: 9.234.18.247, 20.38.35.135, 185.108.70.109, 159.223.126.161, 146.190.66.99, 142.93.11.107
    • po 1: 87.236.176.246, 87.236.176.199, 67.205.130.212, 40.76.136.72, 20.38.35.88, 20.168.121.140, 20.163.11.221, 20.106.172.110, 20.102.99.117, 20.102.90.23, 172.172.161.215, 137.184.64.184, 134.33.97.72, 134.122.117.196
  • Atrybucja zakresów (wg workera, nie weryfikowana whois): 20.x, 40.x, 172.172.x — Azure; 142.93, 146.190, 159.223, 137.184, 134.122, 67.205 — DigitalOcean. Niesklasyfikowane: 9.234.18.247, 134.33.97.72, 185.108.70.109, 87.236.176.199, 87.236.176.246. Profil (liczne zakresy chmurowe, pojedyncze połączenia) jest typowy dla internetowych skanerów MongoDB.

Ograniczenia dowodów

  • mongod przy domyślnym poziomie logowania nie zapisuje zapytań ani komend; do logu trafiają połączenia i operacje wolniejsze niż 100 ms (Slow query). Brak śladów odczytu w logu nie jest dowodem, że odczytu nie było — szybkie listDatabases/find nie zostawiają wpisu.
  • Instancja nie wymagała uwierzytelniania, więc każde z 26 połączeń zewnętrznych mogło wykonać dowolne polecenie.
  • Kontener i jego logi zostały usunięte (docker rm -v); analiza nie może być powtórzona. Zachowane są tylko liczby i lista IP z komentarza na #7609 oraz ta notatka.

Decyzja

Decyzja właściciela (2026-10-08): incydentu nie zgłaszamy (nie zgłaszamy go do organu nadzorczego ani do osób, których dane dotyczą).

Uzasadnienie podane przez właściciela: w logach MongoDB nie ma śladów wycieku (nie stwierdzono odczytu danych). Uzupełnienie właściciela (2026-10-08, wieczorem): zapytania do baz są logowane w serwisie do logowania, a dane są tam przechowywane przez dwa dni; na tej podstawie właściciel potwierdza, że wycieku danych nie było.

Zastrzeżenie oceniającego: patrz „Ograniczenia dowodów”. Brak wpisów w logu jest zgodny z samym skanowaniem, ale nie wyklucza odczytu. Dwa fakty ograniczają też uzupełnienie właściciela: (1) retencja serwisu logowania (2 dni) obejmuje co najwyżej ostatnie 2 z 7 dni ekspozycji (2026-10-01 → 2026-10-08), więc pierwsze ok. 5 dni (do ok. 2026-10-06) nie mają w nim żadnego pokrycia, niezależnie od zawartości; (2) kontener drilla uruchomiono bez opcji logowania komend (mongod --bind_ip_all, domyślny poziom logowania, bez profilera), więc jego mongod sam z siebie zapisuje połączenia i operacje wolniejsze niż 100 ms, a nie wszystkie zapytania; jednorazowa próba odpytania Mezmo (2026-10-08 wieczorem) nie zwróciła żadnych linii dla frazy mongo-drill, a kolejne zapytanie zostało odrzucone limitem (HTTP 429), więc nie udało się potwierdzić, że logi tego kontenera w ogóle trafiały do serwisu. Wniosek oceniającego: brak dowodów odczytu, ale potwierdzenie „nie było wycieku” nie wynika z dostępnych logów dla całego okna ekspozycji. Decyzja o niezgłaszaniu opiera się na ocenie właściciela, a odpowiedzialność za nią spoczywa na nim. Ta notatka jest zapisem podstawy decyzji na wypadek późniejszej weryfikacji. Gdyby pojawiły się nowe informacje (np. ślady użycia danych z kopii), decyzję należy ponownie ocenić; punktem odniesienia dla ewentualnego terminu 72 h jest moment stwierdzenia ekspozycji, tj. 2026-10-08 ok. 18:25–18:35 UTC.

Przyczyny

  1. Bezpośrednia: kontener drilla uruchomiony z --bind_ip_all, bez --auth i z portem opublikowanym na 0.0.0.0 (domyślne zachowanie Dockera omija ufw).
  2. Utrwalenie: TimeoutStartSec jednostki krótszy niż rzeczywisty czas drilla (~57 min tylko dla w3_db) oraz brak sprzątania przy SIGTERM (pułapka tylko na EXIT).
  3. Brak wykrycia: nie było przeglądu osieroconych kontenerów mongo-drill-*; wcześniejsza naprawa #6354 usunęła skutek, nie zabezpieczyła przed powtórzeniem. Alert HostSwapNearlyExhausted zwrócił uwagę dopiero pośrednio.
  4. Dodatkowo: archiwa dumpów w /tmp z uprawnieniami 0644.

Działania

Wykonane (2026-10-08): zatrzymanie i usunięcie kontenera, zamknięcie portu 27019, usunięcie dwóch archiwów z /tmp, wpis audytowy, przegląd logu połączeń (IP).

W toku (#7609, kod): publikacja drilla wyłącznie na 127.0.0.1 lub brak publikacji portu, --auth, etykieta p24.drill=1, dłuższy TimeoutStartSec, sprzątanie przy SIGTERM (ExecStopPost= lub trap TERM/INT/EXIT), prywatny katalog 0700 na archiwa, codzienny sweep osieroconych kontenerów z alertem (Discord + GH issue), testy. Po zmergowaniu: ponowne wykonanie drilla Q4 2026 (kwartalna weryfikacja DR nie została dotąd ukończona — w4_db nie była odtworzona) i zapis wyniku.

Obserwacja poboczna (osobny temat): skrypty w /opt/p24-infra/scripts/ należą do claude-runner z zapisem dla grupy, a część z nich jest uruchamiana jako root przez timery/crony. Do oceny osobno.