MAATRIX / Блог / Порт, о котором вы не знали: инвентаризация того, что торчит наружу

Порт, о котором вы не знали: инвентаризация того, что торчит наружу

MAATRIX

Полгода назад вы поднимали Redis для теста, месяц назад коллега временно открыл 8080 для отладки нового API, а перед этим кто-то разрешил RDP «на пару дней», чтобы быстро что-то проверить с Windows-машины. Сегодня всё это всё ещё торчит наружу — просто никто уже не помнит зачем. Единственный способ узнать правду о своём сервере — не читать конфиги firewall, а посмотреть на него глазами атакующего, снаружи.

Почему на сервере со временем появляются лишние порты

Firewall почти никогда не ломается резко — он протекает медленно. Причины всегда одни и те же, и они не про злой умысел, а про обычную рабочую рутину:

  • Тестовое ПО поднимают «на минутку» — python3 -m http.server 8000, локальный Redis без пароля для отладки кэша, Elasticsearch на 9200 для проверки поиска — и забывают остановить.
  • Временное правило firewall открывают ради одной задачи («дай доступ, я быстро гляну логи через RDP») и не откатывают, потому что откат не попал ни в один тикет.
  • Сервис для отладки — dev-сервер фронтенда на 3000, admin-панель на нестандартном порту, WireGuard management API — остаётся жить после того, как задача закрыта.
  • Docker-контейнеры публикуют порты через -p 0.0.0.0:PORT:PORT, и это правило живёт, пока жив контейнер, а контейнер часто переживает саму задачу на годы.
  • Смена ответственного: администратор, который открывал порт и держал это в голове, уволился или сменил проект, а запись нигде, кроме его памяти, не существовала.

По отдельности каждый такой случай выглядит безобидно. Проблема в сумме: через год-два на сервере оказывается десяток портов, про которые никто уверенно не скажет, зачем они и можно ли их закрыть. Один из них однажды находит не администратор, а сканер злоумышленника — и именно так чаще всего выглядит реальный взлом через забытый порт: не через уязвимость нулевого дня, а через сервис, который вообще не должен был быть виден снаружи.

Почему аудит нужно делать снаружи, а не изнутри

Первый инстинкт — зайти на сервер и посмотреть ss -tulnp или netstat -tulnp. Это полезная команда, но она отвечает на другой вопрос: что слушает сокеты на этой машине. Она не отвечает на вопрос: что из этого реально достижимо из интернета. Разница между этими двумя картинами и есть источник почти всех сюрпризов.

Локальный взгляд искажается несколькими вещами одновременно:

  • Docker переписывает iptables мимо ufw. Docker создаёт собственные цепочки (DOCKER, DOCKER-USER) и вставляет правила ACCEPT/MASQUERADE, которые обрабатываются раньше пользовательских правил ufw в таблице filter. Результат: ufw status показывает default deny incoming, а опубликованный контейнером порт при этом свободно достижим снаружи — ufw его просто не видит в своей логике.
  • Два уровня firewall не совпадают. На VPS обычно есть firewall облачного провайдера (security group / панель управления) и firewall внутри ОС (ufw, nftables, firewalld). Каждый из них по отдельности может выглядеть строгим, а фактическая доступность порта — это пересечение обоих правил, которое ни один из уровней не показывает целиком.
  • IPv6 живёт своей жизнью. Правило написано для iptables, а ip6tables не тронут — сервис оказывается закрыт по IPv4 и открыт по IPv6, и локальная проверка ss этого различия не подсвечивает, если вы не смотрите отдельно на оба стека.
  • Порядок правил решает всё. В nftables и iptables более раннее правило ACCEPT перекрывает более позднее DROP. Прочитать правила глазами и понять итоговое поведение для конкретного порта — задача, в которой легко ошибиться; сервер этого не подскажет, он просто примет или отклонит пакет.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Как просканировать себя полностью: инструменты и команды

Базовый инструмент — nmap. Ключевая ошибка большинства проверок «на глаз» — сканировать только top-1000 портов по умолчанию. Забытые тестовые сервисы почти никогда не сидят на портах из этого списка — они на 8000, 8080, 9200, 6379, 27017 и подобных, и часть из них в top-1000 не входит.

Полное сканирование TCP по всем 65535 портам с определением сервисов:

# запускать с внешней машины, не с самого сервера и не из той же сети
nmap -Pn -sS -sV -p- --min-rate 2000 --max-retries 2 203.0.113.10
  • -Pn — не полагаться на ICMP ping, многие провайдеры режут его отдельно от TCP/UDP;
  • -sS — SYN-скан, быстрее и менее шумный, чем полное TCP-соединение;
  • -sV — определение версии сервиса на открытом порту (полезно, чтобы сразу понять, Redis это, Elasticsearch или что-то ваше);
  • -p- — все 65535 портов, а не только «популярные».

UDP-порты нужно проверять отдельно — забытый WireGuard management interface, DNS-резолвер, открытый для всего интернета, или SNMP с дефолтным community string обычно находят именно так:

nmap -Pn -sU --top-ports 200 203.0.113.10

Полный перебор UDP по всем портам медленный (UDP-скан по протоколу требует таймаутов на каждый порт), поэтому для UDP обычно достаточно расширенного списка распространённых портов, а не буквально всех 65535 — если только вы не подозреваете конкретный нестандартный порт.

Для серверов с большим количеством IP или для более быстрого прохода по диапазону удобен masscan, который сканирует значительно быстрее nmap за счёт собственного стека пакетов:

masscan -p0-65535 --rate 1000 203.0.113.10 -e eth0

Флаг --rate стоит держать умеренным: слишком агрессивный скан может быть воспринят провайдером или самим сервером как DDoS, а часть хостинг-панелей банит IP источника скана по своим правилам. Сканируйте только серверы, которыми управляете сами — сканирование чужой инфраструктуры без разрешения незаконно в большинстве юрисдикций, даже если это «просто nmap».

Итог этого шага — сырой список: порт, протокол, сервис по баннеру, версия, если определилась. Это не результат аудита, а входные данные для следующего шага — сверки.

Сверка находок с ожидаемым списком

Список открытых портов сам по себе ничего не говорит — «плохо» или «нормально» решает не сканер, а сравнение с тем, что должно быть открыто. Практический формат — таблица с явным статусом по каждой находке:

Порт/протоколЧто нашлиДолжно быть открыто?Что делать
22/tcpOpenSSH 9.xДа, ожидаемоОставить, проверить, что вход только по ключу
443/tcpnginxДа, ожидаемоОставить
51820/udpWireGuardДа, ожидаемоОставить, сверить с списком VPN-клиентов
6379/tcpRedis без пароляНетЗакрыть или ограничить bind 127.0.0.1, requirepass
8080/tcpТестовое Node-приложениеНет, забылиОстановить сервис, закрыть правило firewall
3389/tcpRDPНет, временный доступЗакрыть, при необходимости — доступ только через VPN

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

Отдельно стоит смотреть не только «открыт/закрыт», но и «на каком интерфейсе». Сервис, слушающий 0.0.0.0:6379, виден всему интернету; тот же Redis на 127.0.0.1:6379 недоступен снаружи вообще, даже если порт формально существует в системе. Разница в одной строке конфига — bind 127.0.0.1 вместо bind 0.0.0.0 — и именно её чаще всего забывают проверить после того, как сервис из тестового стал «временно постоянным».

Как встроить внешний аудит в регулярную рутину

Разовый скан снимает картину на один момент, но состояние firewall дрейфует постоянно — новый контейнер, новое тестовое правило, забытый деплой. Смысл появляется только тогда, когда проверка регулярна и её результат с чем-то сравнивается автоматически, а не перечитывается вручную каждый раз.

Практическая схема для небольшой инфраструктуры:

  1. Небольшой внешний VPS (не в той же сети, что основной сервер) с установленным nmap и cron-задачей.
  2. Скрипт раз в неделю или раз в месяц сканирует список ваших публичных IP и сохраняет результат в формате grep-friendly вывода.
  3. Результат сравнивается с предыдущим сканом через diff — новый открытый порт, которого не было в прошлый раз, это сигнал, а не фоновый шум.

Простой каркас такого скрипта:

#!/usr/bin/env bash
TARGET="203.0.113.10"
DATE=$(date +%F)
OUTDIR="/opt/port-audit"
nmap -Pn -sS -p- --min-rate 2000 -oG "$OUTDIR/scan-$DATE.txt" "$TARGET"

LAST=$(ls -1 "$OUTDIR"/scan-*.txt | tail -2 | head -1)
CURRENT="$OUTDIR/scan-$DATE.txt"

if [ -f "$LAST" ]; then
  DIFF=$(diff <(grep -oP '\d+/open' "$LAST" | sort) <(grep -oP '\d+/open' "$CURRENT" | sort))
  if [ -n "$DIFF" ]; then
    echo -e "Изменения в открытых портах $TARGET:\n$DIFF" | \
      curl -s -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
      -d chat_id=<CHAT_ID> --data-urlencode text@-
  fi
fi

Частота проверок зависит от того, как часто у вас меняется инфраструктура. Для сервера, на котором почти ничего не деплоится, достаточно ежемесячного скана. Для среды с активной разработкой, где контейнеры и тестовые сервисы появляются еженедельно, разумнее проверять каждую неделю — именно там забытые порты накапливаются быстрее всего. В любом случае аудит стоит запускать и вне расписания — сразу после крупного деплоя, миграции на новый сервер или смены ответственного за инфраструктуру: это моменты, когда состояние firewall меняется резко и без явного контроля.

Документирование ожидаемого состояния firewall

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

Практичный минимум — текстовый файл в репозитории рядом с конфигами сервера, например firewall-baseline.md или .yaml:

# firewall-baseline.yaml — ожидаемое состояние публичных портов
- port: 22/tcp
  service: SSH
  reason: администрирование сервера
  owner: infra-team
  reviewed: 2026-08-15
- port: 443/tcp
  service: nginx (HTTPS)
  reason: публичный сайт
  owner: infra-team
  reviewed: 2026-08-15
- port: 51820/udp
  service: WireGuard
  reason: VPN для сотрудников
  owner: infra-team
  reviewed: 2026-08-15

Такой файл даёт три вещи сразу: явный ответ «зачем открыт этот порт», дату последнего пересмотра (что позволяет находить записи, которые никто не проверял год) и историю изменений через git — видно, кто и когда добавил конкретное правило. Это же имеет смысл дополнить комментариями прямо в правилах ufw, чтобы связь была видна и на самом сервере:

ufw allow 51820/udp comment 'wireguard vpn для сотрудников'
ufw status numbered

Для nftables или голого iptables хорошей практикой будет коммитить вывод nft list ruleset или iptables-save в тот же репозиторий при каждом осознанном изменении — тогда diff конфига firewall становится таким же читаемым событием, как diff кода. Пункт про документирование ожидаемого состояния стоит включить и в первичную настройку нового сервера — это один из шагов в чек-листе безопасности нового сервера, а не то, что добавляется постфактум, когда порты уже накопились. Если вы готовитесь к внешнему аудиту безопасности — например, для клиента или регулятора — заранее собранный и актуальный baseline экономит часы на этапе подготовки сервера к аудиту, потому что не приходится восстанавливать историю решений по памяти.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Достаточно ли просканировать top-1000 портов вместо полных 65535?

Нет для целей этого аудита. Top-1000 nmap рассчитан на типовые сервисы; забытые тестовые порты — Redis на 6379, Elasticsearch на 9200, dev-серверы на 3000/8000/8080 — часто в этот список не входят или входят не все. Полный -p- медленнее, но именно там находится то, ради чего аудит и делается.

Можно ли доверять ss -tulnp на самом сервере вместо внешнего скана?

Он полезен как первый шаг — покажет, что вообще слушает сокеты, — но не покажет, что из этого реально достижимо снаружи через оба уровня firewall (облачный + ОС) и через особенности вроде правил Docker в iptables. Финальную проверку всегда делает внешний скан.

Не заблокирует ли провайдер меня за сканирование собственного сервера?

Сканирование сервера, которым вы управляете, легально и обычно не проблема, но агрессивный --rate может быть распознан как аномальный трафик системой защиты от DDoS — как на стороне вашего провайдера, так и провайдера сканирующей машины. Держите скорость умеренной и, если сомневаетесь, уточните политику у обоих провайдеров заранее.

Нашли открытый порт и не знаете, что это за сервис — что делать в первую очередь?

Определите процесс на сервере — ss -tulnp | grep <порт> или lsof -i :<порт> покажут PID и имя процесса. Если сервис не нужен — остановите его и закройте правило firewall; если нужен, но должен быть закрыт снаружи — либо смените bind-адрес на 127.0.0.1, либо ограничьте доступ по IP через firewall, либо уберите его за VPN.

Как часто нужно повторять такой аудит?

Ориентир — раз в месяц для стабильной инфраструктуры и раз в неделю для активно меняющейся, плюс внеплановый скан сразу после крупного деплоя, миграции или смены ответственного за сервер. Конкретную периодичность лучше подстроить под то, как часто у вас реально меняется состав сервисов.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →