Привет всем,
Продолжаем практиковаться с puppet, и в рамках этой заметки дополним нашу инфраструктуру новыми компонентами - PuppetDB и Dashboard.
Настройка PuppetDB
PuppetDB - по сути это отдельный модуль, представляющий из себя хранилище puppet. Все данные помещаются в базу на postgresql. И в дополнение после внедрения puppetdb, нам открывается возможность взаимодействия посредством API.
В базе хранятся данные:
- Последние факты по всем узлам;
- Последний католог каждого узла;
- Ну и опционально, есть возможность хранения отчетов по каждому хосту.
Установка PostgreSQL
Для начала настроим postgresql, и создадим новую базу данных. В документации рекомендуют устанавливать 11 версию postgresql или выше.
Подключаемся на сервер, и выбираем оптимальную версию =)
[root@puppetdb ~]# yum module list postgresql
Last metadata expiration check: 2:04:08 ago on Fri 10 Nov 2023 03:51:06 PM +06.
AlmaLinux 8 - AppStream
Name Stream Profiles Summary
postgresql 9.6 client, server [d] PostgreSQL server and client module
postgresql 10 [d] client, server [d] PostgreSQL server and client module
postgresql 12 client, server [d] PostgreSQL server and client module
postgresql 13 client, server [d] PostgreSQL server and client module
postgresql 15 client, server [d] PostgreSQL server and client module
Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled
Как видно по умолчанию доступна 10я версия. Но мы поставим последнюю доступною версию из коробки.
[root@puppetdb ~]# yum module enable postgresql:15 -y
Ну и теперь просто ставим пакет postgresql:
[root@puppetdb ~]# yum install postgresql-server postgresql-contrib -y
Далее проинициализируем новый инстанс:
[root@puppetdb ~]# /usr/bin/postgresql-setup --initdb
* Initializing database in '/var/lib/pgsql/data'
* Initialized, logs are in /var/lib/pgsql/initdb_postgresql.log
Конфигурация PostgreSQL
Сразу же подредактируем конфигурацию базы. Запускать инстанс будем на локалхосте:
[root@puppetdb ~]# vim /var/lib/pgsql/data/postgresql.conf
---
listen_addresses = 'localhost'
port = 5432
И разрешим подключаться к нашей базе в pg_hba
:
[root@puppetdb ~]# vim /var/lib/pgsql/data/pg_hba.conf
---
# TYPE DATABASE USER ADDRESS METHOD
host puppetdb puppetdb 127.0.0.1/32 scram-sha-256
Запускаем сервис:
[root@puppetdb ~]# systemctl enable --now postgresql
Created symlink /etc/systemd/system/multi-user.target.wants/postgresql.service → /usr/lib/systemd/system/postgresql.service.
Подключаемся к базе для создания новой бд и пользователя.
[root@puppetdb ~]# sudo -u postgres psql
could not change directory to "/root": Permission denied
psql (15.3)
Type "help" for help.
postgres=#
Меняем алгоритм шифрования паролей:
postgres=# set password_encryption = "scram-sha-256";
SET
Создаем нового пользователя и базу:
[root@puppetdb ~]# sudo -u postgres createuser -DRSP puppetdb
Enter password for new role: *******
Enter it again: *******
[root@puppetdb ~]# sudo -u postgres createdb -E UTF8 -O puppetdb puppetdb
Для проверки корректности доступа к базе, попробуем залогинится из под созданного пользователя:
[root@puppetdb ~]# psql -h 127.0.0.1 -U puppetdb -d puppetdb -W
Password: *******
psql (15.3)
Type "help" for help.
puppetdb=>
Далее исходя из рекомендаций нужно включить расширение pg_trgm
, которое требуется для увеличения производительности запросов, где используется регулярные выражения.
[root@puppetdb ~]# sudo -u postgres psql puppetdb
psql (15.3)
Type "help" for help.
puppetdb=# create extension pg_trgm;
CREATE EXTENSION
puppetdb=# \q
Ставим puppet агента
Теперь займемся инсталяцией puppetdb. Сначала монтируем репозиторий puppet, для последующей установки агента.
[root@puppetdb ~]# yum install -y https://yum.puppetlabs.com/puppet8-release-el-8.noarch.rpm
[root@puppetdb ~]# yum install puppet-agent -y
Устанавливаем связь с паппет-серверов. Обновляем конфигурацию агента, и подписываем сертификат нового сервера. Обновляем конфигу:
[root@puppetdb ~]# vi /etc/puppetlabs/puppet/puppet.conf
---
[main]
server = puppetmaster.nixhub.ru
certname = puppetdb.nixhub.ru
runinterval = 30
Далее просто запускаем агента:
[root@puppetdb ~]# /opt/puppetlabs/bin/puppet resource service puppet ensure=running enable=true
Notice: /Service[puppet]/ensure: ensure changed 'stopped' to 'running'
service { 'puppet':
ensure => 'running',
enable => 'true',
provider => 'systemd',
}
Идем на puppet-server, и подписываем сертификат для данного новой ноды:
[root@puppetmaster ~]# puppetserver ca sign --certname puppetdb.nixhub.ru
Successfully signed certificate request for puppetdb.nixhub.ru
Конфигурация puppetdb
Устанавливаем puppetdb, через агента:
[root@puppetdb ~]# /opt/puppetlabs/bin/puppet resource package puppetdb ensure=latest
Notice: /Package[puppetdb]/ensure: created
package { 'puppetdb':
ensure => '8.2.0-1.el8',
provider => 'dnf',
}
После установки сервиса, нужно внести изменения в его конфигурацию.
Первостепенно подключить его к ранее настроенному инстансу postgresql. Редактируем конфигурационный файл puppetdb:
[root@puppetdb ~]# vim /etc/puppetlabs/puppetdb/conf.d/database.ini
---
[database]
# The database address, i.e. //HOST:PORT/DATABASE_NAME
subname = //localhost:5432/puppetdb
# Connect as a specific user
username = puppetdb
# Use a specific password
password = pwdpwd
# How often (in minutes) to compact the database
# gc-interval = 60
Тут раскоментируем поля subname
, username
, password
. Значение в поле subname
остается дефолтным, в случаи если у вашей базы сетевой сокет или имя базы отличается, то нужно будет подправить.
В username
, password
указываем креды созданного пользователя для подключения к базе.
Также можно заглянуть в настройки вебсервера тут: /etc/puppetlabs/puppetdb/conf.d/jetty.ini
.
По умолчанию, он настроен на 8081 ssl порту с самозаверенными сертификатами.
Запускаем утилуту, которая подготовит сертификаты:
[root@puppetdb ~]# puppetdb ssl-setup
Не забываем на фаере открыть порт:
[root@puppetdb ~]# firewall-cmd --add-port=8081/tcp --permanent
[root@puppetdb ~]# firewall-cmd --reload
Ну и запускаем сервис:
[root@puppetdb ~]# /opt/puppetlabs/bin/puppet resource service puppetdb ensure=running enable=true
Notice: /Service[puppetdb]/enable: enable changed 'false' to 'true'
service { 'puppetdb':
ensure => 'running',
enable => 'true',
provider => 'systemd',
}
Подключение к puppetdb
Ну и последним этапом этой задачи остается прикрутить сервер puppet к puppetdb. Вновь подключаемся к серверу puppetmaster и ставим плагин puppetdb-termini
. Данный плагин может быть установлем через стандарный пакентый менеджер, либо через puppet.
[root@puppetmaster ~]# yum install puppetdb-termini
Редактируем конфиг паппет сервера.
[root@puppetmaster ~]# vim /etc/puppetlabs/puppet/puppet.conf
---
[master]
...
storeconfigs = true
storeconfigs_backend = puppetdb
В контекст master
добавляем директивы storeconfigs
, storeconfigs_backend
.
storeconfigs
- включает сохранение данных по каждому клиенту.storeconfigs_backend
- тут указывается бекенд, который будет выступать хранилищем.
Создадим еще один конфиг, в котором будут содержатся настройки подключения к puppetdb:
[root@puppetmaster ~]# vim /etc/puppetlabs/puppet/puppetdb.conf
---
[main]
server_urls = http://puppetdb.nixhub.ru:8081/
В значении server_urls
указывается мой поднятый адресс сервера puppetdb. Как временным решением добавил в /etc/hosts
адрес и ip-сервера.
Ну и последним этапом добавляем маршрут:
[root@puppetmaster ~]# vim /etc/puppetlabs/puppet/routes.yaml
---
master:
facts:
terminus: puppetdb
cache: yaml
На стороне любого хоста с агентом запускаем, проверку агента в дебаг моде:
[root@puppetmaster ~]# /opt/puppetlabs/bin/puppet agent -t
Info: Using environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Caching catalog for puppetmaster.nixhub.ru
Info: Applying configuration version '1699798752'
Notice: /Stage[main]/Zbx_agent::Config/Exec[firewalld]/returns: executed successfully (corrective)
Notice: Applied catalog in 1.38 seconds
Если на этом этапе никаких ошибок не было, значит настройки корректны.
Теперь если в браузере обратимся к серверу по ip и порту, то откроется небольшая дэшка. В ней же мы можем увидеть, колличество активных нод.
Наверное можно отнести к значительному минусу, что у puppetdb никак не защищен доступ к API (За исключением tls). И чтобы этот косяк закрыть, можно на уровне OS разрешить доступ только для мастера, или же использовать какой нибудь прокси с авторизацией.
Закрываем API за прокси
По причине вышеупомянутой проблемы, пойдем по второму пути и настроим Nginx. Вебсервер возьмет на себя создание tls-сессий, ограниение по ip. Ну и в дополнение, установим базовую авторизацию.
Включаем модуль с последней актуальной версией nginx,
[root@puppetdb ~]# yum module reset nginx
[root@puppetdb ~]# yum module enable nginx:1.22 -y
[root@puppetdb ~]# yum install nginx -y
Далее я привел к такому виду основной файл - nginx.conf
:
[root@puppetdb ~]# vim /etc/nginx/nginx.conf
---
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
}
Создадим каталог, куда будет складывать сертификаты в последующем.
[root@puppetdb ~]# mkdir /etc/nginx/ssl
И теперь для того что бы не создавать новые серты, создадим сим.линк на сертификаты puppetdb.
[root@puppetdb ~]# ln -s /etc/puppetlabs/puppetdb/ssl/public.pem /etc/nginx/ssl/public.pem
[root@puppetdb ~]# ln -s /etc/puppetlabs/puppetdb/ssl/private.pem /etc/nginx/ssl/private.pem
На линках меняем владельца/группу:
[root@puppetdb ~]# chown -R nginx:nginx /etc/nginx/ssl/*.pem
Далее пишем конфигурацию прокси сервера для puppetdb:
[root@puppetdb ~]# vim /etc/nginx/conf.d/puppetdb.conf
---
server {
listen 80;
server_name puppetdb.nixhub.ru;
location / {
return 301 https://puppetdb.nixhub.ru$request_uri;
}
}
server {
listen 443 ssl;
server_name puppetdb.nixhub.ru;
ssl_certificate /etc/nginx/ssl/public.pem;
ssl_certificate_key /etc/nginx/ssl/private.pem;
# Log paths;
access_log /var/log/nginx/puppetdb-access.log;
error_log /var/log/nginx/puppetdb-erroe.log;
# Set puppet master ip;
set $puppetmaster 10.8.5.111;
# Restricted by ip;
allow 10.8.5.111;
allow 10.8.5.6;
deny all;
location / {
if ($remote_addr = $puppetmaster) {
set $auth off;
}
if ($remote_addr != $puppetmaster) {
set $auth "HTTP Auth";
}
auth_basic $auth;
auth_basic_user_file .htpasswd;
proxy_pass http://localhost:8080;
}
}
Набросал базовый конфиг, который состоит из двух серверов. Первый запущенный на 80 порту, просто служит редиректом, который перебрасывает запросы на 443 ssl порт.
Второй сервер, который слушает 443 порт, разграничивает доступ по ip-адресам. В данном случаи я разрешаю доступ только со своего компа, и сервера puppet, остальным отказываю в доступе.
# Restricted by ip;
allow 10.8.5.111;
allow 10.8.5.6;
deny all;
Затем в контексте location
, проверяю является ли удаленный адрес клиента - адресом puppet сервера. Если является до nginx спроксирует запрос в бекенд. Иначе, удаленный клиент должен будет пройти авторизацию.
Для авторизации нужно сгенерить файлик с паролями. Ставим пакет httpd-tools
для создания файла с паролями.
[root@puppetdb ~]# yum install httpd-tools
Ну и создаем файлик .htpasswd
, попутно добавив пользователя:
[root@puppetdb ~]# htpasswd -c /etc/nginx/.htpasswd admin
New password: ******
Re-type new password: ******
Adding password for user admin
Наш прокси будет ссылаться на 8080 http порт запушенный локально, поэтому перенастроим pupetdb.
# Раскомментировать эти директивы
host = 127.0.0.1
port = 8080
# Закоментировать эти директивы
# ssl-host = 0.0.0.0
# ssl-port = 8081
Ну и для применения настроек, перезапустить сервис:
[root@puppetdb ~]# systemctl restart puppetdb
Не забываем открыть порт на фаере,
[root@puppetdb ~]# firewall-cmd --add-port={80,443)/tcp --permanent
[root@puppetdb ~]# firewall-cmd --reload
Запускаем сервис вебсервера:
[root@puppetdb ~]# systemctl enable --now nginx
Так как порт к API был изменен, подключаемся на puppet-master, и меняем конфигурацию:
[root@puppetmaster ~]# vim /etc/puppetlabs/puppet/puppetdb.conf
---
[main]
server_urls = https://puppetdb.nixhub.ru/
Перезапускаем сервис паппета на основном сервере, и проверяем работу агента:
[root@puppetmaster ~]# systemctl restart puppetserver
Запускаем тест агентом:
[root@puppetmaster ~]# /opt/puppetlabs/bin/puppet agent -t
Info: Using environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Caching catalog for puppetmaster.nixhub.ru
Info: Applying configuration version '1699867114'
Notice: /Stage[main]/Zbx_agent::Config/Exec[firewalld]/returns: executed successfully (corrective)
Notice: Applied catalog in 1.47 seconds
Отлично все работает.
И по итогу, полный доступ без проверки пароля имеет только puppet мастер. Доступ по паролю имеют клиенты, которые добавлены в список разрешенных ip. Всем остальным в доступе будет отказано.
Настройка Puppet Dashboard
В этой главе давайте прикрутим UI Dashboard для puppetdb. По сути, это дешка дает нам возможность только для просмотра данных по хостам.
Сам сервис написан на python c использованием flask фреймворка. И для запуска проекта будем использовать uwsgi в связке уже с установленным nginx.
Ставим python, uwsgi
На основании требований в сервисе ставим python3.8:
[root@puppetdb ~]# yum install python38 python38-devel gcc
Через пакентный менеджер питона, устанавливаем uwsgi и пакет puppetboard.
[root@puppetdb ~]# pip3.8 install uwsgi puppetboard
Настраиваем puppetboard
Далее создадим вебкаталог, в котором будет лежать файлик с настройками puppetboard и конфигурация wsgi:
[root@puppetdb ~]# mkdir -p /var/www/puppetboard
Из пакета puppetboard копируем семпл с настройками сервиса.
[root@puppetdb ~]# cp -v /usr/local/lib/python3.8/site-packages/puppetboard/default_settings.py /var/www/puppetboard/settings.py
'/usr/local/lib/python3.8/site-packages/puppetboard/default_settings.py' -> '/var/www/puppetboard/settings.py'
В последующем все настройки сервиса выполнятся тут. Но на данном этапе дефолтной конфигурации достаточно для запуска сервиса. Требуется добавить секретный ключ.
/var/www/puppetboard/settings.py
Генерим новый секретный ключ для flask:
[root@puppetdb ~]# python3.8 -c 'import secrets; print(secrets.token_hex())'
f09175100257f83097254e934a06de814f8e0ae581aef08e49f4a540d8153d1d
Полученную строку вставляем в значение переменной SECRET_KEY
:
[root@puppetdb ~]# vim /var/www/puppetboard/settings.py
---
SECRET_KEY = 'f09175100257f83097254e934a06de814f8e0ae581aef08e49f4a540d8153d1d' # nosec
Пишем скриптец для запуска wsgi:
[root@puppetdb ~]# vim /var/www/puppetboard/wsgi.py
---
from __future__ import absolute_import
import os
# Needed if a settings.py file exists
os.environ['PUPPETBOARD_SETTINGS'] = '/var/www/puppetboard/settings.py'
from puppetboard.app import app as application
Теперь можно протестировать работу дешбоарда:
[root@puppetdb ~]# uwsgi --socket :8081 --wsgi-file /var/www/puppetboard/wsgi.py --enable-threads --protocol http
Сервис должен запуститься на порту 8081, в браузере обратимся к серверу:
Из скриншота можете заметить, что хосты в статусе unreported
. Видимо при настройки подключения puppet к puppetdb, я заигнорил этот поинт. Что бы исправить это, подключаемся к серверу puppetmaster и изменяем его конфиг:
[root@puppetmaster ~]# vim /etc/puppetlabs/puppet/puppet.conf
---
[master]
dns_alt_names = puppetmaster,puppetmaster.nixhub.ru
storeconfigs = true
storeconfigs_backend = puppetdb
reports = store,puppetdb # <--- Добавил
В секцию master
, добавляем поле reports
со значением store,puppetdb
.
И перезапускаем сервис:
[root@puppetmaster ~]# systemctl restart puppetserver
Запускаем puppetboard
В заключении остается только прикрутить сервис для запуска uwsgi
. Пишем новый systemd-юнит:
[root@puppetdb ~]# vim /etc/systemd/system/uwsgi.service
---
[Unit]
Description=uWSGI Puppetboard service
After=syslog.target
[Service]
ExecStart=/usr/local/bin/uwsgi --socket :8081 --wsgi-file /var/www/puppetboard/wsgi.py --enable-threads --protocol http
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all
[Install]
WantedBy=multi-user.target
Перечитываем конфигурацию systemd, и запускаем сервис с uwsgi:
[root@puppetdb ~]# systemctl daemon-reload
[root@puppetdb ~]# systemctl enable --now uwsgi
Проверяем запустился ли сервис:
[root@puppetdb ~]# systemctl status uwsgi
● uwsgi.service - uWSGI Puppetboard service
Loaded: loaded (/etc/systemd/system/uwsgi.service; enabled; vendor preset: disabled)
Active: active (running) since Mon 2023-11-13 20:41:20 +06; 4s ago
Main PID: 678428 (uwsgi)
Status: "uWSGI is ready"
Tasks: 1 (limit: 23149)
Memory: 26.7M
CGroup: /system.slice/uwsgi.service
└─678428 /usr/local/bin/uwsgi --socket :8081 --wsgi-file /var/www/puppetboard/wsgi.py --enable-threads --protocol http
Копируем конфиг nginx из прошлой инсталяции, вносим изменения в него:
[root@puppetdb ~]# vim /etc/nginx/conf.d/puppetboard.conf
---
[root@puppetdb ~]# cat /etc/nginx/conf.d/puppetboard.conf
server {
listen 80;
server_name puppetboard.nixhub.ru;
location / {
return 301 https://puppetdb.nixhub.ru$request_uri;
}
}
server {
listen 443 ssl;
server_name puppetboard.nixhub.ru;
ssl_certificate /etc/nginx/ssl/public.pem;
ssl_certificate_key /etc/nginx/ssl/private.pem;
# Log paths;
access_log /var/log/nginx/puppetboard-access.log;
error_log /var/log/nginx/puppetboard-erroe.log;
location / {
auth_basic $auth;
auth_basic_user_file .htpasswd;
proxy_pass http://localhost:8081;
}
}
Аналогично базовый конфиг, в котором мы поменяли только имя сервера и порт на сервис бекенда. По умолчанию, в puppetboard также нет никакой авторизации и поэтому в этом примере вставляем базовую аутентификацию.
В /etc/hosts
добавляем запись с именем сервера и перезапускаем nginx. В браузере открываем страничку уже по новому имени.
На этом все, мы посмотрели два сервиса, с помошью которых можем затюнить нашу puppet инфру. Стоит отметить, что в этой заметке установка puppetboard производилась из под пользователя root и вне python окружения. Также я не раскрыл тему настройки selinux и firewall под этот сетап. Для реализации этой темы в проде, стоит также проработать данные вопросы.