sábado, 10 de abril de 2021

Servidor DNS con backend OpenLDAP

Hoy continuamos con la serie sobre OpenLDAP y después de la anterior entrada sobre el bastionado básico de la arquitectura, pasamos a añadir una nueva pieza a la solución, concretamente un servidor DNS con backend LDAP.

Al añadir este nuevo elemento, la arquitectura de alto nivel quedaría más o menos así:

Relaciones entre los componentes de la solución.

Como podemos ver en el diagrama, el servidor DNS será un nuevo cliente LDAP que empleará el servidor OpenLDAP como backend para la información de zonas DNS. La principal ventaja de utilizar esta solución es que no necesitamos un servidor DNS que replique sus bases de datos, ya que la capacidad de replicación la proporciona directamente el servidor OpenLDAP.

Dentro de todas las posibles opciones de servidores DNS que puedan emplear un servidor OpenLDAP como backend, vamos a probar PowerDNS. Este servidor DNS dispone de un plugin específico para soportar servidores LDAP como backend y en general está disponible en cualquier distribución Linux. Otra característica adicional que puede ser intersante en ciertos despliegues, es que PowerDNS separa las funciones de servidor DNS autoritativo de las funciones del servidor DNS que realiza consultas recursivas. En el caso quue nos ocupa, usaremos el servidor autoritativo, ya que queremos crear un pequeño dominio DNS de pruebas, que nunca realizará consultas a servidores DNS externos.

En el caso de CentOS 7.8, PowerDNS se encuentra disponible en el repo EPEL, así que tras instalar y habilitar dicho repo, podemos instalar el servidor PowerDNS así como las herramientas y el backend LDAP:

Instalación de PowerDNS en CentOS.

De forma similar, podemos instalar PowerDNS en Debian empleando los comandos de gestión de paquetes de Debian.

Paquetes PowerDNS en Debian.

Al igual que hicimos al integrar Kerberos, es necesario ampliar el esquema de OpenLDAP para poder almacenar las clases de objeto y atributos requeridos por PowerDNS. Los ficheros de esquema correspondientes están incluidos con la instalación, encontrándose en la ruta /usr/shared/doc/pdns-backend-ldap-4.1.14 en el caso de CentOS mientras que, en el caso de Debian, se almacenan directamente en la ruta /etc/ldap/schema de instalación del servidor OpenLDAP:


Ficheros de esquema necesarios en CentOS.

Ficheros de esquema necesarios en Debian.

En el caso de CentOS hay dos ficheros de esquema diferentes, uno extiende el esquema del servidor incluyendo información necesaria para la integración de PowerDNS y el otro define los atributos y objetos necesarios para almacenar infomación de registros y zonas DNS. En el caso de Debian solo existe un fichero, el cual define  los atributos y objetos necesarios para almacenar infomación de registros y zonas DNS. Ya que estamos ampliando el esquema de ambos servidores, crearemos un único fichero de extensión de esquema y lo usaremos en ambos.

Para extender el esquema de un servidor OpenLDAP necesitamos crear una nueva entrada en el árbol de configuración cn=config, para lo cual es necesario que usemos el comando ldapadd para cargar el esquema. Es importante tener en cuenta que los ficheros proporcionados con las instalaciones, tanto en CentOS comno en Debian no están en formato LDIF, con lo que no podemos usar los ficheros directamente con el comando ldapadd. Es necesario corregir dichos ficheros, añadiendo las modificaciones necesarias para pasarlos a formato LDIF y así poder usarlos con los comandos ldapadd y ldapmodify. Tras un poco de vi, los ficheros quedarán más o menos así:

Modificación ficheros de schema PowerDNS.
 
Modificación ficheros schema PowerDNS.

Es muy importante tener en cuenta que, al estar dividido en dos ficheros, es necesario continuar los índices numéricos de cada atributo y clase de objeto para poder hacer la carga de forma correcta en el árbol de configuración dinámica.

Para cargar el primer fichero utilizaremos el comando ldapadd y para el segundo ldapmodify, ya que estaremos modificando una entrada ya existente. Los comandos a utilizar, teniendo en cuenta que ahora los servidores requiren TLS para las conexiones, serán más o menos así:

Carga de schema pdns-domaininfo

Carga de schema dnsdomain2

Podemos comprobar que el esquema se ha ampliado correctamente comprobando que aparece dentro de la rama cn=schema del árbol de configuración dinámica cn=config:

 

Esquema del servidor OpenLDAP.

Una vez que hemos realizado la extensión del esquema de ambos servidores OpenLDAP correctamente, podemos empezar a configurar PDNS. El fichero principal de configuración es /etc/pdns/pdns.conf y en este fichero debemos establecer, al menos, las siguientes variables de configuración:

  • local-address, establece la dirección IP en la que escuchará el servicio PDNS.
  • launch, establece que backend empleará PDNS. En nuestro caso especificaremos ldap.
  • ldap-host, nos permite establecer la URL del servidor LDAP que actuará como backend.
  • ldap-starttls, para indicar que debe usarse StarTLS en las conexiones con el servidor LDAP.
  • ldap-bindmethod, esta opción puede tomar diferentes valores. De momento especificaremos simple para usar un DN específico para la conexión y consultas del servidor LDAP por parte de PDNS.
  • ldap-binddn y ldap-secret, DN y password usada para la conexión y consultas de PDNS con el servidor OpenLDAP.
  • ldap-basedn, DN a partir del cual PDNS realziará la búsqueda de entradas. 
  • ldap-method, esta opción puede tomar varios valores y básicamnte establece como se traslada una query DNS a la estructura de la zona dentro del servidor de directorio. La estableceremos en strict y luego analizaremos más detenidamente que significa.

Es importante resaltar que, al utilizar las bibliotecas LDAP cliente del sistema operativo, el fichero de configuración LDAP (/etc/ldap.conf o /etc/openldap/ldap.conf), debe estar correctamente configurado. Para poder iniciar correctamente la conexión StartTLS es muy importante la opción TLS_CACERT. Esta opción debe establecer la ruta completa al fichero que contenga el certificado de la CA que haya firmado el certificado usado por el servidor OpenLDAP.

El fichero de configuración de PowerDNS, en lo que respecta solamente a la configuración del backend LDAP, quedaría más o menos así:

Configuración PowerDNS para backend LDAP.

Una vez configuradas todas estas opciones, antes de arrancar el servicio PDNS, necesitamos construir la información básica de la zona DNS en el servidor OpenLDAP. Es en este punto donde necesitamos distinguir los diferente valores de la opción de configuración ldap-method, siendo el significado de cada uno de ellos el siguiente:

  • En modo simple podremos crear la estructura del árbol de directorio como deseemos. Cada consulta al servidor DNS se traducirá en una búsqueda del atributo associatedDomain de las entradas de la clase de objeto domainRelatedObject.
  • El modo strict es como el modo simple pero permite que PowerDNS devuelva las resoluciones inversas, las entradas PTR, a partir de las entradas directas, con lo que no es necesario crear entradas de tipo PTR.
  • Por último, el modo tree nos obliga a crear una estructura de directorio que coincida con nuestro dominio, es decir, una entrada A para un host como server1.lab.com se traduciría a un DN que sería dc=server1,dc=lab,dc=com,.... con lo que debemos crear las entradas teniendo en cuenta este modo de operación.

Al configurar la opción ldap-method en modo strict, podemos crear una unidad organizativa, que será la base de la zona o zonas de nuestro servidor DNS, añadiendo el objeto que define la zona y varias entradas de servidores, de una forma tan simple como la siguiente:

Estructura básica de zona DNS.

Con esta información básica ya creada en el servidor OpenLDAP, podemos arrancar el servicio pdns y comprobar que el servidor responde a las queries DNS que realicemos desde clientes.

Es importante tener en cuenta que PowerDNS considera que el servidor, con este tipo de configuración no debe ser configurado como master, ya que el backend es el encargado de realizar la replicación de los objetos de la zona DNS.

Con esto ya tenemos listo un servidor DNS integrado con el resto de la infraestructura basada en OpenLDAP. Los siguientes pasos a seguir, para continuar bastionando el sistema, será comenzar a usar Kerberos para las conexiones entre los diferentes elementos, lo cual nos permitirá eliminar contraseñas de ficheros de configuración, así como seguir aplicando opciones recomendadas de seguridad.

jueves, 1 de abril de 2021

Integración de OpenSSL con GnuTLS y el atributo olcTLSCipherSuite

Hoy una entrada rápida sobre OpenSSL y GnuTLS o, más concretamente, sobre los casos en los que, en una arquitectura, existen sistemas que emplean una u otra.

Ambas soluciones son bibliotecas que implementan los protocolos SSL y TLS que nos permiten, entre otras cosas, cifrar las comunicaciones entre sistemas. Es importante saber que hay distribuciones Linux que emplean OpenSSL, como CentOS, mientras que otras emplean GnuTLS, como es el caso de Debian. Por tanto, si en una arquitectura ya existente, es necesario integrar un nuevo servicio que empleará cifrado o si estamos bastionando la arquitectura por motivos de seguridad, es muy importante tener en cuenta las diferencias entre ambas bibliotecas.

La primera diferencia a tener en cuenta entre ambas bibliotecas es como se definen las cifras y protocolos, que luego podremos configurar en cualquier servicio empleando las opciones correspondientes. De forma simple, podemos consultar las cifras soportadas o disponibles en ambos casos del siguiente modo:

Consultando cifras disponibles en OpenSSL.

Consultndo cifras disponibles en GnuTLS.

Una de las primeras diferencias que podemos observar, es como se especifican las cifras en el caso de GnuTLS. Para poder ver la lista de cifras soportadas, así como protocolos y su versión, es necesario que especifiquemos una cadena de prioridad, las cuales son una manera simple y compacta de especificar un conjunto de cifras, protocolos, algortimos de intercambio de claves, etc. Las cadenas de pioridad disponibles, así como su significado, están disponibles aquí y quizás son la parte más importante para integrar correctamente un sistema que está trabjanado con OpenSSL con otro que emplea GnuTLS.

En concreto ¿cual es el problema al hacer este tipo de integraciones? Recordando el post en el que comenzábamos el bastionado de una arquitectura basada en OpenLDAP, desplegábamos unos certificados de servidor para cifrar las comunicaciones entre los diferentes elementos, principalmente para proteger el tráfico de replicación entre ambos servidores OpenLDAP.

Si generamos el certificado de servidor con OpenSSL, utilizando una configuración por defecto y luego fijamos que conjuntos de cifras queremos utilizar en cada extremo, en este caso en cada servidor OpenLDAP, nos encontraremos con problemas de compatibilidad derivados del hecho de que no todas las cifras o algoritmos estarán incluidas en cada implementación.

Recordando que un servidor OpenLDAP que replica información desde otro podemos verlo como un cliente LDAP, al establecer la conexión y solicitar la extensión StartTLS, realizará una comprobación del certificado proporcionado por el servidor y dicha conexión fallará en caso de encontrarse algún algoritmo o cifrado no soportado.

Podemos comprobar este problema de una manera muy sencilla utilizando el comando gnutls-cli, con las opciones necesarias para establecer una conexión StartTLS con un servidor remoto y especificando la cadena de prioridad que queremos utilizar en el lado cliente. Por ejemplo, desde el servidor OpenLDAP réplica, podemos conectar con el servidor OpenLDAP master y comenzar uan conexión StartTLS con un comando como el siguiente:

Conexión mediante gnutls-cli - SECURE128.

En este ejemplo, especificando como parámetros la ruta al fichero que contiene el certificado de la CA que ha firmado el certificado del servidor remoto, así como que queremos establecer una conexión TLS mediante protocolo LDAP, el certificado se valida y la conexión TLS se establece correctamente. El punto importante del comando es la cadena de prioridad que hemos especificado, en este caso la denominada SECURE128. La documentación de GnuTLS establece que al especificar SECURE128, se están empleando aquellas cifras consideradas seguras que ofrecen un nivel de seguridaad de 128 bits o superior y que se establece un perfil de comprobación de certificado bajo.

Si repetimos el comando pero pasamos a especificar una cadena de prioridad SECURE192, en la cual se están empleando cifras consideradas seguras que ofrecen un nivel de seguridaad de 192 bits o superior y que se establece un perfil de comprobación de certificado alto, veremos que sucede lo siguiente:

Conexión mediante gnutl-cli - SECURE192.

Por tanto, al incrementar el nivel de seguridad que deseamos usar, el certificado del servidor ya no es válido por usar un algoritmo inseguro, basado en la cadena de prioridad que hemos especificado en este caso.

Esta configuración es la que establecemos en el atributo olcTLSCipherSuite de OpenLDAP, el cual debe estar soportado por la biblioteca SSL usada para comnpilar el servidor OpenLDAP. Así, para un OpenLDAP que emplea OpenSSL, el valor de dicho atributo podría ser TLSv1.2, mientras que para un servidor OpenLDAP que emplea GnuTLS, el valor de dicho atributo debería ser una de las cadenas de prioridad soportadas, por ejemplo SECURE128.

Por tanto y en resumen, a la hora de tener que integrar sistemas que usen diferente biblioteca SSL, es muy importante analizar que algoritmos, cifras, protocolos de intercambio de claves, etc. están soportados en cada caso y cuales están incluidos si usamos las cadenas de prioridad de GnuTLS, pero, en la medida de lo posible, lo más sencillo es usar la misma distribución para evitar este tipo de problemas.