Кукловодим через OpenVox. Часть 2. Настройка клиентов
В первой части мы подняли сервер управления и научили его раскатывать код из git через r10k. Сервер есть, а управлять ему пока нечем. Пора подключать первых клиентов.
Звучит как формальность — поставил агент, подписал сертификат, готово. На тестовой ноде так и есть. А вот когда начинаешь подключать живые серверы, где уже крутятся базы, почтовики и мониторинг, выясняется, что дьявол, как обычно, в деталях. Про эти детали и будет большая часть статьи — я собрал сюда все грабли, на которые наступил сам.
Адрес сервера в примерах — puppet.example.com. Команды на ноде выполняются от root.
Подключаем ноду: четыре шага
Схема простая: правильное имя → установка агента → запрос сертификата → подпись на сервере. Разберём по порядку, потому что ошибка на первом шаге аукнется на всех остальных.
Шаг 0. Правильный hostname — до первого прогона
Это тот шаг, который хочется пропустить, а потом переделывать всё заново. certname — имя, под которым нода известна серверу, — генерируется из FQDN при первом контакте и сам потом не меняется. Поэтому имя надо задать заранее:
hostnamectl set-hostname ИМЯ.example.com
hostname -f # должен вернуть полный FQDN с доменом
Если hostname -f отдаёт короткое или неправильное имя — пропишите его в /etc/hosts:
echo 'IP_НОДЫ ИМЯ.example.com ИМЯ' >> /etc/hosts
Заодно убедитесь, что нода вообще видит сервер. Если DNS нет:
echo 'IP_СЕРВЕРА puppet puppet.example.com' >> /etc/hosts
Шаг 1. Агент, и обязательно от той же версии ОС
Здесь прячется самая обидная ошибка во всём процессе.
Критично. Release-пакет OpenVox привязан к версии ОС. Поставите пакет «не от той» версии — получите
GLIBC ... not found, и агент просто не запустится. Проверьте версию заранее:cat /etc/os-releaseна Debian илиlsb_release -rsна Ubuntu.
Дальше берёте пакет ровно под свою систему.
Debian 13:
wget -q https://apt.voxpupuli.org/openvox8-release-debian13.deb && dpkg -i openvox8-release-debian13.deb && apt update && apt install -y openvox-agent
Debian 12:
wget -q https://apt.voxpupuli.org/openvox8-release-debian12.deb && dpkg -i openvox8-release-debian12.deb && apt update && apt install -y openvox-agent
Debian 11:
wget -q https://apt.voxpupuli.org/openvox8-release-debian11.deb && dpkg -i openvox8-release-debian11.deb && apt update && apt install -y openvox-agent
Ubuntu (замените ubuntu24.04 на свою версию — lsb_release -rs):
wget -q https://apt.voxpupuli.org/openvox8-release-ubuntu24.04.deb && dpkg -i openvox8-release-ubuntu24.04.deb && apt update && apt install -y openvox-agent
Если wget вернёт 404 — значит для этой версии ОС пакета в репозитории пока нет, смотрите актуальный список на apt.voxpupuli.org.
Шаг 2. Запрос сертификата — на ноде
/opt/puppetlabs/bin/puppet config set server puppet.example.com --section main
/opt/puppetlabs/bin/puppet agent -t --waitforcert 60
Агент сгенерирует запрос на сертификат (CSR) и повиснет в ожидании подписи, переспрашивая сервер каждые 60 секунд. Это штатное поведение — не прерывайте, идёте подписывать на сервер.
Шаг 3. Подпись — на сервере
puppetserver ca list # увидите ожидающий CSR
puppetserver ca sign --certname ИМЯ.example.com
Как только подписали, агент на ноде сам подхватит сертификат и докрутит первый прогон. Применятся все базовые правила: пользователи, пакеты, локаль и часовой пояс, мониторинг — всё, что вы заложили в базовую роль.
Проверяем, что связка сложилась. На ноде — чистый прогон без ошибок:
/opt/puppetlabs/bin/puppet agent -t
На сервере — сертификат в списке подписанных:
puppetserver ca list --all | grep ИМЯ
Bootstrap-скрипт: чтобы не делать это руками каждый раз
Четыре шага — нормально для одной ноды. Для десятой уже хочется автоматизации. У меня для этого есть bootstrap-node.sh: он сам определяет ОС, подключает локальное зеркало пакетов (чтобы не ходить на медленный внешний репозиторий), ставит агент, задаёт certname из FQDN и отправляет CSR.
#!/usr/bin/env bash
#
# Bootstrap ноды OpenVox через локальное зеркало.
#
set -euo pipefail
PUPPET_SERVER="puppet.example.com"
MIRROR_URL="http://puppet.example.com:3333"
KEYRING="/etc/apt/keyrings/openvox-keyring.gpg"
WAITFORCERT=30
die() { echo "ОШИБКА: $*" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || die "запусти от root"
[ -r /etc/os-release ] || die "нет /etc/os-release"
. /etc/os-release
# suite = имя каталога dists/ на зеркале
case "${ID:-}" in
debian)
case "${VERSION_ID:-}" in
11|12|13|14) SUITE="debian${VERSION_ID}" ;;
"") case "${VERSION_CODENAME:-}" in
bullseye) SUITE="debian11" ;;
bookworm) SUITE="debian12" ;;
trixie) SUITE="debian13" ;;
forky) SUITE="debian14" ;;
*) die "неизвестный Debian: ${VERSION_CODENAME:-}" ;;
esac ;;
*) die "Debian ${VERSION_ID} не поддерживается" ;;
esac ;;
ubuntu)
[ -n "${VERSION_ID:-}" ] || die "нет версии Ubuntu"
SUITE="ubuntu${VERSION_ID}" ;; # напр. ubuntu24.04
*) die "ОС '${ID:-}' не поддерживается" ;;
esac
echo ">> ${ID} ${VERSION_ID:-$VERSION_CODENAME} → suite: ${SUITE}"
# GPG-ключ зеркала
install -d -m 0755 /etc/apt/keyrings
wget -qO "$KEYRING" "${MIRROR_URL}/openvox-keyring.gpg" || die "не скачался ключ"
file "$KEYRING" | grep -qiE 'PGP|GPG|keybox|public key' || die "ключ невалиден (не HTML?)"
chmod 0644 "$KEYRING"
# источник на локальное зеркало (signed-by указывает на реальный путь ключа)
cat > /etc/apt/sources.list.d/openvox8-release.list <<EOF
# Managed manually (bootstrap) — local OpenVox mirror
deb [signed-by=${KEYRING}] ${MIRROR_URL}/ ${SUITE} openvox8
EOF
apt-get update
apt-get install -y openvox-agent
PUPPET=/opt/puppetlabs/bin/puppet
[ -x "$PUPPET" ] || die "нет $PUPPET"
FQDN="$(hostname -f 2>/dev/null || true)"
case "$FQDN" in *.*) : ;; *) die "hostname -f без домена: '$FQDN'";; esac
echo ">> certname: $FQDN"
"$PUPPET" config set server "$PUPPET_SERVER" --section main
"$PUPPET" config set certname "$FQDN" --section main
"$PUPPET" agent -t --waitforcert "$WAITFORCERT" || true
echo ""
echo "Теперь на СЕРВЕРЕ подпиши: puppetserver ca sign --certname $FQDN"
Сохраняете в bootstrap-node.sh, запускаете bash bootstrap-node.sh. Подпись всё равно делается руками на сервере — это операция центра сертификации, автоматизировать её без нужды не стоит.
Здесь важно понимать разделение ролей. Скрипт нужен только для первого касания новой ноды — довести её до состояния «агент стоит и подписан». Дальше поддержанием правильного источника пакетов занимается отдельный профиль при каждом прогоне. Небольшое дублирование логики между скриптом и профилем — это нормально: скрипт для bootstrap, профиль для постоянного enforcement.
Оговорюсь про безопасность честно. В этом варианте GPG-ключ зеркала тянется по HTTP, а проверка ловит лишь то, что скачался не HTML вместо ключа. Отпечаток ключа не сверяется. Для доверенной внутренней сети, где зеркало и ноды под вашим контролем, это осознанный компромисс ради простоты. Но если ноды bootstrap-ятся через недоверенный канал — раздавайте ключ по HTTPS и сверяйте фингерпринт, иначе весь смысл подписи пакетов теряется.
Если зеркало недоступно (нода в другой сети, без доступа к порту 3333) — ставьте release-пакет напрямую с
apt.voxpupuli.org, как в ручной установке выше. После первого прогона профиль сам переключит ноду на зеркало.
Живые серверы подключаем через --noop
А вот теперь то, ради чего стоило писать эту статью.
Когда подключаете сервер, на котором уже что-то работает — база, почтовик, мониторинг — никогда не гоните боевой прогон сразу. Сначала сухой:
/opt/puppetlabs/bin/puppet agent -t --noop
--noop (no-operation) показывает, что Puppet собирается сделать, ничего при этом не меняя. Это ваша страховка от конфликтов, и она не абстрактная.
Реальный случай из практики: подключаю сервер мониторинга, а на нём уже стоит zabbix-agent2. Базовый профиль, ничего не подозревая, лезет ставить конфликтующий классический zabbix-agent. Боевой прогон устроил бы весёлые полчаса. --noop показал бы это заранее — и ноду можно было спокойно исключить из профиля через per-node Hiera до того, как что-то сломается.
Мораль простая: на чистой ноде --noop избыточен, на обжитой — обязателен.
Отключаем ноду
Отключение — это не просто «выключить агент». Сервер продолжит считать ноду своей, пока вы не отзовёте сертификат.
На сервере — отозвать сертификат:
puppetserver ca clean --certname ИМЯ.example.com
clean убирает и подписанный сертификат, и висящий CSR. После этого сервер перестаёт доверять ноде и компилировать для неё каталог.
Убрать per-node данные, если заводили. Если для ноды был файл переопределений в Hiera — удаляем его через тот же git-цикл, что и любую другую правку:
cd /root/control-repo
rm -f data/nodes/ИМЯ.example.com.yaml
git add -A && git commit -m 'remove node ИМЯ' && git push origin production
/opt/puppetlabs/puppet/bin/r10k deploy environment production -pv
На ноде — остановить агент, если нода ещё жива:
systemctl disable --now puppet
# при желании снести агент полностью:
apt purge -y openvox-agent
rm -rf /etc/puppetlabs
Переименование подключённой ноды
Раз certname не меняется сам, смена имени — это фактически перевыпуск сертификата в четыре хода.
1. На ноде — сменить hostname:
hostnamectl set-hostname НОВОЕ.example.com
hostname -f # проверь FQDN
2. На сервере — отозвать старое имя:
puppetserver ca clean --certname СТАРОЕ.example.com
3. На ноде — удалить локальный SSL и перезапросить:
systemctl stop puppet 2>/dev/null
rm -rf /etc/puppetlabs/puppet/ssl
/opt/puppetlabs/bin/puppet config set certname НОВОЕ.example.com --section main
/opt/puppetlabs/bin/puppet agent -t --waitforcert 60
4. На сервере — подписать новое имя:
puppetserver ca sign --certname НОВОЕ.example.com
И не забудьте переименовать per-node Hiera-файл, если он был: data/nodes/СТАРОЕ....yaml → data/nodes/НОВОЕ....yaml.
Типовые проблемы
Всё, что ловил на живых нодах, — в одну таблицу, чтобы было куда возвращаться.
| Симптом | Причина и решение |
|---|---|
GLIBC ... not found при запуске агента | Установлен release-пакет не от той версии ОС. Удалите openvox-agent и неверный release-пакет, поставьте правильный (см. Шаг 1). |
No more routes to ca / Connection refused на 8140 | Нода не резолвит puppet.example.com или сервер не запущен. Проверьте /etc/hosts / DNS и systemctl status puppetserver на сервере. |
Certificate ... does not match its private key | Локальный SSL рассинхронизирован. rm -rf /etc/puppetlabs/puppet/ssl на ноде + ca clean на сервере, затем перезапросить. |
ca sign говорит «No certificate request found» | CSR ещё не дошёл (запустите agent -t --waitforcert на ноде) либо имя сертификата не совпадает с FQDN. |
Прогон запущен без sudo | Puppet использует SSL из ~/.puppetlabs вместо системного. Запускайте агент от root полным путём /opt/puppetlabs/bin/puppet. |
Failed to parse keyring ... No such file / «репозиторий не подписан» | Путь в signed-by= не совпадает с реальным расположением ключа. Ключ обычно в /etc/apt/keyrings/openvox-keyring.gpg; приведите signed-by= к нему. |
Что дальше
Теперь у сервера есть кем управлять: ноды подключены, сертификаты подписаны, а живые серверы вы умеете подключать не ломая. Но пока всё, что они получают, — это абстрактная «базовая роль».
В следующей части перейдём к самому интересному — начнём собственно кукловодить: писать роли и профили, раскладывать настройки по Hiera и делать так, чтобы конкретная нода получала ровно то, что ей положено, и ни строчкой больше.