Guías · Nivel 3

Puppet: servidor y agentes en Linux y Windows, certificados, un primer módulo, Hiera por entorno y un agente que deja de aplicar cambios

Puppet 8 u OpenVox 8 tras el cambio de licencia: el servidor en Linux, los agentes en Linux y Windows, la firma de certificados, site.pp, un primer módulo con paquete, plantilla y servicio, roles y perfiles, Hiera por entorno, el código en Git, la ejecución en noop, los informes y un agente que deja de aplicar cambios.

Qué vas a conseguir

Montar desde cero una instalación de Puppet que gestiona servidores Linux y Windows: un servidor primario con su autoridad de certificación, dos agentes con el certificado firmado, un primer módulo que instala un paquete, escribe su fichero de configuración desde una plantilla y reinicia el servicio solo cuando el fichero cambia, los datos en Hiera separados por entorno y el código en Git. Y saber qué mirar cuando un agente deja de aplicar los cambios que se suben al repositorio.

Antes de empezar

  • Un servidor Linux para el servidor primario (RHEL 9 o compatible en esta guía; Ubuntu 24.04 también sirve) con 4 GB de RAM como mínimo: Puppet Server es una aplicación Java y arranca con 2 GB de memoria reservada.
  • Un nombre DNS estable para él, por ejemplo puppet.contoso.local, que resuelvan todos los agentes. Los agentes se conectan por TCP 8140.
  • Dos servidores de prueba: srv-app-01.contoso.local (Linux) y srv-win-01.contoso.local (Windows Server 2022 o 2025), con la hora sincronizada. Los certificados fallan con relojes desfasados.
  • Acceso sudo en los Linux y administrador local en el Windows.
  • Un repositorio Git para el código: desde el primer módulo, nada se edita a mano en el servidor.

1. Puppet u OpenVox: qué instalar

Desde 2025 hay dos caminos para la versión 8:

  • Perforce, propietario de Puppet, anunció a finales de 2024 que los paquetes oficiales pasarían a un repositorio privado. Su distribución, Puppet Core, exige aceptar una licencia comercial, con un uso gratuito limitado (25 nodos en el anuncio original) y suscripción por encima. El código abierto publicado sigue bajo licencia Apache 2.0, pero el desarrollo nuevo ya no se hace en público.
  • OpenVox es la bifurcación de la comunidad (Vox Pupuli), con primera versión en enero de 2025. Es compatible con el código de Puppet, publica paquetes en yum.voxpupuli.org y apt.voxpupuli.org y mantiene la rama 8 mientras prepara la 9.

Los paquetes cambian de nombre (openvox-server y openvox-agent frente a puppetserver y puppet-agent), pero los comandos (puppet, puppetserver), las rutas (/etc/puppetlabs) y el lenguaje son los mismos. Esta guía usa los paquetes de OpenVox 8; con Puppet Core solo cambia el repositorio. Revisa las condiciones vigentes de cada uno antes de elegir.

2. El servidor primario

bash · puppet.contoso.local
sudo rpm -Uvh https://yum.voxpupuli.org/openvox8-release-el-9.noarch.rpmsudo dnf install -y openvox-serversource /etc/profile.d/puppet-agent.shsudo /opt/puppetlabs/bin/puppet config set dns_alt_names puppet,puppet.contoso.local --section serversudo /opt/puppetlabs/bin/puppet config set server puppet.contoso.local --section mainsudo firewall-cmd --permanent --add-port=8140/tcp && sudo firewall-cmd --reloadsudo systemctl enable --now puppetserverpuppetserver --versionpuppetserver version: 8.11.0
  • Los ejecutables quedan en /opt/puppetlabs/bin; el script de /etc/profile.d lo añade al PATH. Con sudo hace falta la ruta completa o sudo -i.
  • dns_alt_names va antes del primer arranque: en ese arranque el servidor crea la autoridad de certificación y su propio certificado, y los nombres alternativos quedan grabados en él. Cambiarlo después obliga a regenerar el certificado del servidor.
  • La memoria de Java está en /etc/sysconfig/puppetserver (en Ubuntu, /etc/default/puppetserver): JAVA_ARGS="-Xms2g -Xmx2g". Con cientos de agentes se sube aquí.
  • El código vive en /etc/puppetlabs/code/environments/, un directorio por entorno. El paquete crea production.

3. El agente en Linux y en Windows

En el Linux:

bash · srv-app-01.contoso.local
sudo rpm -Uvh https://yum.voxpupuli.org/openvox8-release-el-9.noarch.rpmsudo dnf install -y openvox-agentsudo /opt/puppetlabs/bin/puppet config set server puppet.contoso.local --section mainsudo /opt/puppetlabs/bin/puppet agent -tInfo: Creating a new RSA SSL key for srv-app-01.contoso.localInfo: Creating a new SSL certificate request for srv-app-01.contoso.localInfo: Certificate Request fingerprint (SHA256): 4F:1A:7C:...:9CInfo: Certificate for srv-app-01.contoso.local has not been signed yetCouldn't fetch certificate from CA server; you might still need to sign this agent's certificate (srv-app-01.contoso.local).

En Ubuntu, el paquete del repositorio es openvox8-release-ubuntu24.04.deb de apt.voxpupuli.org, se instala con dpkg -i y después apt update y apt install openvox-agent.

En el Windows, el MSI de la rama 8 está en downloads.voxpupuli.org/windows/openvox8/. Se instala en silencio desde PowerShell como administrador:

PowerShell · srv-win-01.contoso.local
msiexec /qn /norestart /i openvox-agent-8.28.0-x64.msi PUPPET_MASTER_SERVER=puppet.contoso.local PUPPET_AGENT_STARTUP_MODE=AutomaticGet-Service puppetStatus   Name     DisplayName------   ----     -----------Running  puppet   Puppet Agent
  • El MSI añade C:\Program Files\Puppet Labs\Puppet\bin al PATH; la configuración queda en C:\ProgramData\PuppetLabs\puppet\etc\puppet.conf. Para ejecutar puppet agent -t a mano, consola como administrador.
  • El nombre del certificado (certname) es el FQDN en minúsculas. Si el Windows no tiene sufijo DNS, se queda solo con el nombre corto; PUPPET_AGENT_CERTNAME=srv-win-01.contoso.local lo fija en la instalación.
  • El agente se ejecuta como servicio cada 30 minutos (runinterval). En el Linux, sudo /opt/puppetlabs/bin/puppet resource service puppet ensure=running enable=true lo deja activo al arrancar.

4. Firmar los certificados

Cada agente genera su clave y envía una petición de certificado. Hasta que el servidor la firma, no recibe nada:

bash · puppet.contoso.local
sudo /opt/puppetlabs/bin/puppetserver ca listRequested Certificates:    srv-app-01.contoso.local   (SHA256)  4F:1A:7C:...:9C    srv-win-01.contoso.local   (SHA256)  B2:77:0E:...:E0sudo /opt/puppetlabs/bin/puppetserver ca sign --certname srv-app-01.contoso.local,srv-win-01.contoso.localSuccessfully signed certificate request for srv-app-01.contoso.localSuccessfully signed certificate request for srv-win-01.contoso.localsudo /opt/puppetlabs/bin/puppetserver ca list --all
  • Compara la huella de ca list con la que mostró el agente antes de firmar: así sabes que firmas el equipo que crees.
  • La firma automática (autosign.conf o un script de política) existe y ahorra trabajo en despliegues masivos, pero firmar lo que llega sin comprobarlo da la configuración, y los secretos que lleve, a cualquier equipo que alcance el puerto 8140.

5. site.pp: qué recibe cada nodo

El fichero manifests/site.pp del entorno asigna clases a cada nodo por su certname:

site.pp · environments/production/manifests/
node 'srv-app-01.contoso.local' {  include role::app_linux} node 'srv-win-01.contoso.local' {  include role::base_windows} node /^srv-web-\d+\.contoso\.local$/ {  include role::web} node default {}
  • Se aplica el bloque más concreto que coincida; si ninguno coincide, default. Sin bloque default el agente falla con Could not find node statement.
  • En instalaciones grandes, la clasificación sale de un clasificador externo (Foreman, la consola de la edición comercial) en lugar de site.pp. La idea es la misma: un nodo, un rol.

6. El primer módulo: paquete, plantilla y servicio

El módulo timesync instala chrony, escribe su configuración desde una plantilla con los servidores de hora de la empresa y reinicia el servicio solo si el fichero cambia:

modules/timesync/manifests/init.pp
class timesync (  Array[String] $servers,) {  case $facts['os']['family'] {    'RedHat': {      $conf    = '/etc/chrony.conf'      $service = 'chronyd'    }    'Debian': {      $conf    = '/etc/chrony/chrony.conf'      $service = 'chrony'    }    default: {      fail("timesync: ${facts['os']['family']} no está soportado")    }  }   package { 'chrony':    ensure => installed,  }   file { $conf:    ensure  => file,    owner   => 'root',    group   => 'root',    mode    => '0644',    content => epp('timesync/chrony.conf.epp', { 'servers' => $servers }),    require => Package['chrony'],    notify  => Service[$service],  }   service { $service:    ensure => running,    enable => true,  }}
modules/timesync/templates/chrony.conf.epp
<%- | Array[String] $servers | -%># Gestionado por Puppet (módulo timesync). Los cambios a mano se pierden.<% $servers.each |$s| { -%>server <%= $s %> iburst<% } -%>driftfile /var/lib/chrony/driftmakestep 1.0 3rtcsync
  • require ordena (primero el paquete, luego el fichero); notify ordena y reinicia el servicio cuando el fichero cambia. Si el fichero ya está como debe, no hay reinicio.
  • $servers no tiene valor por defecto: sale de Hiera (apartado 8). Un valor fijo en el código obligaría a cambiar el módulo para cada sede.
  • Las plantillas EPP usan el propio lenguaje de Puppet; las ERB (template(), sintaxis Ruby) siguen funcionando y son las que se verán en módulos antiguos.
  • Antes de subir: puppet parser validate modules/timesync/manifests/init.pp y puppet-lint sobre el módulo.

En Windows los mismos tipos de recurso funcionan con otras rutas. Un perfil base mínimo:

site/profile/manifests/base_windows.pp
class profile::base_windows {  service { 'W32Time':    ensure => running,    enable => true,  }   file { 'C:/ProgramData/Contoso':    ensure => directory,  }}

En las rutas de Windows, la barra normal evita problemas de escape en el código.

7. Roles y perfiles

El patrón habitual separa tres capas:

CapaQué contieneEjemplo
Módulo de componenteCómo se configura una pieza, sin datos de la empresatimesync, módulos de Puppet Forge
PerfilUna pieza de la empresa: qué módulos y con qué ajustesprofile::base_linux incluye timesync y el agente de monitorización
RolQué es el servidor, solo con include de perfilesrole::app_linux = profile::base_linux + profile::app

Cada nodo recibe un solo rol. Si dos servidores necesitan algo distinto, son dos roles. Los roles y perfiles suelen vivir en site/role y site/profile, junto a los módulos.

8. Hiera: los datos fuera del código

Hiera da valores a los parámetros de las clases según el nodo. En la raíz de cada entorno:

hiera.yaml · environments/production/
---version: 5defaults:  datadir: data  data_hash: yaml_datahierarchy:  - name: "Por nodo"    path: "nodes/%{trusted.certname}.yaml"  - name: "Por sistema operativo"    path: "os/%{facts.os.family}.yaml"  - name: "Común"    path: "common.yaml"
data/common.yaml
---timesync::servers:  - dc01.contoso.local  - dc02.contoso.local
  • Puppet busca de arriba abajo y se queda con el primer valor: un fichero en nodes/ gana a common.yaml. La clave timesync::servers rellena el parámetro $servers sin que el código lo pida.
  • Usa trusted.certname (del certificado) para la capa de nodo, no un fact que el propio agente puede falsear.
  • Como cada entorno tiene su hiera.yaml y su data/, staging puede apuntar a otros servidores de hora sin tocar production.
  • Para ver qué valor sale y de dónde: puppet lookup timesync::servers --environment production --explain en el servidor.
  • Las contraseñas no van en claro: hiera-eyaml las cifra dentro del YAML.

9. El código en Git y los entornos

  • Un repositorio de control con manifests/, site/, data/, hiera.yaml y un Puppetfile con los módulos de Puppet Forge y sus versiones.
  • Cada rama es un entorno: la rama production se despliega en /etc/puppetlabs/code/environments/production, la rama staging en .../staging. La herramienta que hace ese despliegue es r10k (o Code Manager en la edición comercial), lanzada desde el pipeline al fusionar.
  • El flujo de un cambio: rama, revisión, fusión a staging, prueba en unos pocos nodos, fusión a production.

Antes de aplicar nada, el ensayo en el agente:

bash · srv-app-01.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -t --noop --environment stagingInfo: Using environment 'staging'Info: Retrieving pluginfactsInfo: Retrieving pluginInfo: Loading factsInfo: Caching catalog for srv-app-01.contoso.localInfo: Applying configuration version '1762160512'Notice: /Stage[main]/Timesync/File[/etc/chrony.conf]/content: current_value '{sha256}3b1f...', should be '{sha256}9ac2...' (noop)Notice: /Stage[main]/Timesync/Service[chronyd]: Would have triggered 'refresh' from 1 eventNotice: Applied catalog in 0.94 seconds

--noop compila el catálogo y dice qué cambiaría sin tocar nada. Si el resultado es el esperado, la misma orden sin --noop aplica el cambio en ese nodo; el resto lo recibe en su siguiente ejecución al fusionar en production.

10. Informes: qué pasó en cada ejecución

  • Con -t, el código de salida dice el resultado: 0 sin cambios, 2 con cambios, 4 con fallos, 6 cambios y fallos. Sirve para un pipeline o un script.
  • En el agente, puppet config print lastrunfile da la ruta del resumen de la última ejecución (hora, recursos cambiados, fallidos). Una comprobación de monitorización que avise si ese fichero tiene más de dos horas detecta agentes parados.
  • El agente envía un informe al servidor en cada ejecución; por defecto se guarda en /opt/puppetlabs/server/data/puppetserver/reports/<certname>/.
  • Con más de unos pocos nodos, PuppetDB (u OpenVoxDB) guarda hechos, catálogos e informes de todos, y un panel como Puppetboard muestra qué nodos fallan o llevan tiempo sin informar.

11. El ticket: "un agente dejó de aplicar los cambios"

El cambio de servidores de hora llegó a todos los Linux menos a srv-app-02.contoso.local, reinstalado la semana pasada. Puppetboard dice que su último informe es de hace nueve días. Lo primero, una ejecución a mano en el agente y leer el primer error:

bash · srv-app-02.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -tError: The certificate for 'CN=srv-app-02.contoso.local' does not match its private key

Los cuatro motivos habituales, por orden de frecuencia:

1. Certificado que no corresponde tras una reinstalación. El equipo nuevo generó otra clave, pero la CA conserva el certificado firmado del equipo anterior con el mismo nombre. Se borra en los dos lados y se vuelve a firmar:

bash · servidor y agente
# en puppet.contoso.localsudo /opt/puppetlabs/bin/puppetserver ca clean --certname srv-app-02.contoso.local# en srv-app-02.contoso.localsudo /opt/puppetlabs/bin/puppet ssl cleansudo /opt/puppetlabs/bin/puppet agent -t# de nuevo en el servidor: comprobar la huella y firmarsudo /opt/puppetlabs/bin/puppetserver ca sign --certname srv-app-02.contoso.local

puppet ssl clean borra la clave y el certificado del agente; en Windows es el mismo comando en una consola de administrador. Para que no se repita, el procedimiento de baja o reinstalación de un servidor incluye puppetserver ca clean y, con PuppetDB, puppet node deactivate.

2. Reloj desfasado. Errores como certificate verify failed (certificate is not yet valid) o un certificado recién firmado que el agente da por caducado. timedatectl en Linux o w32tm /query /status en Windows. Es justo lo que el módulo timesync evita, pero un equipo recién instalado aún no lo tiene: la hora se corrige a mano antes del primer puppet agent -t.

3. El puerto 8140. Failed to open TCP connection to puppet.contoso.local:8140 indica red o cortafuegos, no Puppet. nc -zv puppet.contoso.local 8140 en Linux o Test-NetConnection puppet.contoso.local -Port 8140 en Windows. Si no conecta, revisa el cortafuegos del servidor, el del propio equipo y si el nombre resuelve a la IP correcta.

4. Agente deshabilitado. Alguien lo paró para un cambio manual y no lo reactivó:

bash · srv-app-02.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -tNotice: Skipping run of Puppet configuration client; administratively disabled (Reason: 'INC-4471 prueba manual de chrony');Use 'puppet agent --enable' to re-enable.sudo /opt/puppetlabs/bin/puppet agent --enablesudo /opt/puppetlabs/bin/puppet agent -t --noop

puppet agent --disable "motivo" es la forma correcta de parar un agente: deja el motivo a la vista. Antes de reactivarlo, el --noop enseña qué deshará Puppet de lo que se cambió a mano; si ese cambio debe quedarse, va al código primero.

En el ticket: nodo, primer error, causa, comandos ejecutados en servidor y agente, y la ejecución final con código de salida 0 o 2 y sin fallos.

Siguientes pasos

¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.

Tech Career Lab · newsletter

¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.

Apúntate y llévate las 33 ideas →