Список відкритих портів — це список способів дістатися до вашого сервера. Усе інше — брандмауери, WAF, виявлення вторгнень — будується поверх нього. Тому «що тут слухає» стоїть першим у будь-якому аудиті, і тому ж це перевірка, яка найчастіше дає неприємну відповідь: половину з того, що спливає, відкрили не ви, а інсталятор якогось пакета.
Читаємо вивід
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. Історично типово без автентифікації. Їхні публічні екземпляри — постійне джерело новин про витоки даних.
API Docker, 2375. Відкритий порт керування Docker — це root на хості без жодного пароля. З’являється зазвичай після експериментів із віддаленим доступом до Docker.
Панелі керування та 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
Не пропускайте другу команду: майже в кожного VPS є адреса IPv6, її правила пишуться окремо, а служба слухає на обох версіях протоколу одночасно.
Це не разова перевірка
Список відкритих портів змінюється сам собою. Поставили пакет — він приніс службу й відкрив порт. Оновили панель — вона повернула типове значення. Запустили контейнер — він опублікував порт в обхід брандмауера. Разова перевірка відповідає за сьогодні й не більше.
Цінність не в самому списку, а в його змінах: новий порт, якого вчора не було, — короткий і надзвичайно інформативний сигнал. Саме так за ним і варто стежити — як за знімком з історією, а не як за виводом ss, згаданим з пам’яті. Демосторінка нижче показує саме це.