Marionetas

Configuración de Puppet en Debian Jessie y creación de un módulo

Cuando tienes hacer un despliegue en muchos servidores o automatizar tareas repetitivas, Puppet es una gran opción. En base a manifiestos declarativos, Puppet se asegura de que existan recursos, los crea sin aún no existen y los utiliza. A diferencia de muchos otros lenguajes, en los manifiestos no se definen los procesos para aprovechar los recursos, sino que se describe «lo que debe haber» y Puppet se encarga de hacerlo realidad. Así ha sido dicho y así será hecho.

Arquitectura

En Puppet, hay dos arquitecturas:

Agent/Master

En esta arquitetura, uno o más servidores son el master de varios nodos cliente, llamados «agentes».

El agente envía una lista de «hechos» (facts) y pide un manifiesto de recursos al master. Éste compila un catalogo y lo envía al agente, el cual lo usará para realizar las tareas.

Standalone

Y, al contrario, en esta arquitectura sólo se utiliza una máquina para el Puppet.

Entorno

En el post utilizo dos máquinas virtuales:

Sistema Operativo:  Debian Jessie
IP address:         192.168.1.137
Host:               puppetserver

 

Sistema Operativo:  Debian Jessie
IP address:         192.168.1.133
Host:               puppetclient

Preparativos

NTP

El tiempo entre ambas máquinas debe estar lo más ajustado posible. Para ello usaremos NTP en los dos lados:

# apt install ntp

Nombres de dominio

Tanto en el master como en el agente, usaremos el archivo /etc/hosts . En el master:

192.168.1.133       puppetclient

En el agente:

192.168.1.137       puppetserver

Repositorio

Descargamos e instalamos el siguiente paquete para apt en ambas máquinas:

# wget //apt.puppetlabs.com/puppetlabs-release-pc1-jessie.deb
# dpkg -i puppetlabs-release-pc1-jessie.deb
# apt update

Instalando el servidor

El servidor puppet es el software que se ejecuta en el Master y envía las configuraciones a los nodos agente.

Instalamos con el siguiente comando:

# apt install -y puppetserver

Configurando el servidor

Editamos el archivo:

# vim /etc/puppetlabs/puppet/puppet.conf

Utilizaremos un entorno test para los manifiestos de más abajo. Añadimos las siguientes líneas:

[main]
certname = puppetserver
server = puppetserver
environment = production
runinterval = 1h
environmentpath = /etc/puppetlabs/code/environments
basemodulepath = /etc/puppetlabs/code/environments/production/modules
log_level = notice

Empezar y autoarrancar al inicio del sistema operativo:

# systemctl start puppetserver
# systemctl enable puppetserver

 Instalando el Agente

Instalamos el agente con el siguiente comando:

# apt install -y puppet-agent

Añadimos en el archivo puppet.conf

[main]
certname = puppetclient
server = puppetserver
environment = production
runinterval = 1h

Arrancamos el agente al modo de Puppet:

# /opt/puppetlabs/bin/puppet resource service puppet ensure=running enable=true

Certificados

Eliminar certificados

Si ya hay certificados creados, pueden eliminarse escribiendo en el master:

# puppet cert sign --all
# puppet cert clean nombrecertificado

Y en el agente:

# rm -rf /var/lib/puppet/ssl/*

Creando certificados

En el cliente:

# /opt/puppetlabs/bin/puppet agent -t

Info: Caching certificate for ca
Info: csr_attributes file loading from /etc/puppetlabs/puppet/csr_attributes.yaml
Info: Creating a new SSL certificate request for puppetclient
Info: Certificate Request fingerprint (SHA256): D0:DF:9F:13:D4:46:9A:9E:81:5D:D7:64:9E:F1:69:40:62:B1:1C:B6:C5:8A:6C:BB:D4:9A:CD:8A:88:F8:B4:78
Info: Caching certificate for ca
Exiting; no certificate found and waitforcert is disabled

En el servidor, firmamos el certificado para el agente:

# /opt/puppetlabs/bin/puppet cert sign puppetclient

Signing Certificate Request for:
  "puppetclient" (SHA256) D0:DF:9F:13:D4:46:9A:9E:81:5D:D7:64:9E:F1:69:40:62:B1:1C:B6:C5:8A:6C:BB:D4:9A:CD:8A:88:F8:B4:78
Notice: Signed certificate request for puppetclient
Notice: Removing file Puppet::SSL::CertificateRequest puppetclient at '/etc/puppetlabs/puppet/ssl/ca/requests/puppetclient.pem'

Comprobamos el certificado:

# /opt/puppetlabs/bin/puppet cert list --all

+ "puppetclient"            (SHA256) F7:9B:57:33:84:D2:06:13:77:61:C7:FD:04:5A:1E:1E:38:E8:12:0F:6E:E0:98:79:06:5E:49:BF:59:22:1F:94
+ "puppetserver"            (SHA256) AC:D9:3C:05:66:68:59:D8:87:DD:85:9D:8F:FA:F6:A7:28:4C:C1:1E:E4:85:B6:C8:F8:B9:AC:6C:24:03:C9:51 (alt names: "DNS:puppet", "DNS:puppetserver")

Comprobamos que el certificado haya sido firmado:

# opt/puppetlabs/bin/puppet agent --test

Info: Caching certificate_revocation_list for ca
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Caching catalog for puppetclient
Info: Applying configuration version '1487457939'
Info: Creating state file /opt/puppetlabs/puppet/cache/state/state.yaml
Notice: Applied catalog in 0.09 seconds

Creando manifiestos

Vamos a realizar unas tareas básicas:

  • Crear un usuario
  • Crear un directorio
Entorno

Iniciamos la creación de un entorno test:

# mkdir -p /etc/puppetlabs/code/environments/test/modules/
# cd /etc/puppetlabs/code/environments/test 
# mkdir manifests

Creamos un módulo llamado enredadera-mod

# cd modules
# /opt/puppetlabs/bin/puppet module generate enredadera-mod
# mkdir /etc/puppetlabs/code/environments/test/mod/files

puppet.conf

Modificamos en el archivo puppet.conf del, master y del agente, la variable environment y cambiamos «production» por «test».

Manifiesto inicial del módulo

Los módulos necesitan un manifiesto con el nombre init.pp a partir del cual accede a los recursos del resto del módulo:

# vim /etc/puppetlabs/code/environments/test/modules/enredadera-mod/manifests/init.pp

Clase mod::usuario

Creamos una clase llamada mod::usuario:

class mod::usuario {

}

Dentro de la clases declaramos los recursos grupo, usuario y home del usuario:

group { 'usuario':
    ensure   => 'present',
    gid      => '500',
}

Continuamos:

user {'usuario':
  ensure     => 'present',
  uid        => '500',
  gid        => '500',
  groups     => ['gus'],
  home       => '/home/usuario',
  shell      => '/bin/bash',
  password   => '$6$uAwjqazsaN$wUb5V4wgIQUbvIXHCfkkfDwtPHm8X8m/nVMgz/kbOgaTUuUA2SsENXN61s5lpIOzB3IFNnlbgmkos414ZwrtQ.',
}

Por último declaramos el home del usuario:

file {'/home/usuario/':
  ensure     => 'directory',
  owner      => 'usuario',
  group      => 'usuario',
  recurse    => 'true',
}

Por último, respetar el orden de ejecución de las declaraciones:

Group['usuario'] -> User['usuario']

El operador -> indica el orden de precedencia. De este modo, el grupo se crea entes del usuario. Podríamos añadir el recurso file, sin embargo, no sería un problema, en este caso, que sea creado a continuación del usuario.

La clase quedaría así:

class mod::usuario {
                  

              group {'usuario':
            
                     ...
            
              }
 

              user {'usuario':
             
                     ...

              }
 
              
              file {'/home/usuario/':
            
                     ...

              }
 
             Group['usuario'] -> user ['usuario']
 
}

Manifiesto principal

Para que la clase sea cargada desde el agente, hay que llamarla desde el archivo site.pp,  para ello se crea un manifiesto en el entorno test:

# vim /etc/puppetlabs/code/environments/test/manifests/site.pp

Llamando a la clase

En el archivo definimos qué agentes tienen acceso con su hostname. El manifiesto site.pp llama a la clase del siguiente modo:

node 'puppetclient' {
        include mod::usuario
}

Lanzando el manifiesto

Con el siguiente comando le decimos al agente que pida el manifiesto al master:

# /opt/puppetlabs/bin/puppet agent -t

Puede añadirse la opción –noop, con la cual ejecuta el manifiesto en «modo simulación» sin cambiar nada en el sistema.

Con esto cualquier nodo que utilice el manifiesto habrá creado un usuario.

Copiando y ejecutando un script bash

Creamos un archivo bash en el directorio files del módulo.

# vim /etc/puppetlabs/code/environments/test/modules/mod/files/nombre.sh

El contenido del archivo lo obvio. Puede ser cualquier script y un simple «echo ‘¡Hola, mundo!'» serviría para el ejemplo.

Ampliando el manifiesto

Objetivo

  • Copiar un script desde el master al agente
  • Ejecutar el script

Añadimos a init.pp la siguiente clase:

class mod::ejecuta {

}

Copiamos el script desde el directorio files del módulo al directorio /usr/local/bin del agente:

file {'nombre':
  ensure            => 'file',
  source            => 'puppet://puppetserver/modules/basico/nombre.sh',
  path              => '/usr/local/bin/nombre.sh',
  owner             => 'root',
  group             => '755',
  mode              => '0777',
}

Después de que se haya realizado la copia, el script será ejecutado en el agente:

exec {'nombre.sh':
  path			         => '/usr/local/bin',
  logoutput	       => 'true'
}

Por último, y no menos importante, ordenamos la precedencia de ambos recursos:

File['nombre'] -> Exec['nombre.sh']

La clase quedará con la misma estructura que la clase definida anteriormente.

Incluimos la clase en el archivo site.pp

node 'puppetclient' {
        include mod::usuario
        include mod::ejecuta
}

Montando carpetas para el agente

Creamos o editamos el archivo en cliente:

# vim /etc/puppetlabs/puppet/fileserver.conf

y añadimos las líneas:

[files]
  path /usr/local/bin
  allow *

Por último

Lanzamos el script del modo ya mostrado:

# /opt/puppetlabs/bin/puppet agent -t

En próximos posts mostraré como leer parámetros del sistema utilizando la aplicación Facter para configurar un módulo y cómo ampliar el comportamiento de Facter creando una extensión. También escribiré sobre los operadores de precedencia, ya que existen otros aparte de -> y varías maneras de utilizarlos.