Se recomienda realizar una previa lectura de los posts Instalación y configuración inicial de OpenLDAP y Configuración LDAPs, pues en este artículo se tratarán con menor profundidad u obviarán algunos aspectos vistos con anterioridad.

El objetivo de esta tarea es el de continuar con la configuración del escenario de trabajo previamente generado en OpenStack, concretamente, añadiendo un servidor LDAP secundario en la máquina Sancho para así ofrecer alta disponibilidad al servidor LDAP principal, ubicado en la máquina Freston.

Hasta ahora hemos hecho uso de Freston como único servidor LDAP, pero en una situación real, depender de un único servidor que ofrezca un servicio tan importante como es LDAP es una práctica muy arriesgada, ya que de dicho servicio dependen muchos otros y por tanto, en caso de que ocurriese un fallo en dicha máquina, las consecuencias serían bastante considerables.

Por ello, se suele practicar la redundancia o alta disponibilidad, consistente en configurar al menos un servidor LDAP secundario que será capaz de dar servicio a los clientes mientras se solucionan los hipotéticos problemas en el servidor principal, haciendo para ello uso del motor LDAP Sync Replication, más comúnmente conocido como syncrepl. Además, la implementación OpenLDAP es extremadamente flexible y soporta por tanto una variedad de modos de funcionamiento de dicho motor para diversos escenarios, que pasaré a explicar a continuación:

  • syncrepl: Es el modo de funcionamiento básico, el típico maestro-esclavo, llamado en LDAP provider-consumer. Consiste en copiar un fragmento del árbol de información del directorio (DIT) en el servidor secundario (consumer) mediante una lectura en el servidor principal (provider), seguido de periódicas actualizaciones de dicha información, haciendo uso de uno de los métodos que se mencionarán a continuación. El motor de replicación, por tanto, se ejecuta en el consumer como un thread de slapd.

    • pull-based: El consumer pregunta periódicamente al provider por actualizaciones en dicha información.
    • push-based: El consumer escucha para recibir dichas actualizaciones por parte del provider en tiempo real.

    • Desventajas:
      • Es un mecanismo basado en objetos, es decir, cuando se modifica un único atributo de un objeto replicado, el consumer procesa el objeto en su totalidad, incluyendo los atributos modificados y los no modificados, causando un gasto de recursos innecesario.
      • Únicamente se permiten escrituras en el provider, ya que los servidores secundarios consultan al mismo para obtener dichas actualizaciones.
    • Ventajas:
      • En caso de producirse varios cambios en un solo objeto, no es necesario conservar una secuencia precisa de dichos cambios, pues únicamente el estado final de la entrada es significativo.
      • Únicamente debe llevarse a cabo la configuración en el lado del consumer.
  • Delta-syncrepl: Es una variante de syncrepl que pretende solventar la primera de las desventajas anteriormente mencionadas, almacenando el provider para ello un registro de cambios (changelog) en una base de datos separada. El consumer consultará dicho registro y en caso de encontrar diferencias, aplicará los cambios a su base de datos. Si el consumer se encontrase muy separado del estado actual del provider o sin sincronización alguna, se hará uso de syncrepl para la primera sincronización, y a partir de ahí, se hará uso del método Delta.

    • Desventajas:
      • Dado que el estado de la base de datos en el lado del provider se almacena tanto en el registro de cambios como en la propia base de datos en sí, será necesario copiar o restaurar ambas partes en caso de una corrupción o migración.
      • Únicamente se permiten escrituras en el provider, ya que los servidores secundarios consultan al mismo para obtener dichas actualizaciones.
      • La configuración debe llevarse a cabo tanto en el lado del consumer como en el lado del provider.
    • Ventajas:
      • Cuando se modifican los atributos de un objeto replicado, el consumer procesa únicamente los cambios realizados, obteniendo una mayor eficiencia en las transferencias y evitando causar un gasto de recursos innecesario.
  • N-Way Multi-Provider: Es una variante que hace uso del método syncrepl para replicar la información entre múltiples providers (multi-master).

    • Desventajas:
      • Puede llegar a romper la consistencia de los datos existentes en el directorio, ya que si por problemas de red unos clientes tienen conectividad con un provider y otros clientes con otro diferente, posteriormente puede ser complicado unificar la información de ambos.
    • Ventajas:
      • Si un provider falla, otros podrán seguir atendiendo peticiones, evitando por tanto el punto único de fallo.
      • Los proveedores se podrán situar en lugares distintos físicamente hablando, combatiendo gracias a ello otros agentes que pudiesen ocasionar problemas en el sistema (terremotos, inundaciones…).
  • MirrorMode: Es un método híbrido que aporta todas las garantías de consistencia de los datos de las réplicas en las que actúa un único proveedor, además de proveer la alta disponibilidad de las réplicas en las que intervienen varios. Consiste en configurar dos proveedores que se repliquen entre sí (como en el método N-Way Multi-Provider) pero utilizando una interfaz externa (frontend) que dirija todas las escrituras a un único proveedor, de manera que el secundario únicamente se utilizaría en caso de que el principal dejase de ofrecer servicio. Posteriormente, cuando se restaurase el proveedor principal, todos los cambios se sincronizarían con el secundario, al haber sido este último el que ha estado recibiendo escrituras durante ese periodo.

    • Desventajas:
      • No consiste exactamente en una solución Multi-Provider, ya que las escrituras únicamente se llevan a cabo en uno de los nodos (aunque posteriormente son replicadas).
      • Necesita un servidor externo (slapd en modo proxy) o dispositivo (balanceador de carga físico) para comprobar qué proveedor se encuentra actualmente activo.
    • Ventajas:
      • Proporciona alta disponibilidad para las escrituras en el directorio, de manera que mientras exista un proveedor operativo, las escrituras van a ser aceptadas.
      • Los proveedores se replican entre sí, de manera que la información está siempre actualizada en todos los nodos.
      • En caso de que uno de los proveedores deje de prestar servicio, se volverá a sincronizar automáticamente cuando vuelva a estar operativo.

En mi opinión, el método de funcionamiento que más se adapta a mi escenario es MirrorMode, pues es un modo híbrido que unifica las ventajas de los anteriores métodos a cambio de unas desventajas que prácticamente carecen de importancia.

Como anteriormente hemos mencionado, el único servidor LDAP actualmente activo en nuestro escenario es Freston, que cuenta además con el protocolo ldaps:// configurado. Sin embargo, no ocurre lo mismo con Sancho, por lo que tendremos que llevar a cabo en dicha máquina las modificaciones mencionadas en los artículos Instalación y configuración inicial de OpenLDAP y Configuración LDAPs, para que así cuente con la misma configuración inicial. No es necesario generar también los objetos de prueba en el servidor secundario, ya que posteriormente se replicarán de forma automática del servidor principal.

Vamos a comenzar por llevar a cabo la configuración en el servidor principal, es decir, en Freston, generando en el directorio un usuario que cuente con los privilegios suficientes para leer todas las entradas existentes en el mismo, con la finalidad de poder replicarlas en el servidor secundario sin necesidad de hacer uso del usuario administrador, además de poder añadir nuevas entradas para así mantener en todo momento una réplica exacta. Para ello, haremos uso de los objectClass de nombre account y simpleSecurityObject, debiendo indicar por tanto, de forma obligatoria, el valor de los atributos uid y userPassword.

Antes de proceder con la creación, tendremos que encriptar una contraseña que posteriormente asignaremos a dicho usuario, haciendo para ello uso de slappasswd. En este caso, el comando a ejecutar sería:

root@freston:~# slappasswd
New password:
Re-enter new password:
{SSHA}TlTAeN7S3B6vYx9JWPv/oSx0uYO2vmt9

Tras introducir en dos ocasiones la contraseña por teclado, se nos habrá mostrado la cadena encriptada haciendo uso del algoritmo SSHA, que debemos asignar al atributo userPassword.

Como ya bien sabemos, para interactuar con los objetos del directorio debemos utilizar ficheros .ldif, de manera que en este caso, generamos un fichero de nombre usuariocopia.ldif, haciendo para ello uso del comando:

root@freston:~# nano usuariocopia.ldif

Dentro del mismo, introduciré el valor de los atributos necesarios para la creación de un usuario de nombre mirrormode, que no considero necesario explicar con detalle ya que en anteriores artículos lo hemos tratado con mayor detenimiento, quedando de la siguiente manera:

dn: uid=mirrormode,dc=alvaro,dc=gonzalonazareno,dc=org
objectClass: account
objectClass: simpleSecurityObject
uid: mirrormode
description: Usuario para MirrorMode
userPassword: {SSHA}TlTAeN7S3B6vYx9JWPv/oSx0uYO2vmt9

Una vez definido el fichero .ldif, tendremos que importarlo para así añadir el nuevo objeto a la estructura del directorio, haciendo para ello uso de uno de los binarios que nos ofrecen las ldap-utils, de nombre ldapadd:

root@freston:~# ldapadd -x -D "cn=admin,dc=alvaro,dc=gonzalonazareno,dc=org" -f usuariocopia.ldif -W
Enter LDAP Password:
adding new entry "uid=mirrormode,dc=alvaro,dc=gonzalonazareno,dc=org"

Donde:

  • -x: Especificamos que se haga uso de la autenticación simple en lugar de SASL.
  • -D: Indicamos el nombre distintivo del usuario del que queremos hacer uso para realizar la inserción.
  • -f: Indicamos el fichero en el que se encuentra definido el objeto a insertar.
  • -W: Indicamos que se solicite la contraseña del usuario de manera oculta, en lugar de introducirla en el propio comando.

Como se puede apreciar en la salida del comando ejecutado, el usuario ha sido correctamente añadido. Sin embargo, de nada nos sirve dicho usuario si no cuenta con los privilegios necesarios para poder leer y escribir en el directorio, de manera que tendremos que crear un nuevo fichero .ldif de nombre permisoscopia.ldif en el que asignaremos dichos privilegios haciendo uso de las listas de control de acceso (ACL), ejecutando para ello el comando:

root@freston:~# nano permisoscopia.ldif

Explicar el funcionamiento de las listas de control de acceso sería bastante extenso, además de salirse del objetivo del artículo, pues en Internet puede encontrarse bastante información al respecto. El resultado final del fichero sería:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: to attrs=userPassword
  by self =xw
  by dn.exact="cn=admin,dc=alvaro,dc=gonzalonazareno,dc=org" =xw #Modificar por el nombre distintivo del administrador del directorio.

  by dn.exact="uid=mirrormode,dc=alvaro,dc=gonzalonazareno,dc=org" read #Modificar por el nombre distintivo del nuevo usuario generado.

  by anonymous auth
  by * none
olcAccess: to *
  by anonymous auth
  by self write
  by dn.exact="uid=mirrormode,dc=alvaro,dc=gonzalonazareno,dc=org" read #Modificar por el nombre distintivo del nuevo usuario generado.

  by users read
  by * none

Una vez definido el fichero .ldif, tendremos que importarlo para así modificar los atributos del objeto correspondiente a la configuración, haciendo para ello uso de ldapmodify, pero utilizando en este caso unos parámetros concretos, haciendo para ello uso del comando:

root@freston:~# ldapmodify -Y EXTERNAL -H ldapi:/// -f permisoscopia.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
modifying entry "olcDatabase={1}mdb,cn=config"

Donde:

  • -Y: Especificamos un mecanismo SASL a utilizar para la autenticación, en este caso, EXTERNAL.
  • -H: Indicamos la URI de conexión al servidor LDAP, haciendo uso en este caso del socket UNIX (ldapi:///) para así contar con los privilegios necesarios para llevar a cabo modificaciones en la configuración.
  • -f: Indicamos el fichero en el que se encuentran definidos los cambios a realizar.

Como se puede apreciar, la entrada olcDatabase={1}mdb,cn=config ha sido correctamente modificada dado que contamos con los privilegios necesarios para ello al haber hecho uso del socket UNIX, de manera que el usuario ya tiene asignados los permisos que necesita.

Tras ello, tendremos que cargar en memoria el módulo encargado de llevar a cabo la sincronización entre los servidores, de nombre syncprov, de manera que tendremos que crear un nuevo fichero .ldif de nombre modulocopia.ldif en el que modificaremos la configuración del directorio para así añadir dicho módulo, ejecutando para ello el comando:

root@freston:~# nano modulocopia.ldif

El resultado final del fichero sería:

dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: syncprov

Una vez definido el fichero .ldif, tendremos que importarlo para así modificar los atributos del objeto correspondiente a la configuración, haciendo uso una vez más del comando:

root@freston:~# ldapmodify -Y EXTERNAL -H ldapi:/// -f modulocopia.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
modifying entry "cn=module{0},cn=config"

Como se puede apreciar, la entrada cn=module{0},cn=config ha sido correctamente modificada, de manera que el módulo necesario ya ha sido cargado en memoria.

El módulo ya está habilitado, pero no cuenta con configuración alguna asignada, de manera que tendremos que crear un nuevo fichero .ldif de nombre modulocopia2.ldif en el que añadiremos la configuración dicho módulo, ejecutando para ello el comando:

root@freston:~# nano modulocopia2.ldif

Dentro del mismo indicaremos la nueva configuración, concretamente añadiendo un atributo olcSpCheckpoint en el que estableceremos cada cuántas operaciones o minutos se va a llevar a cabo un checkpoint, pensado para minimizar la actividad de sincronización después de que un proveedor deje de ofrecer servicio y tenga que recuperarse. En este caso, he considerado que 5 es un buen valor para ambas posibilidades:

dn: olcOverlay=syncprov,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpCheckpoint: 5 5

Una vez definido el fichero .ldif, tendremos que importarlo para así añadir el nuevo objeto a la estructura del directorio, haciendo uso una vez más del comando:

root@freston:~# ldapmodify -Y EXTERNAL -H ldapi:/// -f modulocopia2.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "olcOverlay=syncprov,olcDatabase={1}mdb,cn=config"

Como se puede apreciar, la entrada olcOverlay=syncprov,olcDatabase={1}mdb,cn=config ha sido correctamente añadida al directorio, de manera que el módulo cuenta ahora con la primera parte de la configuración necesaria para su correcto funcionamiento.

Nuestra configuración de MirrorMode está cada vez más cerca de finalizar. El siguiente paso consiste en añadir un número identificativo al servidor, de manera que tendremos que crear un nuevo fichero .ldif de nombre servidorcopia.ldif en el que especifiquemos dicha información, ejecutando para ello el comando:

root@freston:~# nano servidorcopia.ldif

Dentro del mismo estableceremos un atributo olcServerId en el que indicaremos el identificador 1, siendo muy importante que posteriormente, cuando tengamos que llevar a cabo los mismos pasos en el servidor secundario, dicho número sea diferente al que vamos a introducir ahora. El resultado final del fichero sería:

dn: cn=config
changetype: modify
add: olcServerId
olcServerId: 1

Una vez definido el fichero .ldif, tendremos que importarlo para así modificar los atributos del objeto correspondiente a la configuración, haciendo uso una vez más del comando:

root@freston:~# ldapmodify -Y EXTERNAL -H ldapi:/// -f servidorcopia.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
modifying entry "cn=config"

Como se puede apreciar, la entrada cn=config ha sido correctamente modificada, de manera que el servidor se encuentra ahora identificado por el número 1.

Hemos llegado al último paso de la configuración del servidor principal, consistente en indicar algunos parámetros que se utilizarán para la sincronización, así como habilitarla propiamente, de manera que tendremos que crear un nuevo fichero .ldif de nombre habilitarsinc.ldif en el que especifiquemos dicha configuración, ejecutando para ello el comando:

root@freston:~# nano habilitarsinc.ldif

El resultado final del fichero sería:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcSyncrepl
olcsyncrepl: rid=000
  provider=ldaps://sancho.alvaro.gonzalonazareno.org #Modificar por el servidor contrario al que estamos utilizando, es decir, en Freston será Sancho y viceversa. Podemos utilizar la IP en lugar del nombre DNS. Dado que tengo LDAPs correctamente configurado, utilizaré dicho protocolo para así aumentar la seguridad en las transferencias.

  type=refreshAndPersist
  retry="5 5 300 +" 
  searchbase="dc=alvaro,dc=gonzalonazareno,dc=org" #Modificar por la base del directorio que se pretende replicar, que generalmente se encuentra definida a partir del FQDN de la máquina.

  attrs="*,+" 
  bindmethod=simple
  binddn="uid=mirrormode,dc=alvaro,dc=gonzalonazareno,dc=org" #Modificar por el nombre distintivo del nuevo usuario generado.

  credentials=[contraseñaenclaro] #Modificar por la contraseña del nuevo usuario generado.

-
add: olcDbIndex
olcDbIndex: entryUUID eq
olcDbIndex: entryCSN eq
-
replace: olcMirrorMode
olcMirrorMode: TRUE

Una vez definido el fichero .ldif, tendremos que importarlo para así modificar los atributos del objeto correspondiente a la configuración, haciendo uso una vez más del comando:

root@freston:~# ldapmodify -Y EXTERNAL -H ldapi:/// -f habilitarsinc.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
modifying entry "olcDatabase={1}mdb,cn=config"

Como se puede apreciar, la entrada olcDatabase={1}mdb,cn=config ha sido correctamente modificada, de manera que el servidor principal ya se encuentra configurado en su totalidad para hacer uso de MirrorMode.

De otro lado, tendremos que llevar a cabo las mismas modificaciones en la máquina Sancho para que así la conexión sea recíproca, con la única diferencia de que el identificador del servidor (olcServerId) será en esta ocasión 2 y su servidor proveedor (provider) será en esta ocasión ldaps://freston.alvaro.gonzalonazareno.org.

Tras ello, haremos uso de ldapsearch para así ejecutar una búsqueda anónima sobre el directorio del servidor Sancho. En este caso, no quiero establecer ningún tipo de filtro, sino listar todos los objetos existentes en la estructura actualmente definida, ejecutando para ello el comando:

root@sancho:~# ldapsearch -x -b "dc=alvaro,dc=gonzalonazareno,dc=org"

# alvaro.gonzalonazareno.org

dn: dc=alvaro,dc=gonzalonazareno,dc=org
objectClass: top
objectClass: dcObject
objectClass: organization
o: alvaro.gonzalonazareno.org
dc: alvaro

# admin, alvaro.gonzalonazareno.org

dn: cn=admin,dc=alvaro,dc=gonzalonazareno,dc=org
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator

# mirrormode, alvaro.gonzalonazareno.org

dn: uid=mirrormode,dc=alvaro,dc=gonzalonazareno,dc=org
objectClass: account
objectClass: simpleSecurityObject
uid: mirrormode
description: Usuario para MirrorMode

# Personas, alvaro.gonzalonazareno.org

dn: ou=Personas,dc=alvaro,dc=gonzalonazareno,dc=org
objectClass: organizationalUnit
ou: Personas

# Grupos, alvaro.gonzalonazareno.org

dn: ou=Grupos,dc=alvaro,dc=gonzalonazareno,dc=org
objectClass: organizationalUnit
ou: Grupos

# search result

search: 2
result: 0 Success

Donde:

  • -x: Especificamos que se haga uso de la autenticación simple en lugar de SASL.
  • -b: Indicamos la base a partir de la cuál queremos mostrar la información.

Como se puede apreciar, todos los objetos previamente existentes en el directorio de Freston existen ahora también en el directorio de Sancho, por lo que podemos concluir que la sincronización funciona correctamente para aquellos objetos que se inserten en la primera de las máquinas.

De otro lado, vamos a llevar a cabo una pequeña prueba para verificar que funciona también en el sentido inverso, pues he preparado un fichero .ldif en el que he definido un usuario de nombre Pablo, fichero que tendremos que importar para así añadir el nuevo objeto a la estructura del directorio, haciendo uso una vez más del comando:

root@sancho:~# ldapadd -x -D "cn=admin,dc=alvaro,dc=gonzalonazareno,dc=org" -f usuarioprueba.ldif -W
Enter LDAP Password:
adding new entry "uid=pablo,ou=Personas,dc=alvaro,dc=gonzalonazareno,dc=org"

Como se puede apreciar en la salida del comando ejecutado, el usuario ha sido correctamente añadido, así que vamos a volver a la máquina Freston para así ejecutar una búsqueda anónima sobre el directorio de dicho servidor. En este caso, no quiero establecer ningún tipo de filtro, sino listar todos los objetos existentes en la estructura actualmente definida, ejecutando para ello el comando:

root@freston:~# ldapsearch -x -b "dc=alvaro,dc=gonzalonazareno,dc=org"

# alvaro.gonzalonazareno.org

dn: dc=alvaro,dc=gonzalonazareno,dc=org
objectClass: top
objectClass: dcObject
objectClass: organization
o: alvaro.gonzalonazareno.org
dc: alvaro

...

# pablo, Personas, alvaro.gonzalonazareno.org

dn: uid=pablo,ou=Personas,dc=alvaro,dc=gonzalonazareno,dc=org
objectClass: posixAccount
objectClass: inetOrgPerson
uid: pablo
cn: Pablo Ferreras
givenName: Pablo
sn: Ferreras
uidNumber: 2000
gidNumber: 2000
homeDirectory: /home/pablo
loginShell: /bin/bash

# search result

search: 2
result: 0 Success

Efectivamente, podemos concluir que la sincronización ha funcionado también y el usuario añadido existe ahora en ambas máquinas, gracias a la interfaz externa (frontend) que decide de qué manera se escribe la información, de una forma totalmente abstracta y ajena a nosotros.