Настройка брандмауэров в OC Apple macOS 10

imageГрафическая оболочка брандмауэра в операционной системе Apple macOS у любого более или менее подготовленного пользователя вызывает вопросы из-за отсутствия возможности тонкой настройки собственных правил. Она как "рубильник", имеющий лишь два пограничных состояния "ВКЛ" и "ВЫКЛ". О том, что с этим делать и как настроить собственные правила, например, открывающие тот или иной порт в macOS, мы и поговорим в данной заметке.

image

Под капотом графической оболочки этого упрощённого брандмауэра в ОС Apple macOS скрывается демон socketfilterfw, известный также как "Application Firewall" или "Socket Firewall".

Помимо socketfilterfw в ОС есть ещё один, более мощный и функциональный, брандмауэр, скрытый от глаз обычного пользователя - Packet Filter (PF), который пришёл ещё в OS X Lion (10.7) из OpenBSD.

Как в macOS включать pf автоматически во время загрузки ОС?

После внедрения System Integrity Protection (SIP) в OS X El Capitan (10.11) важные для системы каталоги и файлы пользователю доступны только в режиме чтения, поэтому файл демона /System/Library/LaunchDaemons/com.apple.pfctl.plist отредактировать без отключения SIP возможным не представляется. Однако, отключать защиту целостности системы для модифицирования системных файлов затея, сама по себе, не очень правильная. Поэтому мы не будем рассматривать такой метод.

Мне известно два способа заставить pf загружаться по умолчанию при старте системы – запускать pf скриптом из собственного домена или запускать в режиме обработки rc.server

Запуск pf в качестве демона

Суть этого способа заключается в том, чтобы создать собственного демона, который будет запускать pf напрямую, либо через скрипт:

sudo nano /Library/LaunchDaemons/com.custom.pfctl-enable.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.custom.pfctl-enable</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/sh</string>
        <string>-c</string>
        <string>/etc/pf.start.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>ExitTimeOut</key>
    <integer>1</integer>
</dict>
</plist>

В примере будем использовать скрипт, который будет включать pf и загружать кастомную конфигурацию:

sudo nano /etc/pf.start.sh
# Включаем PF с дефолтным конфигом
pfctl -ef /etc/pf.conf
#
# Загружаем кастомную конфигурацию
if [ -f /etc/pf.custom.conf ]; then
    pfctl -ef /etc/pf.custom.conf
fi
#

Разрешим выполнение скрипта:

sudo chmod +x /etc/pf.start.sh

Дополнительная конфигурация /etc/pf.custom.conf необходима из-за того, что файл /etc/pf.conf каждый раз при обновлении macOS переписывается на дефолтный, так же как и подключаемые в нём правила /etc/pf.anchors/com.apple, поэтому и редактировать их напрямую не имеет смысла. Скорее всего, подобное поведение связано с параметрами "Блокировать все входящие подключения" и "включить режим невидимости" в графическом брандмауэре. Включение этих параметров включает pf и добавляет правила в "com.apple/250.ApplicationFirewall".

Создадим файл кастомной конфигурации pf:

sudo nano /etc/pf.custom.conf
#Политика блокировки пакетов по умолчанию
set block-policy drop
#Подключение отпечатков ОС
set fingerprints "/etc/pf.os"
#Игнорируемые интерфейсы
set skip on lo0
#
# Подключаем правила
anchor "FilterRules"
load anchor "FilterRules" from "/etc/pf.anchors/FilterRules"

Настроим правила файрвола под свои нужды:

sudo nano /etc/pf.anchors/FilterRules
##Tables
#
#Networks
table <Admin-Network> persist {10.185.3.0/24}
table <Test-Network> persist {10.185.45.0/20}
table <VPN-Networks> persist {10.20.30.0/24, 10.28.30.0/24, 10.97.115.0/24}
table <Server-Networks> persist {10.185.0.0/24, 10.185.10.0/24, 10.185.20.0/24}
#
#Machines
table <Test> persist {10.185.8.14, 10.185.8.127}
#
#Temp
table <Temp> persist
#
##Scripts
#
# Объединяем таблицы сетей и машин в переменную для разрешения SMB
allowsmb = "{<Server-Networks>, <Test-Network>, <Test>}"
#
##Rules
#
# Блокируем все входящие соединения
block drop in all
#
# Разрешаем все исходящие соединения
pass out all
#
# Логирование
block return in log all
#
# Блокировать любой не маршрутизируемый входящий трафик
block in from no-route
#
#Запрещаем все TCP соединения с устаревших Windows систем
block in os {"Windows XP", "Windows 2003"}
#
#Разрешаем входящие IPv6 соединения на проприетарный софтовый интерфейс awdl0
#Необходимо для работы AirDrop: Передача файлов, буфера обмена, ссылок
pass in quick on awdl0 inet6 proto {tcp, udp} to port {5353, 8770}
#
# Разрешаем ICMP на Ethernet и Wi-Fi интерфейсах
pass in on {en0, en01} proto icmp
#
# Разрешить все соединения из сети администраторов
pass in from <Admin-Network>
#
#Разрешить временные TCP соединения на Ethernet интерфейсе
pass in on en0 proto tcp from <Temp>
#
# Разрешаем подключения SSH и VNC на Ethernet интерфейсе c VPN-сетей
pass in on en0 proto tcp from <VPN-Networks> to port {ssh, rfb}
#
# Разрешаем подключения SMB на Ethernet интерфейсе объявленной переменной $allowsmb
pass in on en0 proto tcp from $allowsmb to port microsoft-ds
#

Обратите внимание на то, что в нашем примере таблица «Temp» пустая. Она необходима для динамического управления правилами, без перезапуска PF.

По умолчанию pf работает с /etc/services, поэтому для открытия ssh не обязательно указывать порт 22, можно указать имя сервиса.

Проверим конфигурацию на наличие ошибок:

sudo pfctl -nf /etc/pf.custom.conf

Если ошибок нет, загрузим службу в систему:

sudo launchctl load -w /Library/LaunchDaemons/com.custom.pfctl-enable.plist

Проверим состояние pf:

sudo pfctl -s info

Если по какой-то причине нет желания связываться с запуском скрипта из демона и хочется запускать PF напрямую, например, без правил Apple, то можно изменить контент XML таким образом, чтобы запускался не скрипт, а pfctl с указанным конфигом.

…
<string>/sbin/pfctl</string>
<string>-ef</string>
<string>/etc/pf.custom.sh</string>
…

В этом случае какие-либо параметры из /etc/pf.conf загружены не будут.

Запуск pf в режиме обработки rc.server

Суть второго способа запуска pf сводится к созданию файла /etc/rc.server, который обрабатывается системой в процессе каждой загрузки.

sudo nano /etc/rc.server
# Add Server Firewall Configuration
if [ -f /etc/pf.custom.conf ]; then
pfctl -ef /etc/pf.custom.conf
fi
sudo chmod +x /etc/rc.server

Ситуация в этом случае аналогичная, после запуска macOS выполняется скрипт, который запускает pfctl с указанной конфигурацией.

Скрипт можно запустить вручную командой вида:

sudo /etc/rc.server

Таким образом каждый раз при загрузке macOS файрвол pf будет загружаться в систему с настроенными правилами автоматически.


Базовые примеры работы с pfctl

Далее небольшая шпаргалка с примерами использования разных ключей pfctl, которые могут пригодится в работе.

Перезапуск pf с указанной конфигурацией:

sudo pfctl -f /etc/pf.custom.conf

Просмотр всех подключенных правил:

sudo pfctl -v -s Anchors

Просмотр системных подключенных правил для работы Bonjour:

sudo pfctl -a com.apple/200.AirDrop/Bonjour -s rules

Добавить 10.185.62.15 в таблицу Temp:

sudo pfctl -a FilterRules -t Temp -T add 10.185.62.15

Просмотреть адреса в таблице Temp:

sudo pfctl -a FilterRules -t Temp -T show

Удалить 10.175.62.15 из таблицы Temp:

sudo pfctl -a FilterRules -t Temp -T delete 10.185.62.15

Удалить все записи в таблице Temp:

sudo pfctl -a FilterRules -t Temp -T flush

Просмотр статистики по всем таблицам:

sudo pfctl -vvs Tables

Просмотр активных соединений:

sudo pfctl -s state

Для тех, кто не хочет использовать окно терминала, но хочет испробовать pf, можно взять на вооружение приложение Murus. Murus Lite бесплатен для некоммерческого использования.


Демон адаптивного брандмауэра

Если Вы устанавливаете Server.app из AppStore, то pf будет включаться автоматически, как уже было сказано выше. Так же появится несколько дополнительных подключаемых конфигураций с правилами и демон адаптивного файрвола /Applications/Server.app/Contents/ServerRoot/usr/libexec/afctl. Для переопределения параметров запуска pf используется конфиг /Library/Server/Firewall/Anchors/combined_anchor.txt.

Настраивать pf или подключать свои правила нужно именно через конфиг /Library/Server/Firewall/Anchors/combined_anchor.txt.

В /etc/pf.anchors/com.apple подключится адаптивный файрвол

/Applications/Server.app/Contents/ServerRoot/private/etc/pf.anchors/400.AdaptiveFirewall

table <blockedHosts> persist file "/var/db/af/blockedHosts"
block in quick from <blockedHosts> to any

Так же добавится конфигурация правил /etc/pf.anchors/com.apple.server-firewall, которая подключает пустые дочерние конфиги правил, которые так же можно использовать:

anchor "base"
load anchor "base" from "/Library/Server/Firewall/Anchors/default_anchor.txt"
anchor "custom-firewall"
load anchor "custom-firewall" from "/Library/Server/Firewall/Anchors/custom_anchor.txt"

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

Так как демон afctl располагается в нестандартном месте, добавим его путь для быстрого вызова в профиль ~/.zprofile текущего пользователя или глобально для всех пользователей в /etc/zprofile.

#afctl path
export PATH=$PATH:/Applications/Server.app/Contents/ServerRoot/usr/libexec

Применим изменения

source ~/.zprofile

Посмотреть временный чёрный список можно двумя способами:

Через afctl, получим просто список

sudo afctl -l

Или прочитать файл /var/db/af/blacklist

sudo cat /var/db/af/blacklist

В файле дополнительно содержится информация о времени удаления из чёрного списка в формате Unix timestamp — количество секунд от 01.01.1970 00:00 UTC. Для преобразования Unix timestamp в понятный для человека формат:

date -r 1582979823

При необходимости, хосты можно блокировать вручную и на больший срок, например, на 90 минут:

sudo afctl -a 10.25.8.45 -t 90
Командное управление socketfilterfw

Управлять фаерволом удобнее с графического интерфейса, но можно и с терминала. Для удобства можно аналогичным образом можно добавить путь в ~/.zprofile или /etc/zprofile

#socketfilterfw path
export PATH=$PATH:/usr/libexec/ApplicationFirewall

Рассмотрим доступные ключи.

Узнать состояние:

sudo socketfilterfw --getglobalstate
  • Firewall is disabled. (State = 0) -- Файрвол выключен
  • Firewall is enabled. (State = 1) -- Файрвол включен
  • Firewall is enabled. (State = 2) -- Файрвол выключен, запрещены все входящие соединения.

Включить или выключить:

sudo socketfilterfw --setglobalstate on | off

Запретить или запретить все входящие соединения:

sudo socketfilterfw --setblockall on | off

Разрешить или запретить встроенным приложениям взаимодействовать с сетью:

sudo socketfilterfw --setallowsigned on | off

Разрешить или запретить подписанным приложениям взаимодействовать с сетью:

sudo socketfilterfw --setallowsignedapp on | off

Разрешить или запретить ICMP:

sudo socketfilterfw --setstealthmode on | off

Включить или отключить логирование:

sudo socketfilterfw --setloggingmode on | off

Уровень детализации лога:

sudo socketfilterfw --setloggingopt throttled | brief | detail

Добавить приложение в фаервол, по умолчанию трафик разрешается:

sudo socketfilterfw --add /Applications/MyApp.app/Contents/MacOS/myapp

Заблокировать входящий трафик приложения:

sudo socketfilterfw --blockapp /Applications/MyApp.app/Contents/MacOS/myapp

Разрешить входящий трафик приложения:

sudo socketfilterfw --unblockapp /Applications/MyApp.app/Contents/MacOS/myapp

Удалить приложение из фаервола:

sudo socketfilterfw --remove /Applications/MyApp.app/Contents/MacOS/myapp

Справка:

socketfilterfw -h
man socketfilterfw
Сосуществование socketfilterfw и pf

Разумеется, лучшей практикой является комбинирование двух решений: Packet Filter и socketfilterfw.

Все входящие соединения сперва обрабатываются pf, затем socketfilterfw.

Рассмотрим пример.

Если требуется создать правило для организации NFS сервера, добавим правило в socketfilterfw:

sudo socketfilterfw --add /sbin/nfsd

Затем добавим в правило в pf и перезапустим его:

pass in proto {tcp, udp} to port {111, 972, 977, 2049}

Таким образом мы не просто откроем TCP и UDP порты в системе, но и ограничим приложения, которые могут принимать соединения.

Диагностика и отладка

В решении проблем, которые могут возникнуть при работе с брандмауэрами, можно воспользоваться их логированием.

Для настройки логирования pf в файле правил /etc/pf.anchors/FilterRules добавим правила логирования, которые должны находится над разрешающими правилами.

block return in log all

Создаём интерфейс:

sudo ifconfig pflog0 create

Читаем интерфейс с помощью tcpdump:

sudo tcpdump -n -e -ttt -i pflog0

Лог с интерфейса можно просматривать и другими средствами, например wireshark.

После завершения диагностики, можно удалить интерфейс (либо он будет удалён при последующей перезагрузке ОС):

sudo ifconfig pflog0 destroy

Если необходимо, чтобы интерфейс pflog0 автоматически создавался при старте системы, необходимо добавить "ifconfig pflog0 create" в /etc/pf.start.sh или в /etc/rc.server, в зависимости от того, что используется.

Если с логированием PF всё более или менее ясно, то с логированием socketfilterfw всё несколько иначе, и, я бы даже сказал, странно. При включении лога файл создаётся /var/log/appfirewall.log, но он всегда пустой. В форумах обсуждают проблему уже достаточно давно (apple, macrumors, stackexchange), но решения нет. Если кто-то знает, как заставить socketfilterfw писать лог, прошу поделится этим тайным знанием Smile

Добавить комментарий