Lista otwartych portów to lista dróg do naszego serwera. Wszystko inne — zapory, WAF, wykrywanie włamań — jest nadbudową nad nią. Dlatego sprawdzenie „co tu nasłuchuje” stoi w każdym audycie na pierwszym miejscu i dlatego właśnie ono najczęściej daje nieprzyjemny wynik: połowę znalezionego otworzyliśmy nie my, lecz instalator jakiegoś pakietu.

Czytamy wynik

sudo ss -tulpn

Przełączniki: t — TCP, u — UDP, l — tylko nasłuchujące, p — proces, n — bez zamiany numerów na nazwy. Bez sudo kolumna z procesem będzie pusta i całe ćwiczenie traci sens.

Patrzeć trzeba na kolumnę Local Address:Port, a różnica jest tam zasadnicza:

  • 127.0.0.1:3306 — usługa dostępna tylko z samej maszyny. To dobrze;
  • 0.0.0.0:3306 — ze wszystkich adresów IPv4, czyli z internetu. To trzeba sprawdzić;
  • [::]:3306 — to samo dla IPv6. Osobny wiersz, który regularnie umyka;
  • 203.0.113.25:443 — na konkretnym adresie, zwykle świadomie.

Dalej przy każdym wierszu z 0.0.0.0 albo [::] zadajemy jedno pytanie: czy osoba postronna powinna móc się tu dostać? Dla 80 i 443 odpowiedź brzmi tak. Dla niemal wszystkiego innego — nie.

Zwykłe znaleziska

Redis, port 6379. Najgroźniejszy z możliwych wierszy. Domyślnie Redis nie wymaga hasła, a jego polecenia pozwalają zapisać plik na dysku — czyli obcy klucz w authorized_keys. Między pojawieniem się Redisa na adresie publicznym a jego wykorzystaniem mijają godziny, czasem mniej. Sprawdźmy bind 127.0.0.1 i protected-mode yes w konfiguracji.

Memcached, 11211/UDP. Nawet jeśli w środku nie ma nic cennego, nasz serwer stanie się wzmacniaczem cudzych ataków — a pretensje przyjdą od hostingodawcy.

MySQL i PostgreSQL, 3306 i 5432. Hasło jest, ale zgadywanie go trwa bez przerwy, a wersje baz danych aktualizuje się rzadziej, niż by wypadało. Na zewnątrz nie są potrzebne prawie nigdy: aplikacja stoi na tej samej maszynie, a do pracy wystarczy tunel SSH.

Elasticsearch 9200, MongoDB 27017. Historycznie bez domyślnego uwierzytelniania. Publiczne instancje tych usług to stałe źródło doniesień o wyciekach.

API Dockera, 2375. Otwarty port sterowania Dockerem to root na maszynie bez żadnego hasła. Pojawia się zwykle po eksperymentach ze zdalnym dostępem do Dockera.

Panele zarządzania i phpMyAdmin na swoich portach: 8080, 8083, 10000. Nie chodzi o to, że nigdy nie wolno ich otwierać, ale to właśnie one ściągają na siebie główny strumień prób.

Naprawiać na poziomie usługi, nie zapory

Pokusa zamknięcia każdego znaleziska regułą UFW jest zrozumiała, ale to druga linia, nie pierwsza. Regułę można przez pomyłkę usunąć, zaporę na chwilę wyłączyć przy debugowaniu, a Docker publikuje porty całkowicie z pominięciem UFW. Ustawienie przypisania w konfiguracji usługi przetrwa to wszystko:

  • MySQL/MariaDB — bind-address = 127.0.0.1;
  • PostgreSQL — listen_addresses = 'localhost';
  • Redis — bind 127.0.0.1 ::1;
  • Docker Compose — publikowanie jako "127.0.0.1:5432:5432".

Zapora dokłada się na wierzch jako zabezpieczenie, a nie zamiast tego.

Znaleźć proces, gdy nie wiadomo, co to

ss pokazuje nazwę i pid. Dalej:

sudo systemctl status <pid>
sudo lsof -i :8080

Pierwsze polecenie nazwie jednostkę systemd, do której należy proces — zwykle to wystarczy, żeby zrozumieć, co to jest i czy jest potrzebne. Nieznany proces nasłuchujący na wysokim porcie i uruchomiony spoza katalogów systemowych — na przykład z /tmp albo /dev/shm — to już nie kwestia konfiguracji, lecz powód do osobnego dochodzenia.

Sprawdzenie z zewnątrz jest obowiązkowe

ss odpowiada na pytanie „co nasłuchuje”, a nie „do czego da się dobić”. Między jednym a drugim stoją zapora, NAT i reguły hostingodawcy. Jedyną uczciwą odpowiedź daje skanowanie z innej maszyny:

nmap -Pn -p- 203.0.113.25
nmap -Pn -p- -6 2001:db8::1

Drugiego polecenia nie należy pomijać: adres IPv6 ma niemal każdy VPS, reguły dla niego pisze się osobno, a usługa nasłuchuje na obu wersjach protokołu naraz.

To nie jest sprawdzenie jednorazowe

Lista otwartych portów zmienia się sama. Zainstalowaliśmy pakiet — przyniósł usługę i otworzył port. Zaktualizowaliśmy panel — przywrócił ustawienie domyślne. Uruchomiliśmy kontener — opublikował port z pominięciem zapory. Jednorazowe sprawdzenie odpowiada za dzisiejszy dzień i za nic więcej.

Wartość tkwi nie w samej liście, lecz w jej zmianach: nowy port, którego wczoraj nie było, to krótki i bardzo wymowny sygnał. Właśnie tak warto na nią patrzeć — jako na migawkę z historią, a nie jako na wynik ss odtwarzany z pamięci. Strona demonstracyjna poniżej pokazuje dokładnie to.