Hello,

Dernière partie importante de cette série consacrée à OpenLDAP ! Au sommaire la gestion des mises à jour, la supervision, les sauvegardes, notre PCA et PRA.

Mises à jour

Pour ce qui est du conteneur OpenLDAP, on a vu précédemment qu’on avait exclu ce dernier des mises à jour par Watchtower via le label com.centurylinklabs.watchtower.enable: 'false'. Je me suis donc abonné au flux rss des releases Github du projet, et lorsqu’une release sort, je vérifie l’upgrade sur le conteneur de préproduction, si tout fonctionne, on met à jour le conteneur de production.

Les autres conteneurs se mettent à jour (si disponible) à chaque run de Watchtower. Quant à noxinmortus/docker-ldap-tool-box, je suit également les releases de LTB pour re-générer les images lorsque je le juge intérressant.

Supervision

backend monitor & TIG stack

Afin de garantir la disponibilité des services, il nous faut superviser notre OpenLDAP, car sans lui, pas d’authentification. Cela va nous permettre d’être alerté en cas de souci, et donc de pouvoir agir sur le/les problème(s). Un aspect important est également de pouvoir visualiser les performances de notre OpenLDAP dans le temps, afin de garantir des temps de réponses acceptables, mais également d’avoir des données susceptibles de nous aider à diagnostiquer un problème.

Cette surveillance va s’effectuer en plusieurs points, tout d’abord nous allons configurer le backend monitor qui permet d’accéder à certaines métriques propres au serveur OpenLDAP.

Voici ma procédure :

# Activation du module
ldapmodify -Y EXTERNAL -H ldapi:/// -f monitor_module.ldif
# Création d'un l'utilisateur dédié
ldapadd -x -w adminPwd -D "cn=admin,dc=mydomain" -f monitor_user.ldif
# Création du backend monitor
ldapadd -Y EXTERNAL -H ldapi:/// -f monitor_database.ldif

# monitor_module.ldif
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: back_monitor

# monitor_user.ldif
dn: cn=monitor,dc=domain
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: monitor
description: LDAP monitor
userPassword:{SSHA}hash-de-votre-password

# monitor_database.ldif
dn: olcDatabase={2}Monitor,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMonitorConfig
olcDatabase: {2}Monitor
olcAccess: {0}to dn.subtree="cn=Monitor" by dn.base="cn=monitor,dc=domain" read by * none

Attention a ne pas faire d’erreurs sur le fichier monitor_database.ldif, dans mon cas j’avais par exemple renseigné dn.base="cn=monitor,domain" au lieu dn.base="cn=monitor,dc=domain" lors de mon premier essai. Résultat, je ne pouvais pas accéder à olcDatabase={2}Monitor,cn=config, donc impossible à corriger, et vu que l’on ne peut créer qu’un seul backend monitor, obligé de recommencer de zéro.

Je me suis servi de la documentation officielle, appuyée par quelques autres liens :

Une fois ceci fait, j’ai utilisé la stack TIG (Telegraf, InfluxDB, Grafana, que je ne décrirais pas ici) afin de remonter certaines métriques en utilisant plugins/inputs/openldap, et ce dashboard https://grafana.com/grafana/dashboards/13349 pour Grafana.

Zabbix

La stack TIG me permet d’avoir une visualisation sur le moyen/long terme des performances de mes architectures, et j’utilise Zabbix pour la gestion des alertes. Je ne vais pas non plus présenter Zabbix ici, je dirais simplement que c’est un logiciel open-source qui permet de surveiller l’ensemble de mon système d’information. J’utilise un rôle ansible fait maison que vous pouvez trouver ici pour la gestion des mes agents et proxy (mais le rôle est très polyvalent): https://github.com/NoxInmortus/role-zabbix

Pour superviser mon cluster je me suis basé sur ce template : https://github.com/MrCirca/OpenLDAP-Cluster-Zabbix

Il m’a fallu réaliser un certain nombre de modifications pour convenir à mon besoin, et le script ressemble au final à ceci :

#!/usr/bin/env bash
set -euo pipefail

#
# First argument is the local (consumer) LDAP server
# Second argument is the provider LDAP server
# Third argument is the Bind DN
# Fourth argument is the Bind DN password
#
CONSUMER_URI=${1}
PROVIDER_URI=${2}
BINDDN=${3}
BINDDN_PWD=${4}

BASE_DN=$(ldapsearch -LLL -x -ZZ -H ldap://${CONSUMER_URI} -s base -b "" "namingContexts" 2> /dev/null | grep namingContexts | awk '{ print $2 }')
LDAP_CSN_CONSUMER_COMMAND=$(ldapsearch -LLL -x -ZZ -H ldap://${CONSUMER_URI} -D "${BINDDN}" -w "${BINDDN_PWD}" -s base -b "${BASE_DN}" contextCSN 2> /dev/null)
LDAP_CSN_CONSUMER_RC=${?}
LDAP_CSN_PROVIDER_COMMAND=$(ldapsearch -LLL -x -ZZ -H ldap://${PROVIDER_URI} -D "${BINDDN}" -w "${BINDDN_PWD}" -s base -b "${BASE_DN}" contextCSN 2> /dev/null)
LDAP_CSN_PROVIDER_RC=${?}
PROVIDER_CSN=$(echo -e "${LDAP_CSN_PROVIDER_COMMAND}" | grep contextCSN | cut -d " " -f 2)
CONSUMER_CSN=$(echo -e "${LDAP_CSN_CONSUMER_COMMAND}" | grep contextCSN | cut -d " " -f 2)

if [[ "${LDAP_CSN_CONSUMER_RC}" != "0" ]] && [[ "${LDAP_CSN_PROVIDER_RC}" == "0" ]]; then
 	echo "1"

elif [[ "${LDAP_CSN_PROVIDER_RC}" != "0" ]] && [[ "${LDAP_CSN_CONSUMER_RC}" == "0" ]]; then
	echo "2"

elif [[ "${LDAP_CSN_PROVIDER_RC}" != "0" ]] && [[ "${LDAP_CSN_CONSUMER_RC}" != "0"  ]]; then
	echo "3"

elif [[ "${PROVIDER_CSN}" == "${CONSUMER_CSN}" ]]; then
	echo "4"
else
	echo "100"
fi

On rajoute un check sur le port LDAP, et voilà je pense qu’on est pas mal.

Sauvegardes

Le plan de sauvegarde est réalisé en deux parties, tout d’abord une sauvegarde de nos volumes docker, ceux importants étants :

  - openldap-data:/var/lib/ldap
  - openldap-conf:/etc/ldap/slapd.d

Le premier contient la configuration et le second les données.

On à ensuite une sauvegarde de nos exports journaliers au format LDIF, ce qui permettra de pouvoir facilement réaliser des diff, et de faire des restaurations partielles voir complètes selon le besoin :

# Un simple cron avec :
find /var/backups/ldap/ -type f -mtime +30 -delete
ldapsearch -x -LLL -ZZ -H ldap://mydomain.com -D cn=admin,dc=mydomain -w adminPwd -b "dc=mydomain" "(objectClass=*)" > /var/backups/ldap/$(date '+%F').ldap.backup.ldif

Dans cette exemple j’utilise le compte admin pour pouvoir avoir tous les champs, dont userPassword (non disponible par défaut avec l’utilisateur readonly).

Plan de continuité d’activité (PCA)

Les applications que nous allons mettre en place vont utiliser une authentification centralisée grâce à notre cluster OpenLDAP, donc si un noeud de notre cluster tombe, les applications qui utilisent ce noeud, ne vont plus être utilisables. Fait très intérressant, de nombreux outils permettent de renseigner plusieurs noeuds LDAP, et il s’avère que c’est le cas de celui qui est au coeur de notre projet. La configuration LDAP de Nextcloud dispose d’un ensemble de champs dédiés à un Backup (Replica) Host, et permet ainsi d’avoir un système de fail-over pour l’authentification.

Plan de reprise d’activité (PRA)

Pour le cas où notre cluster OpenLDAP tombe entièrement, et non pas juste un noeud, j’ai réalisé plusieurs tests de restauration, soit en restaurant directement les volumes de notre conteneur OpenLDAP (openldap-conf et openldap-data), soit en repartant de zéro et en important à nouveau les configurations et données depuis nos fichiers LDIF.

Dans le premier cas (restauration des volumes sur un noeud), on peut estimer une durée de 5 minutes par noeud. Dans le second cas, on peut estimer l’opération à 10 minutes. Une fois les conteneurs et volumes supprimés, il nous faut redéployer via ansible, puis réaliser les imports LDIF :

# Setup password policy module & overlay
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/ppolicy.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap-custom/ppolicy/ppmodule.ldif

# Only on one node
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap-custom/ppolicy/ppolicyoverlay.ldif

# Setup Monitor
ldapmodify -Y EXTERNAL -H ldapi:/// -f /etc/ldap-custom/monitor/monitor_module.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap-custom/monitor/monitor_database.ldif

# Restore backup (only on one node) # -c is for continuing even if errors occurs
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap-custom/misc/init.ldif -c

# olcAccess for bot.ldap and forcetls (only on one node)
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap-custom/misc/bot.ldap.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap-custom/misc/forcetls.ldif

A noter que les schémas/modules et databases sont à ré-importer sur chaque noeud.

Merci à ma chérie pour la relecture.

A+