Często debugowanie kontenerów w kubernetesie wymaga przeanalizowania ruchu, jaki wchodzi lub wychodzi do systemu. Nie ma z tym problemu jeśli mamy dostęp do konta administratora u możemy uruchomić netstat lub tcpdump aby popatrzeć w pakiety. Co jeśli nie ma do tego dostępu?
Znajdź proces
Na początek zacznijmy od znalezienia procesu, który obsługuje ruch na porcie 80:
$ ps bash: ps: command not found
Będzie trudniej… przejdźmy to katalogu /proc. Część z podkatalogów odpowiada procesom. Zwykle w kontenerach oraz w Linuksie proces z PID o numerze 1 odpowiada pierwszemu procesowi w systemie/kontenerze (np. /bin/init). Mamy zatem sporo różnych wpisów w proc:
$ ls -l /proc total 0 dr-xr-xr-x 9 root root 0 Dec 19 11:59 1 dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 29 dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 30 dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 31 dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 32 dr-xr-xr-x 9 root root 0 Dec 27 08:05 33 dr-xr-xr-x 9 root root 0 Dec 27 08:06 55 dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 56 dr-xr-xr-x 9 nginx nginx 0 Dec 27 08:09 63 drwxrwxrwt 2 root root 40 Dec 19 11:59 acpi dr-xr-xr-x 13 root root 0 Dec 19 11:59 asound ...
Każdy z nich odpowiada osobnemu procesowi. Po nazwie właściciela można stwierdzić co to za proces, ale aby upewnić się, sprawdźmy wpis exe w takim katalogu:
$ ls -lh /proc/29/exe lrwxrwxrwx 1 nginx nginx 0 Dec 27 08:10 /proc/29/exe -> /usr/sbin/nginx
exe to link symboliczny do pliku wykonywalnego w systemie plików. Mamy zatem ustalonego podejrzanego oraz jego proces.
Jakie porty są otwarte przez proces
Aby sprawdzić z czego korzysta dany proces zwykle można urzyć lsof lub netstat. W dobrze zabezpieczonych systemach nie mamy takiej możliwości. Zamiast tego możemy podglądnąć listę otwartych prze nginx plików:
$ ls -lh /proc/29/fd/ total 0 lrwx------ 1 nginx nginx 64 Dec 27 08:13 0 -> /dev/null l-wx------ 1 nginx nginx 64 Dec 27 08:13 1 -> 'pipe:[37844]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 10 -> 'anon_inode:[eventfd]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 11 -> 'anon_inode:[eventfd]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 12 -> 'socket:[40105]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 13 -> 'socket:[40107]' l-wx------ 1 nginx nginx 64 Dec 27 08:13 2 -> 'pipe:[37845]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 3 -> 'socket:[40103]' l-wx------ 1 nginx nginx 64 Dec 27 08:13 4 -> 'pipe:[37845]' l-wx------ 1 nginx nginx 64 Dec 27 08:13 5 -> 'pipe:[37844]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 6 -> 'socket:[40099]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 7 -> 'socket:[40100]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 8 -> 'socket:[40102]' lrwx------ 1 nginx nginx 64 Dec 27 08:13 9 -> 'anon_inode:[eventpoll]'
Pliki z numerami (dokładniej deskryptorami, możliwe, że kojarzysz to z funkcji open w C) 0 i 1 to standardowe wejście i wyjście. Za ich pomocą program komunikuje się z użytkownikiem w konsoli. Tutaj jest wejście jest ustawione na /dev/null a wyjście przekierowane do pipe. To, co nas interesuje, to pliki opisane jako socket:[numer]. Są to połączenia sieciowe. Nazwa symlinka to deskryptor pliku/socketu widziany po stronie aplikacji. Numer widoczny po socket:… to identyfikator obiektu po stronie jądra systemu, tzw. inode.
Jak sprawdzić który inode odpowiada socketowi, nasłuchującemu na porcie 80? Popatrzmy w plik /proc/net/tcp:
$ cat /proc/net/tcp sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode 0: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 40099 1 0000000000000000 100 0 0 10 0
Pokazuje on otwarte połączenia sieciowe. Ostatnia kolumna to inode połączenia (socketu). Jeśli poszukasz jej w /proc/29/fd, to znajdziesz podobny wpis, dla deskryptora pliku nr 6. Teraz wystarczy popatrzeć na local_address i zamienić numer portu 0050 na wartość dziesiętną i wyjdzie port 80, na którym słucha nasza aplikacja.
Wszystko to używając jedynie ls oraz cat z konta zwykłego użytkownika, które zwykle nie są usuwane podczas zabezpieczania kontenerów