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

Что может решить пакетный фильтр, а что нет

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

Фильтр также не создаёт маршруты. Когда клиент падает, маршрут по умолчанию может всё ещё указывать на мёртвый туннель, и pf превращает это в блокированные пакеты, а не в тихий откат на Wi-Fi. Блокировка — это задуманный результат, а не неисправность, которую нужно обойти.

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

Сначала прочитайте факты об интерфейсе и конечной точке

Беспроводные интерфейсы нумеруются по-разному на каждом Mac, а устройство туннеля получает номер, который может меняться между загрузками. Правило, называющее несуществующее устройство, падает при разборе, что громко и легко заметно, тогда как правило, называющее не то существующее устройство, разбирается чисто и пропускает трафик, который вы хотели остановить.

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

route -n get default
ifconfig -l
scutil --nc list

Первая команда называет интерфейс, который сейчас побеждает, вторая перечисляет каждое устройство, известное ядру, а третья показывает конфигурации VPN, которые система может запустить. Запишите эти ответы, затем прочитайте адрес и порт сервера из собственной конфигурации клиента.

Напишите набор правил вокруг пути туннеля

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

set skip on lo0
block all
pass out quick on utun0
# maintain this line when the server address changes
pass out quick on en1 inet proto udp to 203.0.113.10 port 1194
# only the resolver the client uses before the tunnel exists
pass out quick on en1 inet proto udp to 192.0.2.53 port 53
pass out quick on en1 inet proto udp from any port 68 to any port 67
pass out quick on en1 inet proto udp to 224.0.0.251 port 5353

Строка DHCP существует потому, что Mac запрашивает адрес раньше, чем получает его, поэтому запрос уходит с порта 68 к порту 67 без пригодного адреса источника. Строка multicast позволяет системе судить, стоит ли использовать беспроводное соединение, а её удаление даёт машину, которая в рабочей сети сообщает об отсутствии связи. Строка конечной точки — это то, что OpenVPN вообще позволяет достучаться до своего сервера; без неё block all отбрасывает внешнее рукопожатие, и туннель никогда не поднимается. Строка резолвера уже общего разрешения DNS, потому что называет один адрес и один порт вместо каждого запроса, который делает машина.

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

Опустите туннель, затем перезагрузитесь

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

sudo pfctl -n -v -f /etc/pf.conf
sudo pfctl -f /etc/pf.conf
sudo pfctl -e
sudo pfctl -s rules
sudo pfctl -s Anchors
sudo pfctl -a <anchor> -s rules
sudo pfctl -s info

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

Перезагрузка — та часть, которую люди пропускают. macOS читает /etc/pf.conf при загрузке, но чтение файла не то же самое, что включение фильтра, поэтому набор правил, который работает сейчас, может оказаться неактивным после перезапуска. Затем выполните sudo pfctl -s info и убедитесь, что строка состояния сообщает о включённом фильтре.

Держите выход и путь перезапуска честными

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

sudo pfctl -d
sudo pfctl -f /etc/pf.conf.previous
sudo pfctl -s rules

Для постоянства нужен launch daemon, который выполняет pfctl -e и загружает ваш файл при загрузке. Относитесь к нему как к любой другой привилегированной задаче: абсолютные пути, журнал, который можно прочитать потом, и проверка на машине, до которой можно физически добраться. Демон, тихо падающий, оставляет машину незащищённой и нормально выглядящей.

Задокументируйте пробелы, начиная со второго клиента VPN, который создаёт собственное устройство туннеля. Ваши правила называют один интерфейс туннеля, поэтому второй остаётся заблокированным или его ловит широкое правило, добавленное позже. Интерфейсы tethering и точки доступа появляются только иногда и заслуживают той же заметки. Ни один из этих пробелов не причина пропускать политику; это условия, при которых она всё ещё действует.

Похожие статьи