Вкратце:
- cfnetwork — Puppet API для полной настройки сети и фильтра через ресурсы Puppet. Идеально дружит с Hiera и потенциально другими "data providers" в концепции Puppet.
- cffirehol — "meta-provider" конкретной реализации настройки фильтра для
cfnetwork
на базе замечательного генератора FireHOL- Пока поддерживаются только Debian 8+ (Jessie и выше) и Ubuntu 12.04+ (Trusty и выше)
Лирическое вступление: так уж сложилось, что автор крайне параноидален на тему контроля развёрнутых систем и автоматизации. Долгие годы копился опыт встречаемых проблем и относительно местечковых решений. После ухода с предыдущего места работы стало понятно, что осязаемого багажа в сфере администрирования в общем-то и нет. Впрочем, то, что было, тащить с собой дальше особо и не хотелось. Так родился новый велосипед. А теперь к делу.
Примечание: По тексту целенаправленно используется фильтр
или сетевой фильтр
вместо другого "русского" термина брандмауэр
, которое насилует неокрепший детский слух, глаза и мозг автора.
Чем не устраивают существующие решения?
- Слабая интеграция конфигурации сети и сетевого фильтра — дополнительная возня с (пере-)конфигурацией и высокий риск ошибок; сложности в создания plug&play модулей отдельных сервисов, которые дружат с системой безопасности.
- Отсутствие наглядности для аудита — конфигурация либо не лаконична и размазана по многим файлам, либо вообще существует в нечитаемом для человека виде.
- Излишне низкоуровневая конфигурация фильтра без абстракций — те же проблемы, что и пунктом выше. Можно сравнить с написанием веб-странички на ассемблере.
- Отсутствие "интеллектуальности" установок по умолчанию — для некоторых топорность является преимуществом, но не для автора.
- Настройку сетевого стека вообще обходят стороной — ванильные настройки ядра далеко не всегда то, что вы хотите видеть на боевой системе даже до начала тестирования и оптимизации, не говоря уже о безопасности.
Общая концепция настройки сети и фильтра
Никаких новых теорий — обыденность из разных мест.
- Каждый логических сетевой интерфейс имеет уникальное значимое имя вроде
world
,dmz
,office
и т.д.local
зарезервировано дляloopback
интерфейса. - Настройки стандартных логических интерфейсов задаются и доступны генератору правил фильтра.
- Поддержка особого типа интерфейса
any
— генератор фильтра должен быть достаточно интеллектуальным чтобы не плодить лишние правила там, где они никогда не сработают. К примеру, если для исходящих или входящих задан список разрешённых адресов, то правила не должны добавиться на интерфейсы, где такие соединения не предполагаются конфигурацией сети в принципе. - Вместо прямого указания портов, используется ассоциативное имя, за которым может скрываться целый набор портов и протоколов.
- Временная донастройка сети и фильтра должна легко производится на целевой машине без централизованного управления при восстановлении после сбоев.
- Достаточная сетевая безопасность должна быть достигнута ещё до включения AppArmor или SeLinux.
- Динамическая защита должна быть реализована отдельно, но интерфейс черных списков задаётся на этом уровне.
Выбор технологий
- Puppet 4 + Puppet DB + Hiera — автор честно пытался привить себе любовь хотя бы к одному из Ansible и Chef, но четвёртая версия Puppet взяла своё. Хотя, Ansible выглядит интересным для периодических задач по содержанию систем и изначальному развёртывания Puppet.
- Ruby — по сути предопределённый выбор для расширений Puppet. К слову, автору пришлось изучить этот ЯП в ходе проекта, о чём совершенно не жалеет.
- FireHOL — это первый сторонний генератор
iptables
, которому автор смог доверить свой сетевой фильтр за более чем 10 лет активного администрирования серверов. Все остальные генераторы субъективно меркнут.
Что получилось
Сам интерфейс состоит из основного класса cfnetwork
и набора типов cfnetwork::*
для задания настроек сети и сетевого фильтра. Есть возможность задавать все настройки программно через Puppet DSL или же через поставщика данных вроде Hiera.
Краткое описание API с неполным списком параметров. С полным можно ознакомиться на английском языке.
класс cfnetwork
main
— настройки типаcfnetwork::iface
для основного интерфейса.dns
— список DNS серверов или специальные значения:
'$recurse'
— поставить локальный сервер.'$serve'
— то же самое, но и ещё и обслуживать клиентов на$service_face
.
is_router
— выполняет ли эта машина функцию сетевого маршрутизатора.optimize_10gbe
— подогнать настройки TCP по умолчанию для максимальной производительности соединений через 10+Gbit интерфейсы вместо публичных "интернетных" с ориентировочной задержкой в 50-100ms.- Удобства для использования Hiera.
Все значения, кромеifaces
имеютlookup_options: { merge: hash }
(документация).
ifaces
— набор конфигураций второстепенных интерфейсов типаcfnetwork::iface
.describe_services
— набор описаний ресурсов типаcfnetwork::describe_service
(описание сервисов).service_ports
— набор *cfnetwork::service_port
(входящие соединения).client_ports
— набор *cfnetwork::client_ports
(исходящие соединения).dnat_ports
— набор *cfnetwork::dnat_ports
.router_ports
— набор *cfnetwork::router_ports
.
тип cfnetwork::iface
— конфигурация интерфейса.
title
— ассоциативный идентификатор, который будет использоваться в других ресурсах.device
— системный сетевой интерфейс.address
— основной адрес IPv4/IPv6 вместе с маской сети в формате"address/cidr"
.extra_addresses
— дополнительные адреса в таком же формате.extra_routes
— дополнительные настройки маршрутизации (тоже важно для генератора фильтра).gateway
— подразумевает маршут по умолчанию, что используется в генераторе фильтра.force_public = auto
— крайне важная настройка для фильтра:
- По умолчанию, если
$address
принадлежит 10/8, 172.16/12 или 192.168/16, тоfalse
, иначеtrue
. - Если
true
:
- Автоматически добавляет SNAT или MASQUERADE для исходящих соединений.
- Автоматически включает TCP SYNPROXY для входящих соединений, включая DNAT.
- Ставит политику
DROP
вместоREJECT
по умолчанию. - Ограничивает входящие ping до 1/сек. через hashlimit для одного IP.
- Устанавливает глобальный чёрный список входящик IP, за исключение особого белого списка.
- По умолчанию, если
тип cfnetwork::describe_service
— описание сервиса (протоколов и портов).
title
— название ресурса используется по всех названиях портов.server
— список серверных портов в форматеproto/portnum
. Пример:[ 'tcp/80', 'tcp/443' ]
.
тип cfnetwork::client_port
— описание исходящего соединений.
Терминология взята из FireHOL..
title = '<iface>:<service>[:<tag>]'
src
,dst
,user
,group
,comment
тип cfnetwork::service_port
— описание входящего соединений.
title = '<iface>:<service>[:<tag>]'
src
,dst
,comment
тип cfnetwork::router_port
— описание разрешённого маршрутизируемого соединения.
title = '<iface>/<outface>:<service>[:<tag>]'
src
,dst
,comment
тип cfnetwork::dnat_port
— описание одновременно машрутизируемого соединения и трансляции адреса назначения
title = '<iface>/<outface>:<service>[:<tag>]'
src
,dst
,comment
to_dst
— адрес перенаправления (IPv4 и IPv6)to_port
— порт перенаправления (не обязательно)
Описание унифицированных параметров:
<iface>
— название ассоциированного ресурсаcfnetwork::iface
или же:
'local'
— как уже сказано выше — только локальный трафик, но учитывайте, что трафик на СВОЙ же внешний IP тоже идёт черезlocal
!'any'
— специальная замысловатая логина на базеsrc
,dst
иto_dst
чтобы не создавать заведомо неиспользуемые правила. При отсутствии этих параметров, добавляется на все возможные интерфейсы, где имеет смысл. (Например,local
не имеет смысла вrouter_port
)
<outface>
— то же самое, но для второго интерфейса в случае сdnat_port
иrouter_port
<service>
— название описания сервиса вcfnetwork::describe_service
<tag>
— необязательная часть, которая идёт вcomment
.
Добавлена для избежания конфликта имён ресурсов без необходимости явно использовать "virtual resources"src
,dst
— списки исходящих и целевых адресов IPv4/IPv6comment
— любой однострочный комментарий (принудительно вырезаются переводы строки)user
,group
— проверка пользователя и группы для исходящих соединений (настоящий параноик обязан их использовать даже дляlocal
)
класс cfnetwork::sysctl
— возможность тонкой настройки сетевого стека, стандартные ключи выведены в виде параметров класса.
класс cffirehol
— генератор фильтра
enable=false
— нужно принудительно включить после того, как убедитесь, что конфиг фильтра соответствует ожиданиямsynproxy_public=true
— флаг включения SYNPROXY на публичных интерфейсахip_whitelist
/ip_blacklist
— статические списки.ip_blacklist
не следует вообще задавать тут, а нужно запихивать динамически вipset
из постоянно обновляемых баз и систем динамической защиты, но это отдельная история.
Предопределённый наборы:
whitelist4
иwhitelist6
— IPv4 и IPv6 сети белого спискаblacklist4
— индивидуальные IPv4 адреса чёрного спискаblacklist4net
иblacklist6net
— IPv4 и IPv6 сети чёрного списка
Поскольку в Debian и Ubuntu не было достаточно свежего пакета FireHOL, и поскольку стандартные запускаются лишь ПОСЛЕ поднятия сетевых интерфейсов, пришлось сделать свои сборки .deb пакетов.
Примечание: в описании каждого Puppet модуля из серии cfxxx
есть раздел "Implicitly created resources", где описываются все определяемые ресурсы сетевого фильтра.
Живой пример
Полноценное развёртывание инфраструктуры в Vagrant, используя не освещённые в этой статье модули, можно посмотреть здесь.
Для наглядности приводится конфигурация сети и фильтра маршрутизатора:
настройки Hiera
classes: - cfnetwork # После того, как конфиг проверен через `/sbin/firehol try` #cffirehol::enable: true cfnetwork::is_router: true cfnetwork::main: device: eth1 address: '192.168.1.30/24' extra_addresses: '192.168.1.40/24' gateway: '192.168.1.1' # принудительная имитация публичного интерфейса force_public: true cfnetwork::ifaces: vagrant: device: eth0 method: dhcp # просто доводим до сведения extra_routes: ['10.0.1.1/25'] infradmz: device: eth2 address: '10.10.1.254/24' dbdmz: device: eth3 address: '10.10.2.254/24' webdmz: device: eth4 address: '10.10.2.254/24' cfnetwork::describe_services: testdb: server: 'tcp/1234' cfhttp: server: - 'tcp/80' - 'tcp/443' # DNAT для входящих HTTP соединений (не лучшее решение в боевом режиме) cfnetwork::dnat_ports: 'main/webdmz:cfhttp': dst: '192.168.1.40' to_dst: '10.10.2.10' cfnetwork::router_ports: # Разрешить локальному NTP, DNS, APT стучаться во внешний мир 'infradmz/main:cfhttp:apt': src: 'maint.example.com' 'infradmz/main:ntp': src: 'maint.example.com' # Разрешить Puppet Server (r10k) скачивать модули 'infradmz/main:cfhttp:puppet': {} # Разрешить серверам из DMZ обращаться к инфраструктурным сервисам 'any/infradmz:ntp': src: '10.10.0.0/16' dst: 'maint.example.com' 'any/infradmz:dns': src: '10.10.0.0/16' dst: 'maint.example.com' 'any/infradmz:aptproxy': src: '10.10.0.0/16' dst: 'maint.example.com' 'any/infradmz:puppet': src: '10.10.0.0/16' dst: 'puppet.example.com' # Разрешить веб серверам обращаться к базам данных 'webdmz/dbdmz:testdb': {}
сгенерированный конфиг генератора фильтра (именно так выходит)
Да, есть небольшой процент избыточности, но оптимизацию возможно провести в последующих версиях. Например, нет смысл иметь два правила, одно из которых частных случай другого. В принципе, код генерации уже оброс множеством "интеллектуальных" условий по мере внедрения в жизнь.
Это конфигурация вступает в силу автоматически при cffirehol::enable=true
!
# This file is autogenerated by cffirehol Puppet Module # Any changes made here may be overwritten at any time version 6 # Defaults #---------------- DEFAULT_INTERFACE_POLICY="DROP" DEFAULT_ROUTER_POLICY="DROP" FIREHOL_LOG_MODE="NFLOG" FIREHOL_TRUST_LOOPBACK="0" FIREHOL_DROP_ORPHAN_TCP_ACK_FIN="1" FIREHOL_INPUT_ACTIVATION_POLICY="DROP" FIREHOL_OUTPUT_ACTIVATION_POLICY="DROP" FIREHOL_FORWARD_ACTIVATION_POLICY="DROP" # Custom Services #---------------- server_dns_ports="tcp/53 udp/53" client_dns_ports="any" # Use to open all TCP ports (e.g. for local) server_alltcp_ports="tcp/1:65535" client_alltcp_ports="any" # Use to open all UDP ports (e.g. for local) server_alludp_ports="udp/1:65535" client_alludp_ports="any" # Use to open all TCP and UDP ports (e.g. for local) server_allports_ports="udp/1:65535 tcp/1:65535" client_allports_ports="any" server_cfhttp_ports="tcp/80 tcp/443" client_cfhttp_ports="default" server_testdb_ports="tcp/1234" client_testdb_ports="default" server_cfssh_ports="tcp/22" client_cfssh_ports="default" server_smtp_ports="tcp/25" client_smtp_ports="default" server_cfsmtp_ports="tcp/25 tcp/465 tcp/587" client_cfsmtp_ports="default" server_puppet_ports="tcp/8140" client_puppet_ports="default" # Setup of ipsets #---------------- ipset4 create whitelist4 hash:net ipset6 create whitelist6 hash:net ipset4 create blacklist4 hash:ip ipset4 create blacklist4net hash:net ipset6 create blacklist6net hash:net # note: hardcoded list is not expected to be large ipset4 add whitelist4 "10.0.0.0/8" # Protection on public-facing interfaces #---------------- # main blacklist4 input inface "eth1" ipset:blacklist4net ipset:blacklist4 except src ipset:whitelist4 blacklist6 input inface "eth1" ipset:blacklist6net ipset:blacklist6 except src ipset:whitelist6 iptables -t raw -N cfunroute_main iptables -t raw -A cfunroute_main -s "10.0.0.0/8,172.16.0.0/12,224.0.0.0/4,127.0.0.1/8" -j DROP iptables -t raw -A cfunroute_main -d "10.0.0.0/8,172.16.0.0/12,224.0.0.0/4,127.0.0.1/8" -j DROP iptables -t raw -A PREROUTING -i "eth1" -j cfunroute_main # cfauth: synproxy4 input inface main dst "192.168.1.30/24" dport "22" src "192.168.0.0/16" accept synproxy4 forward inface main dst "192.168.1.40" dport "80" dnat to "10.10.2.10" synproxy4 forward inface main dst "192.168.1.40" dport "443" dnat to "10.10.2.10" iptables -t nat -N cfpost_snat_main iptables -t nat -A cfpost_snat_main -s 192.168.1.30,192.168.1.40 -j RETURN iptables -t nat -A cfpost_snat_main -j SNAT --to-source=192.168.1.30 iptables -t nat -A POSTROUTING -o "eth1" -j cfpost_snat_main # vagrant blacklist4 input inface "eth0" ipset:blacklist4net ipset:blacklist4 except src ipset:whitelist4 blacklist6 input inface "eth0" ipset:blacklist6net ipset:blacklist6 except src ipset:whitelist6 # cfauth: iptables -t nat -A POSTROUTING -o "eth0" -j MASQUERADE # Custom Headers #---------------- # NAT #---------------- dnat4 to "10.10.2.10" inface "eth1" proto "tcp" dport "80" dst "192.168.1.40" dnat4 to "10.10.2.10" inface "eth1" proto "tcp" dport "443" dst "192.168.1.40" # Interfaces #---------------- interface "eth1" "main" policy deny protection bad-packets client icmp accept server4 ping accept with hashlimit ping upto 1/s burst 2 # cfauth: server4 "cfssh" accept src "192.168.0.0/16" # cfsystem: client "http" accept uid "root" # cfsystem: client "https" accept uid "root" # cfsystem: client "ntp" accept uid "root ntpd" # cfsystem: client "cfsmtp" accept uid "root Debian-exim" # cfsystem: client "puppet" accept uid "root" # cfnetwork: client "dns" accept interface "eth0" "vagrant" policy deny protection bad-packets client icmp accept server4 ping accept with hashlimit ping upto 1/s burst 2 # cfauth: server4 "cfssh" accept src "10.0.0.0/8 192.168.0.0/16 172.16.0.0/12" # cfsystem: client "http" accept uid "root" # cfsystem: client "https" accept uid "root" # cfsystem: client "ntp" accept uid "root ntpd" # cfsystem: client "cfsmtp" accept uid "root Debian-exim" # cfsystem: client "puppet" accept uid "root" # cfnetwork: client4 "dns" accept dst "10.10.1.10" interface "eth2" "infradmz" policy reject client icmp accept server icmp accept # cfauth: server4 "cfssh" accept src "10.0.0.0/8" # cfsystem: client "http" accept uid "root" # cfsystem: client "https" accept uid "root" # cfsystem: client "ntp" accept uid "root ntpd" # cfsystem: client "cfsmtp" accept uid "root Debian-exim" # cfsystem: client "puppet" accept uid "root" # cfnetwork: client4 "dns" accept dst "10.10.1.10" interface "eth3" "dbdmz" policy reject client icmp accept server icmp accept # cfauth: server4 "cfssh" accept src "10.0.0.0/8" # cfsystem: client "http" accept uid "root" # cfsystem: client "https" accept uid "root" # cfsystem: client "ntp" accept uid "root ntpd" # cfsystem: client "cfsmtp" accept uid "root Debian-exim" # cfsystem: client "puppet" accept uid "root" interface "eth4" "webdmz" policy reject client icmp accept server icmp accept # cfauth: server4 "cfssh" accept src "10.0.0.0/8" # cfsystem: client "http" accept uid "root" # cfsystem: client "https" accept uid "root" # cfsystem: client "ntp" accept uid "root ntpd" # cfsystem: client "cfsmtp" accept uid "root Debian-exim" # cfsystem: client "puppet" accept uid "root" interface "lo" "local" policy reject client icmp accept server icmp accept # cfauth: server4 "cfssh" accept src "10.0.0.0/8 192.168.0.0/16" # cfsystem: client "http" accept uid "root" # cfsystem: client "https" accept uid "root" # cfsystem: client "ntp" accept uid "root ntpd" # cfsystem: server "smtp" accept # cfsystem: client "smtp" accept # cfsystem: client "cfsmtp" accept uid "root Debian-exim" # cfsystem: client "puppet" accept uid "root" # Routers #---------------- router "main_infradmz" inface "eth1" outface "eth2" policy drop client icmp accept # apt: client4 "cfhttp" accept src "10.10.1.10" client4 "ntp" accept src "10.10.1.10" # puppet: client "cfhttp" accept router "main_webdmz" inface "eth1" outface "eth4" policy drop client icmp accept server4 "cfhttp" accept dst "10.10.2.10" router "vagrant_infradmz" inface "eth0" outface "eth2" policy drop client icmp accept server4 "ntp" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "dns" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "aptproxy" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "puppet" accept dst "10.10.1.11" src "10.10.0.0/8" router "infradmz_infradmz" inface "eth2" outface "eth2" policy reject server icmp accept client icmp accept server4 "ntp" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "dns" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "aptproxy" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "puppet" accept dst "10.10.1.11" src "10.10.0.0/8" router "dbdmz_infradmz" inface "eth3" outface "eth2" policy reject server icmp accept client icmp accept server4 "ntp" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "dns" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "aptproxy" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "puppet" accept dst "10.10.1.11" src "10.10.0.0/8" router "webdmz_infradmz" inface "eth4" outface "eth2" policy reject server icmp accept client icmp accept server4 "ntp" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "dns" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "aptproxy" accept dst "10.10.1.10" src "10.10.0.0/8" server4 "puppet" accept dst "10.10.1.11" src "10.10.0.0/8" router "webdmz_dbdmz" inface "eth4" outface "eth3" policy reject server icmp accept client icmp accept server "testdb" accept
конфигурация сети
Модуль сам не будет пытаться менять настройки сети на лету — это нужно будет делать ручками или рестартом.
# # Generated by cfnetwork::iface puppet module # auto lo iface lo inet loopback source /etc/network/interfaces.d/* # # Generated by cfnetwork::iface puppet module # auto eth3 iface eth3 inet static address 10.10.2.254 netmask 24 up sysctl --ignore net.ipv6.conf.eth3.disable_ipv6=1 # # Generated by cfnetwork::iface puppet module # auto eth2 iface eth2 inet static address 10.10.1.254 netmask 24 up sysctl --ignore net.ipv6.conf.eth2.disable_ipv6=1 # # Generated by cfnetwork::iface puppet module # auto eth1 iface eth1 inet static address 192.168.1.30 netmask 24 gateway 192.168.1.1 dns-nameservers 10.10.1.10 dns-search example.com up ip addr add 192.168.1.40/24 dev eth1 up sysctl --ignore net.ipv6.conf.eth1.disable_ipv6=1 # # Generated by cfnetwork::iface puppet module # auto eth0 iface eth0 inet dhcp netmask 255.255.255.0 up ip route add 10.0.1.1/25 dev eth0 up sysctl --ignore net.ipv6.conf.eth0.disable_ipv6=1 # # Generated by cfnetwork::iface puppet module # auto eth4 iface eth4 inet static address 10.10.2.254 netmask 24 up sysctl --ignore net.ipv6.conf.eth4.disable_ipv6=1
Заключение
Как видно, конфигурация сети и фильтра элементарна, чиста и лаконична, а самое главное удобна для изменений без горы магический чисел.
Пока нет длительной истории в боевом режиме. Обкатка проходит на паре реальных серверов и примерно десятке виртуалок без серьёзной нагрузки. Поэтому интересуют добровольцы, у которых разросся парк систем, а подход к администрированию ещё не успел подстроиться или же не до конца устраивает.
ссылка на оригинал статьи https://habrahabr.ru/post/277085/
Добавить комментарий