Pull to refresh

Простой firewall средствами puppet

Reading time 4 min
Views 6.3K
Обзор и основы установки и настройки puppet уже публиковались Часть 1 и Часть 2 поэтому я не буду на этом останавливаться а сразу перейду к настройке фаервола.

Постановка задачи

В проекте используется некоторое количество серверов, большинство в из которых в облаке. Часть используется для обеспечения инфраструктуры компании, часть обеспечивают публичный сервис клиентам. Необходимо обеспечить централизованное управление доступом.

Список серверов для нашего примера:
  • admin.example.com — логин сервер для адинистраторов и разработчиков, DNS, CRM
  • www.example.com — веб-сервер
  • app.example.com — сервер приложений apache tomcat
  • mysql.example.com — mysql-сервер

Базовая настройка

Для начала создадим конфигурацию базового фаервола, в котором будут закрыты все входящие порты кроме ssh.

class firewall {
    exec { 'minimal-firewall':
	path => ["/bin", "/sbin", "/usr/bin", "/usr/sbin"],
	command => "iptables -P INPUT DROP \
		    && iptables -P FORWARD DROP \
		    && iptables --flush \
		    && iptables -t nat --flush \
		    && iptables --delete-chain \
		    && iptables -P FORWARD DROP \
		    && iptables -P INPUT DROP \
		    && iptables -A INPUT -i lo --source 127.0.0.1 --destination 127.0.0.1 -j ACCEPT \
		    && iptables -A INPUT -m state --state \"ESTABLISHED,RELATED\" -j ACCEPT \
		    && iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT \
		    && iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT \
    		    && iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT \
		    && iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT \
		    && iptables -A INPUT -p tcp --dport ssh -j ACCEPT \
		    && iptables -A INPUT -j LOG -m limit --limit 40/minute \
		    && iptables -A INPUT -j DROP",
    }
}


Здесь нет никакой магии. Правила iptables находятся гуглом по «iptables minimal firewall», подходит первая же ссылка. Единственное отличие: чтобы не плодить сущностей — не создавать отдельного скрипта, все команды объединены в одну оператором &&.

Чтобы применить эту конфигурацию к серверу нужно указать класс в настройке сервера, по терминологии puppet — узла:

node 'www' { 
    include firewall
}

В качестве имени узла можно указывать как FQDN так и короткое имя хоста. По умолчанию puppet-клиент перечитывает конфигурацию каждые тридцать минут, так что вы можете немного подождать, или послать процессу сигнал SIGHUP, или просто перезапустить службу. После этого можно убедиться, что правила фаервола применены: iptables -L.

Открываем входные порты

Тут есть небольшая тонкость. Порядок добавления правил в iptables имеет значение, если мы просто добавим новое правило, разрешающее доступ к порту, то оно никогда не сработает. Поэтому мы будем вставлять новое правило в нужное место. Определить его очень просто: дадим команду iptables -L --line-numbers и ищем строку с упоминанием ssh 7 ACCEPT tcp -- anywhere anywhere tcp dpt:ssh. Значит будем вставлять правило в позиции 7.

define allowport ($protocol, $port) {
    exec { "/sbin/iptables -I INPUT 7 -p $protocol --dport $port -j ACCEPT":
	require => Exec['minimal-firewall'],
    }
}


Тогда конфигурация узла www становится:

node 'www' { 
    allowport { http: protocol => tcp, port => 80, }
    allowport { https: protocol => tcp, port => 443, }
    include firewall
}


Обратите внимание: язык описаний puppet декларативный, порядок выполнения правил не определен. Поэтому на самом деле все равно сначала будет записан 'import firewall' а потом 'allowport' или наоборот. Для нас же важно, чтобы вначале отработало правило настройки минимального фаервола и только потом — открытие порта. Это требование записано в определении allowport — require => Exec['minimal-firewall']

Конфигурация сервера admin:

node 'admin' {
    allowport { dns1: protocol => tcp, port => 53, }
    allowport { dns2: protocol => udp, port => 53, }
    allowport { http: protocol => tcp, port => 80, }
    allowport { webmin: protocol => tcp, port => 10000, }
    include firewall
}


Усложняем правила

Сервер приложений tomcat требуется запускать от непривилегированного пользователя, при этом отвечать от должен на стандартном 443 порту. Поэтому сделаем трансляцию адресов (DNAT) соединения на 443 порт перенаправим на 8443, на котором будет слушать tomcat.

define dnat ($protocol, $from, $to) {
    exec { 'dnat-$protocol-$from-$to':
	command => "/sbin/iptables -t nat -A PREROUTING -d $hostname -p $protocol --dport $from -j DNAT --to-destination $to \
		    && /sbin/iptables -t nat -A OUTPUT -d $hostname -p $protocol --dport $from -j DNAT --to-destination $to ",
	require => Exec['minimal-firewall'],
    }
}


Опишем конфигурацию сервера приложений:

node 'app' {
    allowport { https: protocol => tcp, port => 443, }
    allowport { tomcat: protocol => tcp, port => 8443, }
    dnat { java: protocol => tcp, from => 443, to => ':8443', }
    include firewall
}


Mysql сервер не должен быть доступен извне компании. Разработчики при необходимости обращаются к нему с сервера admin. Ограничим доступ с помощью соответствующего определения:

define dmzport ($protocol, $port) {
    exec { 'dmz-$protocol-$port':
	command => "/sbin/iptables -I INPUT 7 -s admin.example.com -p $protocol --dport $port -j ACCEPT \
		    && /sbin/iptables -I INPUT 7 -s www.example.com -p $protocol --dport $port -j ACCEPT \
		    && /sbin/iptables -I INPUT 7 -s mysql.example.com -p $protocol --dport $port -j ACCEPT \
		    && /sbin/iptables -I INPUT 7 -s app.example.com -p $protocol --dport $port -j ACCEPT",
	require => Exec['minimal-firewall'],
    }
}

Теперь конфигурация узла mysql:

node 'mysql' {
    dmzport { mysql: protocol => tcp, port => 3306, }
    include firewall
}


Таким образом мы получили простой централизованный фаервол для всех серверов компании.
Tags:
Hubs:
+11
Comments 4
Comments Comments 4

Articles