Графическая оболочка брандмауэра в операционной системе Apple macOS у любого более или менее подготовленного пользователя вызывает вопросы из-за отсутствия возможности тонкой настройки собственных правил. Она как "рубильник", имеющий лишь два пограничных состояния "ВКЛ" и "ВЫКЛ". О том, что с этим делать и как настроить собственные правила, например, открывающие тот или иной порт в macOS, мы и поговорим в данной заметке.
Под капотом графической оболочки этого упрощённого брандмауэра в ОС 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 писать лог, прошу поделится этим тайным знанием
Добавить комментарий