На каждом Mac есть две вещи, которые люди называют «файрволом». Одна — это Файрвол приложений в Системных настройках, переключатель по приложениям для входящих соединений. Другая, менее заметная — это pf, BSD-фильтр пакетов, унаследованный macOS от линии FreeBSD/OpenBSD, который сама Apple использует под капотом для общего доступа к интернету, VPN и NAT. Продвинутые пользователи и администраторы могут обращаться к нему напрямую через pfctl, и множество руководств показывают фрагмент pf.conf и на этом заканчивают. Чего эти руководства почти никогда не объясняют — это где pf перестаёт быть полезным для того, чего на самом деле хочет большинство людей: наблюдать и контролировать, что отправляют вовне их собственные приложения. Это практическая работа, а не лекция: мы включим pf, напишем правило, прочитаем состояние, которое он хранит, а затем разберёмся, почему эта форма состояния — неправильная форма для файрвола приложений.
Что такое pf на самом деле
pf — это фильтр пакетов на уровне ядра: он проверяет пакеты, проходящие через сетевые интерфейсы, и решает, правило за правилом, пропустить их или заблокировать. У него нет понятия «приложений» — он работает исключительно с заголовками пакетов: адрес источника и назначения, порт, протокол, интерфейс, направление. Это не недоработка, которую кто-то забыл исправить — это сам замысел. pf был создан для фильтрации трафика на сетевом уровне, том же уровне, на котором работают маршрутизаторы и шлюзы, и он отлично справляется с этой задачей.
Общаться с ним можно через управляющую утилиту pfctl. Её man-страница прямо разделяет два действия, которые она выполняет: загружает наборы правил из конфигурационного файла и сообщает о состоянии, которое хранит ядро. Для включения и выключения фильтра важнее всего два флага, в формулировках самой man-страницы: -e («Включить фильтр пакетов.») и -d («Выключить фильтр пакетов.»). Больше ничего в pf не включается и не выключается по отдельности — весь набор правил переключается целиком.
Включаем и читаем его состояние
Небольшая практическая работа на Mac, где вам комфортно пользоваться sudo. Сначала проверьте, включён ли уже фильтр, и посмотрите его счётчики:
sudo pfctl -s info
# example output, trimmed — the real thing includes per-rule and per-source-tracking stats with -v
Status: Enabled for 0 days 02:14:07 Debug: err
State Table Total Rate
current entries 42
searches 88213 9.7/s
inserts 611 0.1/s
removals 569 0.1/sЗатем выведите список правил, которые сейчас хранит ядро. pfctl -s rules делает именно это; man-страница отмечает, что с -v она также выводит счётчики оценки для каждого правила, пакеты и байты:
sudo pfctl -s rules
# example output, trimmed
scrub-anchor "com.apple/*" all fragment reassemble
anchor "com.apple/*" all
block drop in log quick from <blocklist> to anyСтрока com.apple/* — не декорация. Apple загружает собственные правила в именованные якоря (anchors) — термин pf для самостоятельного поднабора правил, который можно подключать и отключать без перезагрузки всего остального. Флаг -a у pfctl указывает на конкретный якорь, и, согласно man-странице, использование его с маской включает рекурсивный вывод вложенных якорей — так вы видите, что загрузила сама Apple, наряду с тем, что добавили вы:
sudo pfctl -a '*' -s rules
# example output, trimmed to the anchors that exist on a stock MacПишем и загружаем тестовое правило
Минимальный файл якоря, блокирующий исходящий трафик к одному IP-адресу, сохранённый как /etc/pf.anchors/test-block:
block drop out quick on en0 proto tcp to 203.0.113.10 port 443Чтобы загрузить отдельный файл якоря, pfctl -f читает правила из файла — согласно man-странице, этот файл может содержать макросы, таблицы, опции и правила фильтрации:
sudo pfctl -f /etc/pf.anchors/test-block
sudo pfctl -s rules
block drop out quick on en0 proto tcp from any to 203.0.113.10 port = 443Почему pf не может быть вашим файрволом приложений
Ничто из того, что следует далее, не является багом. Это то, что происходит, когда сетевой фильтр пакетов пытаются приспособить для задачи, требующей знания о том, какой процесс отправил пакет.
Он понятия не имеет, какое приложение отправило пакет
Правила pf сопоставляются по IP, порту, протоколу, интерфейсу и направлению. Там нет поля для «имени процесса», «идентификатора бандла» или «подписи кода», потому что pf находится на уровне, где существуют пакеты, но не существуют процессы. Два совершенно разных приложения, открывающих TCP-соединения к одному и тому же IP и порту, для pf неразличимы. Если вы хотите разрешить Slack обращаться к хосту, заблокировав при этом доступ к тому же хосту всем остальным приложениям, один только pf не может выразить такое правило.
Правило на имя хоста — это правило на тот IP, который был у этого имени в момент загрузки
Файлы pf.conf часто ссылаются на имя хоста для читаемости — block from evil.example.com. На самом деле загружается не это имя, а тот адрес, в который оно разрешилось. Man-страница pf.conf от OpenBSD прямо об этом говорит: «Разрешение имён хостов и преобразование интерфейсов в адреса выполняются во время загрузки набора правил». Никакого DNS-запроса во время прохождения трафика не происходит — подстановка выполняется один раз, при запуске pfctl -f, и правило продолжает совпадать с этим одним адресом до следующей перезагрузки. Это нормально для сервера со статическим IP. Всё разваливается в тот момент, когда за именем стоит CDN, облачный балансировщик нагрузки или любой сервис, ротирующий или распределяющий трафик между множеством адресов — а это описывает большую часть интернета в 2026 году. Правило, задуманное как блокировка «этого сервиса», незаметно сужается до «того одного из адресов этого сервиса, который ответил в момент загрузки правила», а трафик ко всем остальным адресам, в которые разрешается то же имя, проходит беспрепятственно.
Никаких запросов, никакого диалога — только статичный набор правил
У pf нет модели взаимодействия. Он не может приостановить соединение и спросить: «Mail впервые пытается обратиться к 51.x.x.x на порту 993 — разрешить?». Он либо совпадает с уже написанным правилом, либо переходит к поведению по умолчанию. Каждое решение нужно предугадать и записать заранее, в терминах IP и портов, до того, как трафик пойдёт. Эквивалента запроса при первом соединении не существует, потому что для запроса нужно знать, какое приложение спрашивает, а у pf этой информации нет изначально.
Ваша написанная вручную конфигурация не переживает обновление
Apple рассматривает /etc/pf.conf и загружаемые им якоря как конфигурацию, управляемую системой и завязанную на внутренние механизмы macOS — общий доступ к интернету, VPN, собственные якоря Файрвола приложений зависят от него. Обновления macOS вольны переписать или заменить этот файл. Если вы вручную отредактировали его, добавив свои правила, нет гарантии, что они переживут следующее обновление; вы узнаёте об этом постфактум, задним числом, когда ваше правило незаметно перестало применяться. Конфигурационный файл, который человек поддерживает вручную, а операционная система периодически перезаписывает, — плохое место для хранения единственной вещи, которая вас реально волновала: «обращался ли снова мой Mac к этому адресу».
Нет просмотрщика логов, нет истории, нет карты
pf может логировать совпавшие пакеты в псевдоинтерфейс pflog0, если правило включает ключевое слово log — это видно выше, в правиле блок-листа из вывода -s rules. Но этот лог — поток захвата пакетов, читаемый через tcpdump -i pflog0, а не история с возможностью поиска. Нет встроенного просмотрщика, нет списка заблокированного по приложениям с указанием времени, нет страны или организации, привязанной к адресу, — ничего, что можно было бы показать кому-то в ответ на вопрос «к чему пытался обратиться этот Mac на прошлой неделе». Вы получаете сырые пакеты, а остальное придётся строить самим.
Уровень, который Apple на самом деле построила для этой задачи
Собственный ответ Apple на запрос «я хочу фильтровать трафик своего Mac по приложениям» — это не pf, а фреймворк Network Extension, конкретно его провайдеры контентных фильтров. Документация разработчика Apple описывает эту модель прямо: «Контентный фильтр сети на устройстве проверяет пользовательский сетевой контент по мере его прохождения через сетевой стек и определяет, следует ли заблокировать этот контент или пропустить его к конечному назначению», а провайдер данных фильтра — NEFilterDataProvider — «получает пользовательский сетевой контент и проверяет его, чтобы определить, заблокировать или разрешить». Потоки представлены объектами NEFilterFlow (с конкретными случаями NEFilterBrowserFlow и NEFilterSocketFlow) — это и есть недостающий элемент, которого у pf никогда не было: объект потока, который фильтрующее приложение может изучить и привязать к процессу, открывшему его, прежде чем принять решение пропустить или заблокировать.
Именно поэтому встроенный Файрвол приложений (переключатель в Системных настройках) — это другой зверь, а не интерфейс поверх pf. Собственное руководство Apple описывает его исключительно в терминах входящего трафика: он «может защитить ваш Mac от нежелательных контактов, инициированных другими компьютерами», и работает, позволяя вам «выбирать приложения и сервисы и указывать, разрешён ли им доступ через файрвол». По приложениям — да, но только для входящих соединений, и только через тот конкретный механизм, который Apple построила именно для этой задачи. Он отвечает на другой вопрос, чем «что отправляет вовне моё приложение».
| Подход | Видит IP/порт | Знает приложение | Справляется с переименованными/ротирующимися IP | Может спросить пользователя | Направление |
|---|---|---|---|---|---|
pf (pfctl) | Да | Нет | Нет — разрешается один раз при загрузке | Нет | Любое, по правилу |
| Файрвол приложений (Системные настройки) | Нет (переключатель на уровне приложения) | Да | Неприменимо | Нет | Только входящее |
| Контентный фильтр Network Extension | Да | Да, через объект потока | Да — оценивается для каждого живого потока | Да, приложением, построенным на нём | Исходящее и входящее |
Ничто из этого не делает pf бесполезным. Если вы используете Mac как лёгкий маршрутизатор, вам нужно отклонять известный плохой диапазон адресов на уровне ядра независимо от того, какой процесс обращается, или вы хотите понять, что делают под капотом собственные функции Apple для общего доступа к интернету и VPN — pf правильный и единственный инструмент для этой задачи, а pfctl -s rules / -s info — правильный способ на него смотреть. Чего он никогда не мог сделать — это ответить на вопрос, который на самом деле есть у большинства людей: какое из моих приложений с кем сейчас разговаривает, и можно ли меня спросить прежде, чем появится новое.
Какое место здесь занимают FireAI и HisnLabs
pf and the built-in Application Firewall are both worth using — FireAI does not replace either; it fills the specific gap neither one can, by tying outbound decisions to the app’s code signature and asking before an unknown one gets a first connection.
FireAI — это собственный продукт HisnLabs: ИИ-файрвол, работающий прямо на вашем Mac. Он показывает простым языком каждое соединение, которое устанавливают ваши приложения, и позволяет вам решать, что покидает ваш Mac, — его ИИ работает локально, поэтому ваш трафик никогда не отправляется ни нам, ни кому-либо ещё. Команда исследователей безопасности HisnLabs — это те, кто поддерживает точность этих решений: они каталогизируют, какие домены являются обычной телеметрией, а какие — реальным сервисом, отслеживают страну и сеть, стоящие за соединением, и обучают локальную модель (функцию Autopilot) на реальных шаблонах трафика — и ничего из этого не покидает ваш Mac.
Вы можете прочитать о технических решениях, лежащих в его основе, или попробовать FireAI в течение 17 дней на странице FireAI от HisnLabs.
