Короткий ответ
Картина портов — три слоя: что слушает процесс (ss -tulnp), что пропускает хостовый firewall, что видно снаружи (nmap с hop в той же зоне, что атакующий). Сверьте с матрицей «порт / bind / source CIDR / зачем». Расхождение ss vs nmap — нормальный сигнал (bind localhost, UFW, SG облака, DNAT). Не сканируйте только 127.0.0.1. Не отключайте UFW «чтобы nmap совпал». Хост host.example (10.0.20.10), пользователь admin.
Симптомы и как отличить
Это процедура, не инцидент. Признаки, что её нет:
- в wiki «открыто 22 и 443», на деле
:9100и:2375; - скан с самого сервера
nmap localhost; - Docker порты не в матрице.
Если уже ясно, что лишнее торчит в WAN — закрытие: лишние порты. Если фильтр выключен: firewall.
Возможные причины расхождений
- Слушает
127.0.0.1, внешний nmap closed/filtered. - Слушает
0.0.0.0, UFW drop → filtered с WAN, open с LAN. - docker-proxy vs процесс в namespace.
- IPv4 закрыт,
:::443открыт. - Security group режет до гостя.
- Hairpin/NAT, скан «публичного IP» с LAN идёт другим путём.
Диагностика
Работайте от admin с sudo. Сохраняйте вывод в каталог заявки с датой.
1. Слушатели на хосте
sudo ss -tulnp
sudo ss -tulnp | awk 'NR==1 || /LISTEN/'
ip -br addrКолонка Local Address:Port — bind. *:22 / 0.0.0.0:22 / [::]:22 / 127.0.0.1:22 различайте.
Процессы:
sudo lsof -iTCP -sTCP:LISTEN -P -n2. Firewall
sudo ufw status verbose
sudo nft list ruleset | grep -E 'dport|policy'Не ufw disable.
3. Docker/kube
docker ps --format '{{.Names}}\t{{.Ports}}' 2>/dev/null
sudo ss -tlnp | grep docker-proxyСокет API: docker.sock.
4. Скан с hop
С машины в admin-сети 10.0.20.0/24:
nmap -Pn -sT -p 22,80,443,3306,5432,6379,8080,9100,2375 10.0.20.10С hop «как интернет» (другая сеть/NAT), тот же список. Не сканируйте чужие диапазоны и не устраивайте -p- на проде в рабочий день без окна (нагрузка, IDS). Для полного списка сначала ss, потом точечный -p по факту слушателей.
IPv6:
nmap -6 -Pn -p 22,80,443 2001:db8::10Подставьте реальный адрес хоста, не пример db8 если его нет.
UDP (DNS, NTP, VPN) — отдельно -sU, медленнее, с пониманием.
Решение (как собрать матрицу)
Таблица в заявке, колонки:
| Порт | Протокол | Bind | Процесс | Кто клиент (CIDR) | FW правило | Нужно |
|---|---|---|---|---|---|---|
| 22 | tcp | 0.0.0.0 | sshd | 10.0.20.0/24 | ufw allow from … | да |
| 3306 | tcp | 0.0.0.0 | mysqld | WAN??? | Anywhere | нет → чинить |
Для каждой строки «нет» — задача на bind или UFW (соседние статьи), не «документировать как фичу».
SSH-ожидание: sshd. Итог hardening: baseline.
Повторяемость:
mkdir -p /home/admin/port-audit
sudo ss -tulnp > /home/admin/port-audit/ss-$(date +%F).txtDiff со вчера — регрессия.
Как читать результаты nmap без самообмана. open на 22 с WAN при политике «только VPN» — дефект SG или UFW Anywhere. filtered на 3306 при ss 0.0.0.0:3306 — фильтр работает, bind всё ещё опасен при ошибке правила: оставьте в матрице статус «слушает все, FW режет» и запланируйте bind. closed на 80 — ничего не слушает, RST дошёл: либо нет сервиса, либо bind только localhost (тогда с WAN closed, с localhost open — два скана).
Порядок съёмки в инциденте «нас сканируют»: не включайте ufw disable, чтобы «увидеть настоящие порты». Настоящие порты даёт ss. Nmap с WAN показывает экспозицию. Оба файла в заявку.
UDP: ss -ulnp. DNS 53, NTP 123, WireGuard 51820. Nmap -sU без -p списка на проде не делайте в рабочее время. Для WG внешний open/filtered зависит от firewall; сам ss должен показать 0.0.0.0:51820 только если это VPN-шлюз, не app-сервер.
IPv4 vs IPv6 матрица — две строки или явная пометка dual-stack. Часто AAAA забывают в UFW при IPV6=yes.
Автоматизация: скрипт раз в неделю с hop (runner в другой сети) пишет nmap XML, diff с эталоном. Эталон = матрица, не «вчерашний скан» (дрейф узаконивает дыру).
Не путайте listening и established: ss -tp куча 443 ESTAB не значит, что вы слушаете 443 как сервер — это исходящие. Для матрицы только LISTEN.
Повторите скан после ufw reload и после деплоя: conntrack может кратко показывать иное. Для отчёта фиксируйте UTC-время и hop-IP, с которого снимали nmap.
Как проверить, что проблема устранена
Процедура успешна, если:
- Файл
ssи два nmap (LAN/WAN) лежат в заявке. - Матрица покрывает все LISTEN кроме localhost-only, помеченных явно.
- WAN-open ⊆ разрешённые в политике.
- Firewall active.
- Повтор через неделю даёт diff=0 или тикеты на дельту.
Это не «закрыть всё», а проверяемый инвентарь. Закрытие — отдельные работы.
Если не помогло
- nmap с сервера ≠ с hop: используйте hop.
- Cloud: снимите SG отдельно, приложите к матрице.
ssпустой для порта, nmap open: DNAT на другом узле.- conntrack/timeout: повтор с
-Pn. - AppArmor не скрывает listen.
Профилактика
- Матрица в git без секретов.
- CI после деплоя: сравнить
ssс allow-list. - Алерт на новый LISTEN.
- Запрет
docker -pбез bind-адреса в шаблоне.
FAQ
Достаточно ли netstat?
ss штатный. netstat из net-tools может отсутствовать. Не ставьте пакет ради привычки, если ss есть.
nmap с Windows до Ubuntu
Нормально. Те же open/filtered. Не забудьте IPv6.
Сканировать UDP 1-65535
Дорого и шумно. Только по матрице и ss -ulnp.
localhost сервисы в матрице?
Да, пометка bind 127.0.0.1, клиент «только локально/ssh -L». Иначе забудете и опубликуете.
Можно ли masscan
Для периметра с окном — да, с rate limit. Для одного 10.0.20.10 хватит nmap -p по списку.