Puppet

Puppet прикольный. Если разворачивать однотипные конфигурации на большом количестве серверов, его использование дает ощутимый профит. Я думаю, что для разруливания основных вопросов вполне можно обойтись без скриптописания на ruby. Во всяком случае я пока не нашел для чего бы мне понадобилось написание пупет скриптов. Хотя… если их кто то пишет  — значит это кому то нужно :).
Но это жутко опасная штука. Очень опасная.

Лопата в руках + puppet (iptables)

После настройки DNS надо подумать о настройке fierwall на серверах.

Точно так же, как и с DNS, существуют модули puppet, предназначенные для управления fierwall. Исходя из соображения, зачем использовать сторонние обёртки, когда iptables сам по себе крут и админ должен знать его на Ять, я не буду использовать эти модули.

Что будем делать:

  • Удалим firewalld, поставим классический iptables-service.
  • Создадим файл с конфигурацией фаервола, в стиле /etc/sysconfig/iptables.
  • На клиенте запустим скрипт, который применит правила fierwall.

На puppet сервере, создадим файл с конфигурацией firewall для машины с dns:
# cd /var/puppet/centos7-2.test.local/etc/
# mkdir sysconfig
# vim sysconfig/iptables
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state —state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state —state NEW -m tcp —dport 22 -j ACCEPT
-A INPUT -p udp -m state —state NEW -m udp —dport 53 -j ACCEPT
-A INPUT -p tcp -m state —state NEW -m tcp —dport 53 -j ACCEPT
COMMIT

В файле site.pp добавляем новый класс.
class minimal-iptables {
# Удаляем firewalld
package { ‘firewalld’:
ensure => absent,
  before => Package[‘iptables-services‘]
}
# Перед удалением firewalld останавливаем соответствующий
# сервис и делаем ему disable.
service { ‘firewalld’:
name => «firewalld»,
enable => false,
ensure => stopped,
before => Package[‘firewalld’]
}
# Устанавливаем iptables-services
package { ‘iptables-services’ :
ensure => installed
}
service { ‘iptables’:
  enable => true,
ensure => running,
  require => Package[«iptables-services»]
 }
# Определяем файл, в котором будем описывать
# правила firewall
file { ‘/etc/sysconfig/iptables’:
ensure => file,
mode => ‘0600’,
owner => ‘root’,
# Для каждой машины предполагается свой файл
source => «puppet:///configs/$fqdn/etc/sysconfig/iptables»,
require => Package[«iptables-services»]
}
# Описываем программу, которая будет выполняться
# на машине для поднятия правил fierwall
exec { ‘firewall’:
# «Подписываемся» на файл
subscribe => File[«/etc/sysconfig/iptables»],
# Устанавливаем правила фаервола из файла.
command => «/usr/bin/cat /etc/sysconfig/iptables | /usr/sbin/iptables-restore «,
# Говорим, что программа будет выполняться
# только если файл, на который мы подписаны,
# изменился.
refreshonly => true
}
}

Вносим изменения в node.
node ‘centos7-2.test.local’ {
include ‘baselinux’
include ‘minimal-iptables’
include ‘dns’
}

Проверяем файл манифеста.
# puppet parser validate /etc/puppet/manifests/site.pp

У данного способа есть недостаток — при исполнении скрипта будут рваться сессии. Это можно обойти, используя вместо файла /etc/sysconfig/iptables самописанный скрипт.

Лопата в руках + puppet (bind)

На виртуалке centos7-2 будем поднимать DNS сервер.

Я долго думал, использовать ли для управления DNS сервером скрипты из PuppetForge (https://forge.puppetlabs.com/)? Или держать на puppet сервере файлы конфигурации bind и рулить удаленным сервером изменяя эти файлы.

Победил второй путь. Я прекрасно умею управлять DNS сервером при помощи его конфигурационых файлов и файлов зон. Учить дополнительную обертку, написанную сторонними компаниями, на неизвестном мне ruby… Зачем так усложнять?

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

Создадим директорию /var/puppet.
# mkdir /var/puppet

В которой будут директории, содержащие конфигурационные файлы для отдельных машин.
# mkdir /var/puppet/centos7-2.test.local
# cd /var/puppet/centos7-2.test.local
# mkdir etc
# mkdir -p var/named/slaves

В новой директории etc разместим файл named.conf. Возьмем стандартный файл и внесем в него некоторые изменения. Не буду приводить тут весь файл, только измененные и добавленные строки.
listen-on port 53 { any; };
listen-on-v6 port 53 { none; };
allow-query     { any; };
dnssec-enable no;
dnssec-validation no;
zone «test.local» IN {
type master;
file «test.local»;
allow-update { none; };
allow-query { any; };
allow-transfer { none; };
};
zone «200.168.192.in-addr.arpa» IN {
type master;
file «200.168.192»;
allow-update { none; };
allow-query { any; };
allow-transfer { none; };
};

В директории var/named создадим два файла описания зон
# cat var/named/test.local 
$TTL 86400 ; 1 day
test.local. IN SOA post.test.local. artur.test.local. (
2015091701 ; serial
10800      ; refresh (3 hours)
3600       ; retry (1 hour)
604800     ; expire (1 week)
86400      ; minimum (1 day)
)
NS centos7-2.test.local.
MX 10 post.test.local.
TXT «v=spf1 mx -all»
SPF «v=spf1 mx -all»

artur-office A 192.168.200.1
post A 192.168.200.97
centos7-2 A 192.168.200.98
centos7-1 A 192.168.200.99

# cat var/named/200.168.192 
$TTL 86400 ; 1 day
200.168.192.in-addr.arpa. IN SOA post.test.local. artur.test.local. (
2015091701 ; serial
10800      ; refresh (3 hours)
3600       ; retry (1 hour)
604800     ; expire (1 week)
86400      ; minimum (1 day)
)
NS centos7-2.test.local.

99 IN PTR centos7-1.test.local.
98 IN PTR centos7-2.test.local.
97 IN PTR post.test.local.
1 IN PTR artur-office.test.local.

Проверкой синтаксиса мы должны заниматься сами. Поэтому:
# named-checkconf etc/named.conf
# named-checkzone test.local var/named/test.local 
zone test.local/IN: loaded serial 2015091701
OK
 # named-checkzone 200.168.192.in-addr.arpa var/named/200.168.192 
zone 200.168.192.in-addr.arpa/IN: loaded serial 2015091701
OK

Теперь необходимо что бы файловый сервер puppet узнал про нашу директорию с файлами.
# vim /etc/puppet/fileserver.conf
[configs]
        path /var/puppet
        allow *

Перезапустим сервер:
# systemctl restart puppetmaster

Внесем изменения в файл манифест site.pp. Сначала добавим новый класс.
class dns {
# для работы необходим пакет bind
    package { ‘bind’:
                ensure => installed
    }
    include ‘stdlib’
    # Отключим ipv6 у bind.
    # Для этого надо при запуске демона передать ему
    # параметр -4. Добавим соответствующую строку в
    # конфигурационный файл.
    file_line { ‘named_no_ipv6’:
        path => ‘/etc/sysconfig/named’,
        line => ‘OPTIONS=»-4″‘
    }
    # Конфигурационный файл демона
    file { «/etc/named.conf» :
        ensure => file,
        mode => ‘0640’,
# Путь к файлу на файловом сервере
# Я специально присвоил директории имя такое
        # же как и имя машины.
# Теперь можно пользоваться переменной $fqdn
# И если у нас появится другой DNS сервер,
        # то благодаря этой переменной
# мы спокойно разрулим кому какие конфиги
        # отдавать.
        source => «puppet:///configs/$fqdn/etc/named.conf»,
        require => Package[«bind»],
# После изменения файла сделаем reload 
        # серверу bind
        notify  => Service[«named»]
    }
    file { «/var/named» :
        path => «/var/named»,
        mode => ‘0640’,
        group => ‘named’,
        source => «puppet:///configs/$fqdn/var/named»,
# Тут у нас передается содержимое директории
        ensure => directory,
# Говорим, что бы передавались все файлы и
        # поддиректории.
# Вместо remote можно использовать true. 
        # Тогда в целевой директории будут удаляться
        # все файлы, которые отсутствуют в директории
        # шаблоне.
# Поскольку делал по быстрому, пока оставлю
        # так. Но в дальнейшем надо будет привести 
        # все в порядок.
        recurse => remote,
        sourceselect => all,
        require => Package[«bind»],
        notify  => Service[«named»]
    }
    service { ‘named’:
        enable => true,
        ensure => running,
        require => Package[«bind»]
    }
}

Ну и изменим соответствующую ноду.
node ‘centos7-2.test.local’ {
    include ‘baselinux’
    include ‘dns’
}

Пока остановимся. Останется только разрулить правила фаервола.

Лопата в руках + puppet (2)

Посидел, подумал и понял, что ставить пакет ntp только ради его конфигурационного файла… Что то тут не правильно. Тем более, что ntpdate.service из конфигурационного файла берет только имена ntp серверов, осуществляя поиск параметра peer или server.

Поэтому, для ntpdate.service можно оставить простейший файл /etc/ntp.conf следующего содержания:
server 0.centos.pool.ntp.org
server 1.centos.pool.ntp.org
server 2.centos.pool.ntp.org
server 3.centos.pool.ntp.org

Итого, нам надо удалить пакет ntp и добавить свой конфигурационный файл /etc/ntp.conf

Удалить пакет можно следующим образом:
package { ‘ntp’:
ensure => absent
}

Будет удален пакет, его конфигурационные файлы останутся на месте. Нам не надо удалять конфиги. Мы просто поменяем их содержимое.

Дальше надо будет добавить свой конфигурационный файл, убедившись, что пакет ntp не установлен, а ntpdate наоборот установлен.

Изменим класс ntpdate в файле манифеста следующим образом:
class ntpdate {
        package { ‘ntpdate’:
                ensure => installed
        }
        package { ‘ntp’:
                ensure => absent,
# Пакет, если он есть, надо удалить до того как
# будет создан файл /etc/ntp.conf
before => File[‘/etc/ntp.conf’]
        }
        # Определяем конфигурационный файл
        file { ‘/etc/ntp.conf’:
                ensure => file,
                owner  => ‘root’,
                group  => ‘root’,
                mode   => ‘0644’,
# Для создания файла необходимо наличие
# установленного пакета ntpdate
                require => Package[«ntpdate»],
                # Содержимое файла указываем прямо тут
                content => «server 0.centos.pool.ntp.org
server 1.centos.pool.ntp.org
server 2.centos.pool.ntp.org
server 3.centos.pool.ntp.org
«
        }
        service { ‘ntpdate’:
                enable => true,
                ensure => running,
                require => Package[«ntpdate»],
        }
}

Проверим конфиг на ошибки:
# puppet parser validate /etc/puppet/manifests/site.pp

В результате на клиенте:

  • если установлен пакет ntp, он будет удаляться.
  • будет добавлен или изменен файл /etc/ntp.conf

Если в дальнейшем необходимо изменить конфигурационный файл, меняем его содержимое в файле манифеста.

Если конфигурационные файлы большие, включаем файловый сервер puppet или ftp сервер или … Размещаем конфиги там. Но об этом в другой раз. 

Лопата в руках + puppet

Побегал по собеседованиям и c удивлением обнаружил, что многим работодателям требуется знания по puppet. Решил за одно с разборками по CentOS 7 научиться пользоваться этим чудом автоматизации конфигурации :).

Пока создал две виртуалки:

  • centos7-1.test.local — сервер с puppet сервером на борту. На нем мы будем конфигурировать клиент.
  • centos7-2.test.local — собственно клиент, который будет все данные по своей конфигурации брать с сервера.

При установке клиента и сервера была сразу настроена сеть. DNS сервер пока не поднимался, поэтому в файле /etc/hosts на обеих машинах добавлены строки:
192.168.200.99 centos7-1.test.local  centos7-1
192.168.200.98 centos7-2.test.local  centos7-2

Маршрут по умолчанию на сервере, через хост машину. Маршрут по умолчанию на клиенте, через сервер.
Fierwall на сервере настроен. Удален fierwalld, настроен маскарадинг и доступ по ssh. На клиенте все по умолчанию.
При установке клиента выбрана минимальная конфигурация сервера.

Puppet будем конфигурить исходя из того, что у нас везде CentOS 7. Т.е. пока не будем заморачиваться с многоплатформенностью.

Установка puppet на сервер.

# rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-7.noarch.rpm
# yum install puppet-server

Поскольку лениво руками возиться c сертификатами, включим автоматическое подписывание сертификатов. В реальной жизни так делать низзя.
# vim /etc/puppet/puppet.conf
[main]
    …
    autosign = true

Создадим манифест файл заглушку. По мере понимания системы, будем его расширять.
# vim /etc/puppet/manifests/site.pp
package {
  ‘mc’:
    ensure => installed;
}
Собственно тут говориться, что надо поставить пакет mc, если его еще нет.

Запускаем сервер:

# systemctl start puppetmaster.service
# systemctl enable puppetmaster.service
# systemctl status puppetmaster

Проверяем наличие открытого порта 8140 в fierwall:
# iptables -L INPUT -n —line-numbers

Если порт не открыт, добавляем:
# iptables -I INPUT 5 -p tcp -m state —state NEW -m tcp —dport 8140 -j ACCEPT
# service iptables save

Установка puppet клиента на centos7-2.

# rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-7.noarch.rpm
# yum install puppet

Указываем сервер, куда буем ходить:
# vim /etc/puppet/puppet.conf
[agent]
    server=centos7-1.test.local
    certname=centos7-2-cl.test.local

Подключаемся агентом, заодно генерируя и подписывая сертификаты:
# puppet agent —test 

Если ошибок не показало, запускаем сервис:
# systemctl start puppet.service
# systemctl enable puppet.service
# systemctl status puppet

Кстати, пакет mc уже установился, программой можно пользоваться.

Переходим на сервер.

На всякий пожарный ставим средство для работы с конфигами. Чувствую, что оно нам пригодится.
# yum install augeas

Ставим библиотеку, необходимую для работы со строками.
# puppet module install puppetlabs-stdlib

Создадим манифест файл puppet, в котором опишем пакеты и параметры, которые будут использованы на всех серверах.

Мне понадобятся: ssh, ntp, mc, vim (мой любимый редактор), net-tools (ip ставиться по умолчанию, но я привык к стандартному ifconfig и route), traceroute, tzdata (с обязательным обновлением до последней версии, а вдруг, не дай бог, опять дадут порулить «Повелителю Времени»?).
Так же я люблю кастомную строку приглашения.

Итого файл будет выглядеть следующим образом:
# vim /etc/puppet/manifests/site.pp

class sshd {
    # долен быть установлен пакет
    package { ‘openssh-server’:
ensure => latest
    }
    # сервис должен быть запущен
    service { ‘sshd’:
name => «sshd»,
enable => true,
ensure => running
    }
}

# Сервер ntpd поднимать не будем, ограничимся ntpdate, благо в
# CentOS 7 проверку можно запускать как сервис.
# для ее работы потребуется конфигурационный файл /etc/ntpd.conf
# который находится в пакете ntp
class ntpdate {
    # ставим пакет ntp. Ntpdate будет поставлен по зависимости.
    package { ‘ntp’:
ensure => installed
    }
    # запускаем соотвествующий сервис.
    service { ‘ntpdate’:
enable => true,
ensure => running
    }
}

# создаем базовый класс, для всех машин
class baselinux {
    package { ‘mc’: ensure => installed }
    package { ‘vim-enhanced’: ensure => installed }
    package { ‘net-tools’: ensure => installed }
    package { ‘traceroute’: ensure => installed }
    package { ‘tzdata’: ensure => latest }
    include ‘sshd’
    include ‘ntpdate’
    # необходимо для работы file_line
    include ‘stdlib’
    # Добавляем переменную PS1 в конец /etc/bashrc, если ее в этом файле еще нет.
    # Точнее говоря, PS1 там уже есть, мы контролируем наличие именно такой строки
    # и если ее нет, то добавляем в конец файла.
    file_line { ‘ps1_rule’:
        path => ‘/etc/bashrc’,
        # line пишем одной строкой
        line => ‘PS1='[e[44;36m]t:[w][e[0;0m]n[e[0;31;04m]u[e[0;0m]@[e[0;32m]h[e[0;0m] $ »
    }
}
# node по умолчанию, используется в том случае, если нет явного
# нода для конкретной машины.
node default {
    include ‘baselinux’
}

# node для клиента.
node ‘centos7-2.test.local’ {
    include ‘baselinux’
}

Node для клиента пока идентичен ноду по умолчанию. Но я буду его потихоньку добавлять.

Если лениво ждать пока на клиенте включатся наши изменения, на нем можно запустить:
# puppet agent —test 

На клиенте будут установлены все необходимые пакеты и запустятся (если ещё не были запущены) указанные нами сервисы.

Пока как то так.