sábado, 8 de agosto de 2020

OpenLDAP - Replicación entre servidores OpenLDAP

Continuamos trabajando con OpenLDAP y en este post, veremos como podemos establecer una replicación entre servidores OpenLDAP para crear arquitecturas tolerantes a fallos.

En los posts anteriores hemos visto como realizar la configuración dinámica de un servidor OpenLDAP y como integrar un servicio Kerberos con OpenLDAP: Hoy, partiendo de otra máquina en la que hemos realizado las mismas tareas, veremos como crear una relación de replicación entre ambos servicios.

Primero vamos con un poco de rollo teórico sobre como OpenLDAP replica su información y permite crear servicios de nombres tolerantes a fallos.

OpenLDAP proporciona diferentes topologías de replicación y, en función de nuestras necesidades, unas se adaptarán mejor que otras a nuestra arquitectura. Independientemente de la topología seleccionada, todas se basan en el motor LDAP sync replication o syncrepl. Este sistema de replicación es un motor de cliente, es decir, los servidores OpenLDAP designados como consumers establecen una conexión con los servidores OpenLDAP designados como providers para mantener una copia del árbol de directorio.

Simplificando mucho el mecanismo de replicación, un consumer realiza una búsqueda en el provider y aplica todos aquellos cambios que se hayan producido desde que realizó la última búsqueda. Si lo explicamos con un poco más de detalle, el provider mantiene un atributo llamado contextCSN  por cada base de datos como el indicador del estado actual de la replicación. Basándose en la diferencia entre el atributo contextCSN del provider y el almacenado en el consumer, se transfieren todos los cambios necesarios del provider al consumer hasta que ambos atributos contextCSN sean idénticos.

Vamos a comenzar configurando nuestro servidor provider, para lo cual cambiaremos la configuración de forma dinámica. Básicamente es necesario añadir el módulo syncprov a la configuración del servidor y activar el overlay syncprov, en la base de datos que queramos replicar.

Para añadir el módulo al servidor OpenLDAP, basta con que creemos un fichero ldif con la configuración deseada, indicando la ruta donde se encuentran los módulos de OpenLDAP y el nombre del fichero del módulo a cargar y añadiremos dicho módulo usando un comando ldapadd, sobre la rama cn=config:
 
Configuración módulo syncprov en formato LDIF
Configuración módulo syncprov en formato LDIF.
 
Carga de la configuración del módulo
Carga de la configuración del módulo.

Con esta carga realizada, podemos comprobar que la configuración del servidor OpenLDAP ha cambiado correctamente y que ahora, este módulo, está cargado en el servidor:
 
Módulo syncprov disponible en el servidor
Módulo syncprov disponible en el servidor.

Ahora que el módulo que proporciona las características de replicación está cargado en el servidor, necesitamos habilitar el overlay syncprov en aquellas bases de datos que queramos replicar. Para esto, de una manera similar a como hemos cargado el módulo, tendremos que crear la configuración correspondiente para habilitar el overlay syncprov en la base de datos y aplicar el fichero a la rama cn=config. El proceso sería similar al siguiente:

Configuración overlay syncprov en formato LDIF
Configuración overlay syncprov en formato LDIF.

Carga de la configuración del oberrlay para la base de datos
Carga de la configuración del oberrlay para la base de datos.

De nuevo, podemos comprobar que ahora el overlay se ha añadido correctamente a la base de datos que queremos replicar:

Overlay syncprov habilitado para la base de datos hdb
Overlay syncprov habilitado para la base de datos hdb.
 
Con esta configuración realizada, podemos comprobar que el árbol de directorio tiene un atributo contextCSN en la raíz del árbol que podemos obtener con el siguiente comando ldapsearch:

Atributo contextCSN de base de datos
Atributo contextCSN de base de datos.
 
Como ya he comentado, basándose en las diferencias entre el atributo contextCSN del servidor provider y el de los servidores consumer, se realiza la replicación de aquellos cambios producidos en el provider. Este atributo se mantiene en memoria y solo se escribe en la base de datos cuando el servidor OpenLDAP se para correctamente. Para generar checkpoints que modifiquen el valor de este atributo, asegurando así la correcta sincronización de los cambios entre los diferentes servidores de la topología de replicación que establezcamos, especificamos con el atributo olcSpCheckpoint que se produzca un checkpoint, y por tanto un cambio del valor del atributo contextCSN cada cierto número de operaciones o si ha pasado un cierto número de minutos. La definición del atributo es:

olcSpCheckpoint <NumOps> <Minutes>
 
con lo que establecemos cuando queremos que se genere un nuevo checkpoint en la base de datos. Es importante entender que, este checkpoint, se realiza tras una operación de modificación correcta y siempre que se hayan realizado tantas operaciones correctas o hayan pasado tantos minutos como especifiquemos desde el último checkpoint. Una forma simple de verlo es realizar varios cambios manualmente y comprobar el atributo contextCSN antes y después de realizar los cambios:

Cambio de contextCSN
Cambio de contextCSN.

Como podemos ver, entre la primera consulta y la segunda, haciendo cambios manualmente en la base de datos, el servidor OpenLDAP ha forzado un checkpoint provocando el cambio del atributo contextCSN.

¿Como obtiene OpenLDAP este contextCSN? aplicando el valor del atributo entryCSN más alto que haya en la base de datos. Cada vez que modificamos o creamos una entrada nueva, el atributo entryCSN, que básicamente es una marca de tiempo, indica cuando se hizo la última modificación sobre la entrada. Por ejemplo, revisando los atributos del siguiente objeto de clase account, vemos lo siguiente:

Un objeto de tipo account
Un objeto de tipo account.
 
Como podemos ver, la parte inicial del atributo entryCSN coincide con el atributo createTimestamp, indicando así la fecha de creación del objeto. La diferencia entre ambos atributos es que entryCSN registra el timestamp incluyendo microsegundos y ciertos valores adicionales, como un contador del número de operación y un identificador de réplica, para dar más granularidad a dicho atributo y poder controlar mejor las replicaciones. Si comparamos el valor del atributo entryCSN de este objeto, con el valor actual del atributo contextCSN vemos que ambos coinciden:

Valor de atributo contextCSN
Valor de atributo contextCSN.
 
Al modificar esta entrada, por ejemplo al cambiar el atributo description, vemos como cambia el valor del atributo entryCSN y, el valor del atributo contextCSN pasa a ser el nuevo valor del atributo entryCSN de esta entrada:
 
Nuevo valor del atributo entryCSN
Nuevo valor del atributo entryCSN.
 
Nuevo contextCSN tras checkpoint.
 
Es importante recordar que el checkpoint se forzará cada cierto número de operaciones correctas, o si han pasado tantos minutos como especifquemos, desde el último checkpoint. En este ejemplo solo he realizado un cambio, pero el checkpoint en este caso se ha realizado por el número de minutos pasados desde el anterior checkpoint.
 
Una vez que hemos establecido la configuración necesaria en el servidor provider, continuamos con la configuración de aquellos servidores que designemos como consumers o réplicas. Ya que nuestros consumers también serás servidores Kerberos, debemos realizar todas las configuraciones necesarias para que los servicios kdc y kadmin utilicen como base de datos el servidor OpenLDAP como ya vimos esta entrada.

Al igual que hemos hecho con el servidor provider, debemos cargar el módulo syncprov, para habilitar las características de replicación y añadir un atributo específico a la base de datos que queremos usar como destino de la replica. Para cargar el módulo podemos utilizar el mismo LDIF que empleamos para el provider y el atributo a añadir a la base de datos, en una de sus formas más simples, es más o menos el siguiente:
 
Atributo olcSyncRepl para establecer la réplica
Atributo olcSyncRepl para establecer la réplica.
 
Este atributo contiene las siguientes opciones de configuración que merece la pena explicar:
  • La linea rid, establece la identidad del consumer. Debe ser un número de tres dígitos.
  • La línea provider establece el nombre o dirección IP del servidor consumer.
  • La linea type establece el tipo de replicación que se utilizará. Existen dos tipos diferentes, refreshOnly y refreshAndPersist, cuyas diferencias veremos un poco más abajo.
  • La línea retry indica la estrategia de reintentos en casoo de fallo de conexión. Establece parejas de intervalo de reintento y número de reintentos. En este ejemplo, el consumer reintentará la conexión cada 5 segundos las 5 primeras veces y, si no es capaz de conectar, cada 300 segundos indefinidamente.
  • La linea searchbase establece la base donde se realizará la búsqueda de cambios en el provider.
  • La línea attrs establece que atributos van a replicarse. La especificación *,+ establece que se replicarán todos los atributos de usuario y operacionales.
  • Las lineas bindmethod, binddn y credentials, establecen que tipo de bind y el usuario que utilizará el servidor consumer para conectarse al provider y realizar la búsqueda necesaria para realizar la replicación. Como es lógico, dicho DN debe existir en el servidor provider.
Es muy importante crear una ACL dedicada para el DN que usaremos como replicador. Ese DN debe tener permisos de lectura en todo el árbol de directorio, salvo que haya OUs u otros objetos específicos que no queramos replicar. Esto es especialmente importante para el contenedor del reino Kerberos, que creamos y está controlado por un DN específico, como vimos en el post sobre la inegración de Kerberos con un backend OpenLDAP.

Uno de los puntos más importantes de esta configuración es el tipo de replicación a emplear. Como ya he indicado existen dos tipos, cuyas principales características son:
  • Tipo refreshOnly. En este caso, el servidor consumer se conecta al servidor provider, realiza la búsqueda de todas las modificaciones, realiza la sincronización y cierra la conexión. Cada cierto tiepo, establecido en la configuración del consumer, repite la misma operación. Por tanto podemos ver este tipo de replicación como una replicación de tipo pull, en la que el consumer siempre inicia la operación de replicación de información.
  • Tipo refreshAndPersist. En este caso, el servidor consumer se conecta al servidor provider, realiza la búsqueda de todas las modificaciones, realiza la sincronización y la conexión se mantiene establecida. En este caso, cualquier modificación realizada al provider es inmediatamente propagada al consumer. Este tipo de replicación podemos verla como una replicación de tipo push, en la que tras la conexión inicial, el provider envía los cambios realizados.

Una vez que tenemos el atributo listo podemos cargarlo directamente usando un comando ldapadd del siguiente modo:

Modificación de la base de datos de réplica
Modificación de la base de datos de réplica.

Una vez establecida la configuración en el servidor réplica, si no se nos ha escapado la correcta configuración de ningún posbile cortafuegos y la base de datos a replicar no es excesivamente grande, podremos ver como el servidor réplica sincroniza el contenido completo de la base de datos del provider sin problemas:

Base de datos del provider
Base de datos del provider.

 

Base de datos del consumer
Base de datos del consumer.

Como podemos ver, el contenido de ambas bases de datos es idéntico y, si nos fijamos en el atributo contextCSN del raíz de ambas bases de datos, vemos que coinciden perfectamente.

Hasta aquí como realizar la configuración necesaria para establecer una replicación entre servidores OpenLDAP, en entradas posteriores mejoraremos la seguridad de esta conexión y trabajaremos con las ACLs para controlar el acceso a los objetos y atributos del servidor LDAP.