Инженерная статья

Аудит прослушиваемых портов облачного Mac и сокращение сетевой экспозиции

Аудит прослушиваемых портов облачного Mac и сокращение сетевой экспозиции

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

Сначала создайте базовый снимок прослушиваемых портов

Соберите состояние TCP и UDP, когда узел не занят и на нём не выполняются временные задачи отладки. Параметр -nP отключает разрешение имён и преобразование номеров портов в названия служб, поэтому результат удобнее сохранять и сравнивать.

mkdir -p "$HOME/audit/network"
stamp="$(date -u +%Y%m%dT%H%M%SZ)"

sudo lsof -nP -iTCP -sTCP:LISTEN \
  > "$HOME/audit/network/tcp-$stamp.txt"

sudo lsof -nP -iUDP \
  > "$HOME/audit/network/udp-$stamp.txt"

netstat -anv -p tcp \
  > "$HOME/audit/network/netstat-tcp-$stamp.txt"

Не ограничивайтесь номером порта. Поля COMMAND, PID, USER и NAME нужно оценивать вместе: обычный пользователь, запустивший известный сервер разработки, и неизвестная программа, работающая от root, требуют совершенно разного приоритета реагирования.

Форма прослушивания Обычное значение Следующий шаг
127.0.0.1:8080 Доступ только локально по IPv4 Убедиться, что это ожидаемый сервис разработки
[::1]:8080 Доступ только локально по IPv6 Оставить и проверить SSH-перенаправление
*:8080 Прослушивание всех доступных интерфейсов Проверить процесс и в первую очередь сузить привязку
10.x.x.x:8080 Привязка к конкретному интерфейсу Сверить с сетевой политикой узла
UDP без фиксированного удалённого узла Возможно, служба обнаружения или вспомогательный сервис Выяснить назначение процесса и источник запуска

Состояние LISTEN само по себе не означает, что порт обязательно доступен извне. Итоговая доступность зависит одновременно от адреса прослушивания, межсетевого экрана хоста и сетевой политики узла. Однако адрес с подстановочным знаком показывает, что процесс намеренно расширил область приёма подключений, поэтому такую привязку следует объяснить в первую очередь.

Свяжите порт с процессом и механизмом запуска

Определив PID, используйте ps, чтобы проверить полную команду, родительский процесс и время работы, а затем выясните, не запускается ли процесс постоянным заданием автозапуска.

pid=1234
ps -p "$pid" -o pid,ppid,user,lstart,command
sudo lsof -nP -p "$pid"

launchctl print "gui/$(id -u)" > "$HOME/audit/network/launch-gui.txt"
sudo launchctl print system > "$HOME/audit/network/launch-system.txt"

find "$HOME/Library/LaunchAgents" \
  /Library/LaunchAgents \
  /Library/LaunchDaemons \
  -maxdepth 1 -name '*.plist' -print 2>/dev/null

Для подозрительного plist-файла выполните plutil -p путь_к_файлу и проверьте Program, ProgramArguments, RunAtLoad и KeepAlive. Процесс, временно запущенный скриптом сборки, должен завершиться вместе с задачей. Если параметр KeepAlive постоянно перезапускает его, один лишь kill проблему не решит.

Ведите реестр мер реагирования

Для каждого несистемного слушателя как минимум фиксируйте назначение сервиса, ответственного, способ запуска, адрес привязки и причину сохранения. Если владельца процесса определить не удаётся, сначала остановите связанную задачу и сохраните командную строку, пути к журналам и содержимое plist. Не удаляйте файлы сразу, иначе будет потерян контекст расследования.

Сначала измените адрес привязки, а не блокируйте порт

Если сервис разработки нужен только текущему пользователю, самое прямое исправление — привязать его к адресу обратной петли. Тогда даже при изменении правил межсетевого экрана процесс не начнёт принимать подключения через другие интерфейсы.

python3 -m http.server 8080 --bind 127.0.0.1

HOST=127.0.0.1 PORT=8080 ./run-development-server

Для доступа со своего компьютера организуйте точку входа через локальное SSH-перенаправление:

ssh -N \
  -L 127.0.0.1:9000:127.0.0.1:8080 \
  "$REMOTE_USER@$REMOTE_HOST"

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

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

Проверьте оба уровня межсетевого экрана в режиме только для чтения

Межсетевой экран приложений macOS и pf решают разные задачи. Во время аудита сначала считайте их состояние и не заменяйте правила непосредственно из удалённого сеанса.

sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --listapps

sudo pfctl -s info
sudo pfctl -sr
sudo pfctl -sn

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

Главный риск удалённого изменения pf — не синтаксическая ошибка, а блокировка текущего административного подключения. Если нет второго проверенного способа доступа, ограничьтесь проверкой только для чтения. Когда изменение действительно необходимо, сначала сохраните резервную копию правил, подготовьте точную команду отката и непрерывно проверяйте доступность консоли из другого сеанса.

Выявляйте повторное появление портов сравнением снимков

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

latest_tcp="$(ls -t "$HOME"/audit/network/tcp-*.txt | head -n 1)"
diff -u "$HOME/audit/network/tcp-approved.txt" "$latest_tcp"

latest_udp="$(ls -t "$HOME"/audit/network/udp-*.txt | head -n 1)"
diff -u "$HOME/audit/network/udp-approved.txt" "$latest_udp"

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

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

Часто задаваемые вопросы

Означает ли состояние LISTEN, что порт доступен из интернета?

Нет. Нужно проверить адрес привязки, сетевую политику и правила межсетевого экрана. Службы на 127.0.0.1 или ::1 обычно доступны только локально, а привязка к * или сетевому интерфейсу требует внешней проверки.

Можно ли менять правила pf во время удалённого аудита?

Только при наличии проверенного пути восстановления. Сначала сохраните правила и список слушателей, оставьте второе административное соединение и тестируйте минимальные изменения. Иначе проводите аудит только на чтение.

Как подключиться к службе, привязанной только к localhost?

Используйте локальную переадресацию портов SSH. Порт рабочей станции будет направлен к удалённой службе на 127.0.0.1, поэтому открывать её на всех сетевых интерфейсах не потребуется.

OwnAMac Облачный Mac

Перенесите следующую сборку на выделенный физический узел Apple Silicon

Выберите одну из трёх конфигураций и пяти узлов с учётом масштаба задачи. Вычислительные ресурсы и хранилище не делятся с другими клиентами. Все узлы стабильно работают 365 дней в году; фактический статус доступности возвращается в реальном времени через консоль.

Выбрать конфигурацию и заказать