Список открытых портов — это и есть список способов до сервера добраться. Всё остальное — фаерволы, WAF, IDS — надстройки над ним. Поэтому проверка «что у меня слушает» стоит первой в любом аудите, и она же чаще всего даёт неприятный результат: половина найденного открыта не вами, а установщиком какого-нибудь пакета.

Читаем вывод

sudo ss -tulpn

Ключи: t — TCP, u — UDP, l — только слушающие, p — процесс, n — не преобразовывать номера в имена. Без sudo колонка с процессом будет пустой, и смысл теряется.

Смотреть надо на колонку Local Address:Port, и разница там принципиальная:

  • 127.0.0.1:3306 — служба доступна только с самой машины. Это хорошо;
  • 0.0.0.0:3306 — со всех адресов IPv4, то есть из интернета. Это то, что нужно проверять;
  • [::]:3306 — то же самое для IPv6. Отдельная строка, и её регулярно не замечают;
  • 203.0.113.25:443 — на конкретном адресе, обычно осмысленно.

Дальше по каждой строке с 0.0.0.0 или [::] задаём один вопрос: должен ли посторонний человек иметь возможность сюда обратиться. Для 80 и 443 ответ «да». Почти для всего остального — «нет».

Обычные находки

Redis, порт 6379. Самая опасная строка из возможных. По умолчанию Redis не требует пароля, а его команды позволяют записать файл на диск — то есть чужой ключ в authorized_keys. Между появлением Redis на публичном адресе и его использованием проходят часы, иногда меньше. Проверьте bind 127.0.0.1 и protected-mode yes в конфиге.

Memcached, 11211/UDP. Даже если внутри ничего ценного, ваш сервер станет усилителем чужих атак — и претензии придут от хостера.

MySQL и PostgreSQL, 3306 и 5432. Пароль есть, но перебор по нему идёт непрерывно, а версии СУБД обновляются реже, чем хотелось бы. Наружу они не нужны почти никогда: приложение живёт на той же машине, а вам для работы хватит SSH-туннеля.

Elasticsearch 9200, MongoDB 27017. Исторически без аутентификации по умолчанию. Публичные базы этих служб — постоянный источник новостей об утечках.

Docker API, 2375. Открытый порт управления Docker — это root на хосте без каких-либо паролей. Обычно появляется после экспериментов с удалённым доступом к докеру.

Панель управления и phpMyAdmin на своих портах: 8080, 8083, 10000. Не то чтобы их нельзя открывать, но именно они собирают на себя основной поток попыток.

Чинить на уровне службы, а не фаервола

Соблазн закрыть всё найденное правилом UFW понятен, но это второй рубеж, а не первый. Правило можно удалить по ошибке, фаервол — временно выключить при отладке, а Docker и вовсе публикует порты в обход UFW. Настройка привязки в конфиге службы переживает всё это:

  • MySQL/MariaDB — bind-address = 127.0.0.1;
  • PostgreSQL — listen_addresses = 'localhost';
  • Redis — bind 127.0.0.1 ::1;
  • Docker Compose — публикация как "127.0.0.1:5432:5432".

Фаервол ставится сверху как страховка, а не вместо этого.

Найти процесс, если непонятно

ss показывает имя и pid. Дальше:

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

Первая команда назовёт юнит systemd, которому принадлежит процесс, — обычно этого достаточно, чтобы понять, что это и нужно ли оно. Незнакомый процесс, слушающий высокий порт и запущенный не из системного каталога — например, из /tmp или /dev/shm, — это уже не вопрос настройки, а повод для отдельного разбирательства.

Проверка снаружи обязательна

ss отвечает на вопрос «что слушает», но не на вопрос «до чего можно достучаться». Между ними стоят фаервол, NAT и правила хостера. Единственный честный ответ даёт сканирование с другой машины:

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

Второй командой не пренебрегайте: адрес IPv6 есть почти у каждого VPS, правила для него пишутся отдельно, а служба слушает обе версии протокола сразу.

Смотреть надо не разово

Список открытых портов меняется сам. Установили пакет — он привёл службу и открыл порт. Обновили панель — она вернула настройку по умолчанию. Запустили контейнер — он опубликовал порт мимо фаервола. Разовая проверка отвечает за сегодняшний день и ни за что больше.

Ценность здесь не в самом списке, а в его изменениях: новый порт, которого вчера не было, — короткий и очень информативный сигнал. Именно так это и стоит смотреть — снимком с историей, а не разбором вывода ss по памяти. Страница демо ниже показывает ровно это.