После нескольких циклов сборки на удалённом 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, поэтому открывать её на всех сетевых интерфейсах не потребуется.
Перенесите следующую сборку на выделенный физический узел Apple Silicon
Выберите одну из трёх конфигураций и пяти узлов с учётом масштаба задачи. Вычислительные ресурсы и хранилище не делятся с другими клиентами. Все узлы стабильно работают 365 дней в году; фактический статус доступности возвращается в реальном времени через консоль.