Zamora Teran/Puppet
Introducción y conceptos
Puppet es un sistema de sincronizacion. Lo usamos para instalar y configurar los servidores de la escuela ("XS"). El resultado es que para instalar un nuevo XS, hacemos la instalación base del XS, y despues puppet hace todo el resto de la configuracion relacionada a nuestro proyecto, incluyendo:
- Configuracion de OpenVPN para escuelas seleccionadas
- Configuracion de OpenDNS
- Descarga de activaciones
- y mucho mas
El sistema es basado en un servidor central, que se llama el puppetmaster. Este servidor se encuentra en la oficina de la fundacion.
Los clientes (los XS) se conectan con el puppetmaster cada 30 minutos, y reciben un manifest. Este manifest es una especificacion de la configuracion que deberia tener cada XS. Si hay alguna diferencia entre el manifest y la configuracion actual del XS, puppet automaticamente aplica las diferencias desde el manifest.
Un punto muy interesante es que se puede cambiar el manifest aun despues de instalar los XS. Eso significa que aun despues de tener los XS instalados y corriendo en las escuelas, podemos cambiar su configuracion con facilidad, sin tener que ir a la escuela. Esta capacidad reflecciona muy bien la realidad de que nunca terminas de configurar tu servidor.
El manifest tambien es algo dinamico, podemos programar que algunos XS tienen casos especiales (por ejemplo, unos XS quedan en redes privados, entonces lo conectamos al VPN).
Despues de conectar un XS al sistema de puppet, ya dejamos de hacer cambios a ese XS con la mano. Se maneja todo por puppet, incluyendo los casos raros que tenemos que aplicar de vez en cuando.
Mantinimiento de certificados
Como un proceso de autorizacion, el puppetmaster mantiene certificados autorizados.
Cada XS tiene sus propios certificados (generados automaticamente la primera vez que se corre), y los manda al puppetmaster la primera vez que se conecte. Si el certificado es conocido por el puppetmaster, la conexion esta autorizada. Si no, la autorizacion se pone en espera.
Entonces, la primera vez que el XS sea configurado para conectarse al puppetmaster, hay que autorizar sus certificados. Se hace eso en el puppetmaster con:
# puppet cert list # puppet cert sign schoolserver.sanjudastadeo.managua.fundacionzt.org
El XS se conectara otra vez 30 minutos despues de la solicitud original, y luego comenzará a configurarse segun el manifest de puppet. Si no queres esperar, podes reiniciar el cliente de puppet en el XS ya:
# service puppet restart
El puppetmaster mantiene una lista de todos los certificados que han sido firmados en /var/lib/puppet/ssl/ca/inventory.txt.
Reinstalacion de un XS
Si hay que reemplazar un XS, el nuevo XS se generaría nuevos certificados, y puppetmaster no aceptaría su conexion (por diferencia de certificado). Para resolver eso, sigue las siguientes pasos:
Si ya intentaste conectar el nuevo XS al puppetmaster, hay que borrar los certificados generados y recibidos. En el XS:
# service puppet stop # rm -rf /var/lib/puppet/ssl
- Eso es necesario para quitar el error: Could not request certificate: Retreived certificated does not match private key: please remove certificate from server and regenerate it with the current key
Ahora, hay que borrar la historia de los certificados viejos en el puppetmaster con:
# puppet cert clean schoolserver.escuela.departamento.fundacionzt.org
Despues, se puede seguir el proceso de arriba para autorizar el nuevo certificado, como si fuera una nueva escuela en el proyecto.
Para mas informacion, lee Certificates and Security.
Programación del manifest
Enlaces utiles
La información que sigue asume que tenes un buen conocimiento del formato de los manifests de puppet. Entonces, estudia bien:
- Learning puppet - un libro de introducción muy util
- Core Types Cheat Sheet - un resumen de los elementos claves de los manifests
- Resource Types - la documentación completa de los elementos de los manifests
- Language Guide - la referencia del lenguaje de los manifests
- y mas...
Estructura del manifest
Dividimos las funciones del manifest entre clases. Por ejemplo, tenemos clases separadas para vpn, nagios, y sincronización de actividades. Cada clase debe ser sencillo para entender, y no tan grande.
Ponemos cada clase en su propio archivo, el nombre del archivo basado en el nombre de la clase. Por ejemplo, la clase vpn queda en el archivo vpn.pp.
En el manifest central, site.pp, usamos uno solo nodo donde incluimos el codigo de las clases, y declaramos las clases para ponerlas en acción:
node 'default' { import 'base.pp' import 'vpn.pp' import 'dns.pp' class { 'base': } class { 'vpn': } class { 'dns': } }
- La documentación de puppet sugiere no usar import, se reemplazó está capacidad con modulos que se cargan automaticamente. Es posible que tengamos que cambiar a ese sistema en futuro, pero no me gusta mucho la idea. Parece que hay que separar los modulos del manifest central, y que tambien hay que hacer un monton de jeraquia solo para poner una sola clase dentro un modulo. Entonces por ahora evitamos los modulos y seguimos con import con cuidado.
Como manejar excepciones
Sería bueno que todos los XS tengan la misma configuración. Pero la realidad no es asi, siempre tenemos casos raros (digamos: excepciones). Por ejemplo, en algunas escuelas conectamos a una red que ya existía con router, entonces no tenemos aceso externo al XS (p.ej. para conectar por ssh). En este caso, conectamos el XS al VPN.
Otro ejemplo son las escuelas que están conectadas por un modem de Claro. Esa configuracion sale con el mismo direccion publico como todos los clientes de Claro, osea no podemos configurar OpenDNS para filtrar por IP sin afectar a muchos otros clientes de Claro. En este caso, en vez de usar OpenDNS directamente, mandamos las solicitudes DNS a otro servidor nuestro, que tiene IP fijo y privado, que las manda a OpenDNS.
Como definir variables de extlookup
En ambos casos usamos extlookup para manejar las excepciones. extlookup nos permite asignar variables con valores desde una base de datos sencilla. Dentro de site.pp tenemos:
$extlookup_datadir = "/etc/puppet/manifests/extdata" $extlookup_precedence = ["%{fqdn}", "common"]
Esas lineas dicen que nuestra base de datos queda en /etc/puppet/manifests/extdata, y que vamos a buscar entradas primero con el nombre completo del dominio del XS (FQDN: Fully Qualified Domain Name, p.ej. schoolserver.juanpablo2.managua.fundacionzt.org), y despues, si no encontramos una fila, buscamos en un archivo "common".
Dentro del directorio extdata ponemos un archivo por XS con excepciones. A esos archivos digamos archivos de excepcion. Es decir, los XS que no tiene nada "especial" sobre ellos no tienen ningun archivo alla, pero los que son casos especiales (p.ej. los casos de redes privados y modems de claro) tienen archivos aqui. Los archivos tienen el FQDN del XS, y deben de terminar con '.csv'. Por ejemplo: schoolserver.juanpablo2.managua.fundacionzt.org.csv
Dentro de los archivos CSV, los datos tienen formato:
nombre_del_variable,valor
Por ejemplo, el archivo schoolserver.juanpablo2.managua.fundacionzt.org.csv podria tener contenido:
vpn_enabled,true
Con este ejemplo, indicamos a puppet que la escuela Juan Pablo II deberia configurarse para conectar al VPN de la fundacion.
Tambien ponemos un archivo common.csv que dice:
vpn_enabled,false
Puppet usa el archivo common.csv cuando no encuentra un valor de un variable para un XS en un archivo de excepcion. En este caso, estamos diciendo que por defecto, no habilitamos el VPN en los XS. Es muy importante poner un valor defecto para cada variable en common.csv. Si puppet no encuentra algun valor que busca por extlookup, genera un error, y el XS no se sincronizará.
Como usar variables de extlookup
Despues de definir los variables y sus valores, tenemos que cambiar los manifests de puppet para leer estos datos y ponerlos en marcha.
Dentro de una clase, se puede definir un variable asi:
$usar_vpn = extlookup("vpn_enabled")
Según la configuración detallada más arriba, eso significa que el puppetmaster va a buscar en extdata, primero para un archivo de excepción para el XS que está conectado y pidiendo manifest, y despues, si no se encuentra un variable con este nombre en un archivo de excepción, va a usar el valor del variable en common.csv.
Ahora podemos usar el variable $usar_vpn dentro de los manifests. Por ejemplo, podemos hacer un if:
if $usar_vpn == 'true' { ... }
Como desarrollar
Puppet nos da mucho poder: con un cambio pequeño, cambiamos la configuracion de todos los XS del pais dentro de 30 minutos. Pero eso tambien es peligroso. Si hacemos un error, y resulta que pasa algo que es dificil dehacer, tenemos grandes problemas.
Por eso hay que tener mucho cuidado mientras tocamos los archivos de manifest. Usamos una manera de desarollar que es un poco extenso, pero es seguro:
- Elegimos un XS de prueba (por ejemplo, un XS de prueba que tenemos en la oficina)
- Hacemos los cambios a parte de la configuracion global, de tal manera que los cambios solo aplican al XS de prueba.
- Probamos los cambios.
- Si logramos en las pruebas, aplicamos los cambios a todos los XS del pais.
Hacer los cambios solo para un XS de prueba
Acordamos que incluimos y declaramos todas las clases dentro de un nodo 'default'. La razon por eso ahora se volverá clara.
Copiar ahora el nodo 'default', y pegarlo, pero en la copia, cambia el nodo para tener el nombre del XS, por ejemplo, schoolserver.escuela.dept.fundacionzt.org. Como ejemplo, el manifest ahora tendria este contenido:
node 'default' { import 'base.pp' import 'vpn.pp' import 'dns.pp' class { 'base': } class { 'vpn': } class { 'dns': } } node 'schoolserver.escuela.dept.fundacionzt.org' { import 'base.pp' import 'vpn.pp' import 'dns.pp' class { 'base': } class { 'vpn': } class { 'dns': } }
La clave es que no tocamos la configuracion del nodo defecto, hacemos todos los cambios dentro del nodo del XS de proeuba.
Entonces ya estamos listos para desarrollar. Hay 2 posibilidades:
- Queremos agregar una nueva clase
- Queremos cambiar una clase que ya existe
En el ejemplo de que queremos agregar una nueva clase, escribimos el codigo de la clase en un nuevo archivo y agregamos el import y la declaracion al nodo del XS de prueba. Por ejemplo, si queremos desarrollar una clase de 'activities':
node 'schoolserver.escuela.dept.fundacionzt.org' { import 'base.pp' import 'vpn.pp' import 'dns.pp' import 'activities.pp' # agregar esta linea class { 'base': } class { 'vpn': } class { 'dns': } class { 'activities': } # agregar esta linea }
En el caso de que queremos cambiar una clase que ya existe, el proceso es diferente. Hacemos una copia de la clase original, con un nuevo nombre, por ejemplo si queremos cambiar la clase 'dns':
# cp dns.pp dns2.pp
Y cambiamos el nodo del XS de prueba para importar el nuevo archivo:
node 'schoolserver.escuela.dept.fundacionzt.org' { import 'base.pp' import 'vpn.pp' import 'dns2.pp' # cambiamos esta linea dns --> dns2 class { 'base': } class { 'vpn': } class { 'dns': } }
En este momento podemos comenzar a hacer los cambios dentro del archivo dns2.pp. No tocamos el archivo original (dns.pp) en este momento.
Hacemos los cambios (en la nueva clase, o en la copia de la clase original) y probamos bien los cambios en el XS de prueba.
Aplicar los cambios a todo el mundo
Cuando estemos contentos que los cambios estan bien, podemos aplicar los cambios a todos los XS.
En el caso que agregamos una nueva clase, ahora solo tenemos que agregar esa clase al nodo 'default'. Con el ejemplo anterior (agregar la nueva clase 'activities'):
node 'default' { import 'base.pp' import 'vpn.pp' import 'dns.pp' import 'activities.pp' class { 'base': } class { 'vpn': } class { 'dns': } class { 'activities': } }
En el caso que hicimos un cambio a una clase ya existente, solo tenemos que sobreescribir el archivo original con el nuevo. En el ejemplo anterior de la modificaion de la clase 'dns':
# cp dns2.pp dns.pp
Y tambien podemos borrar el archivo dns2.pp que usamos para la prueba:
# rm dns2.pp
Por fin, podemos borrar el nodo 'schoolserver.escuela.dept.fundacionzt.org' (del XS de prueba) para solo quedar con el nodo 'default'.
Hecho!