Le paquet slapd a déjà été installé « sans configuration » dans la partie Section 3.3, « Installer les paquets TLS et générer les certificats », uniquement pour disposer du compte système openldap nécessaire au dépôt des certificats TLS. Les certificats (/etc/ldap/certs/) et le fichier /etc/ldap/ldap.keytab sont donc déjà en place, mais le démon slapd n'a encore aucune configuration cn=config. Il reste en échec de démarrage tant que le contexte de nommage n'est pas défini.
Cette partie reprend les étapes décrites dans le support Introduction aux annuaires LDAP avec OpenLDAP et les adapte à ce scénario en trois sous-parties :
-
Initialiser une configuration minimale du service avec le contexte de nommage
dc=lab,dc=local. -
Activer le chiffrement TLS et l'authentification forte SASL/GSSAPI, à partir des certificats et du
keytabdéjà déployés. -
Construire l'arbre de l'annuaire (unités organisationnelles
users,groupsethosts) puis y implanter les comptes utilisateurs.
Contrairement au support Introduction aux annuaires LDAP avec OpenLDAP, il ne reste ici qu'à générer la configuration cn=config et à l'injecter dans le paquet déjà installé.
|
Q193. |
Comment générer et appliquer une configuration minimale pour ce nouveau contexte de nommage ? |
|||
|
Créez le script cat << 'SCRIPT_EOF' >prepare-apply-bootstrap-ldif.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Global variables
readonly DOMAIN="lab.local"
# Extract suffix for domain (e.g. lab.local -> dc=lab,dc=local)
readonly SUFFIX="dc=${DOMAIN%%.*},dc=${DOMAIN#*.}"
readonly SLAPD_CONFIG_DIR="/etc/ldap/slapd.d"
readonly SLAPD_DATA_DIR="/var/lib/ldap"
readonly LDAP_BOOTSTRAP_FILE="${HOME}/bootstrap-config.ldif"
readonly ADMIN_PASS_FILE="${HOME}/.ldap_admin_pass"
echo "=== 1. Generating administrator credentials ==="
# 18 random bytes yield exactly 24 base64 characters without padding
ADMIN_PASS=$(openssl rand -base64 18)
( umask 077 && printf '%s' "${ADMIN_PASS}" > "${ADMIN_PASS_FILE}" )
# Compute Argon2 password hash for slapd
ROOT_HASH="$(sudo slappasswd -o module-load=argon2.la -h "{ARGON2}" -s "${ADMIN_PASS}")"
echo "=== 2. Generating cn=config bootstrap LDIF ==="
cat <<EOF >"${LDAP_BOOTSTRAP_FILE}"
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /run/slapd/slapd.args
olcPidFile: /run/slapd/slapd.pid
olcLogLevel: none
olcToolThreads: 1
dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema
include: file:///etc/ldap/schema/core.ldif
include: file:///etc/ldap/schema/cosine.ldif
include: file:///etc/ldap/schema/nis.ldif
include: file:///etc/ldap/schema/inetorgperson.ldif
dn: cn=module{0},cn=config
objectClass: olcModuleList
cn: module{0}
olcModulePath: /usr/lib/ldap
olcModuleLoad: back_mdb
olcModuleLoad: argon2
dn: olcDatabase={-1}frontend,cn=config
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: {-1}frontend
olcPasswordHash: {ARGON2}
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * none
dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * none
dn: olcDatabase={1}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {1}mdb
olcDbDirectory: ${SLAPD_DATA_DIR}
olcDbMaxSize: 1073741824
olcSuffix: ${SUFFIX}
olcRootDN: cn=admin,${SUFFIX}
olcRootPW: ${ROOT_HASH}
olcLastMod: TRUE
olcDbCheckpoint: 512 30
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break
olcAccess: {1}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {2}to attrs=shadowLastChange by self write by * read
olcAccess: {3}to * by users read by * none
olcDbIndex: objectClass eq
olcDbIndex: uid eq
olcDbIndex: cn,sn,mail eq,sub
olcDbIndex: uidNumber,gidNumber eq
olcDbIndex: member,memberUid eq
EOF
echo "=== 3. Offline import and directory initialization ==="
# Stop slapd to prevent database locking during offline import
sudo systemctl stop slapd 2>/dev/null || true
# Clear directories to guarantee idempotency across multiple runs
sudo rm -rf "${SLAPD_CONFIG_DIR:?}"/* "${SLAPD_DATA_DIR:?}"/*
sudo mkdir -p "${SLAPD_CONFIG_DIR}" "${SLAPD_DATA_DIR}"
# Import LDIF into cn=config (-n0)
sudo slapadd -F "${SLAPD_CONFIG_DIR}" -n0 -l "${LDAP_BOOTSTRAP_FILE}"
# Set proper ownership for the openldap daemon
sudo chown -R openldap:openldap "${SLAPD_CONFIG_DIR}" "${SLAPD_DATA_DIR}"
echo "=== 4. Finalizing package configuration and restarting slapd ==="
sudo dpkg --configure --pending
sudo systemctl restart slapd
echo ""
echo "Success: OpenLDAP server is configured and running."
echo "Base DN: ${SUFFIX}"
echo "Admin DN: cn=admin,${SUFFIX}"
echo "Admin password saved in: ${ADMIN_PASS_FILE}"
SCRIPT_EOF
bash prepare-apply-bootstrap-ldif.sh
|
||||
|
Q194. |
Comment vérifier que le service est bien opérationnel avec ce nouveau contexte de nommage ? |
|||
|
Contrôlez d'abord l'état du service. systemctl status slapd.service --no-pager ● slapd.service - OpenLDAP Server Daemon
Loaded: loaded (/usr/lib/systemd/system/slapd.service; enabled; preset: enabled)
Active: active (running) since Fri 2026-09-04 15:46:45 CEST; 34s ago
Invocation: cab64b3b59194245ae78eb78920b6db6
Docs: man:slapd
man:slapd-config
man:slapd-mdb
Main PID: 3701 (slapd)
Tasks: 3 (limit: 971)
Memory: 6.9M (peak: 6.9M)
CPU: 42ms
CGroup: /system.slice/slapd.service
└─3701 /usr/sbin/slapd -d0 -h "ldap:/// ldapi:///" -u openldap -g openldap
sept. 04 15:46:45 srvr systemd[1]: Starting slapd.service - OpenLDAP Server Daemon...
sept. 04 15:46:45 srvr slapd[3701]: @(#) $OpenLDAP: slapd 2.6.14+dfsg-2 (Aug 25 2026 00:46:58) $
Debian OpenLDAP Maintainers <pkg-openldap-devel@lists.alioth.debian.org>
sept. 04 15:46:45 srvr slapd[3701]: slapd starting
sept. 04 15:46:45 srvr systemd[1]: Started slapd.service - OpenLDAP Server Daemon.
Vérifiez ensuite que le port sudo ss -antlp 'sport = :389'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 2048 0.0.0.0:389 0.0.0.0:* users:(("slapd",pid=3701,fd=7))
LISTEN 0 2048 [::]:389 [::]:* users:(("slapd",pid=3701,fd=8))
Contrôlez enfin, via la connexion locale SASL/ sudo ldapsearch -LLL \ -Y EXTERNAL \ -H ldapi:/// \ -b cn=config \ -s base olcLogLevel SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 dn: cn=config olcLogLevel: stats Le contexte de nommage ldapsearch -x -H ldap:/// \ -b dc=lab,dc=local # extended LDIF # # LDAPv3 # base <dc=lab,dc=local> with scope subtree # filter: (objectclass=*) # requesting: ALL # # search result search: 2 result: 32 No such object # numResponses: 1 C'est normal : la création de cette entrée racine, ainsi que celle des unités organisationnelles, fait l'objet de la sous-partie Section 4.3, « Construire l'arbre et peupler l'annuaire ». |
Les certificats TLS (/etc/ldap/certs/) et le fichier /etc/ldap/ldap.keytab ont déjà été déployés dans la partie Section 3.2, « Définir les objets gérés par l'autorité de certification » et Section 3.4, « Installer et initialiser le service Kerberos ». Il ne reste donc qu'à les activer dans la configuration cn=config du service, en cinq étapes : chiffrement TLS, authentification SASL/GSSAPI, correspondance des identités, configuration du client LDAP local, puis vérifications croisées.
|
Q195. |
Comment activer le chiffrement TLS à partir des certificats déjà déployés ? |
|||||||||
|
Injectez les trois attributs de désignation des certificats ( cat << 'SCRIPT_EOF' >enable-ldap-tls.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Global variables
readonly CERT_DIR="/etc/ldap/certs"
readonly CA_CERT="${CERT_DIR}/labCA.crt"
readonly SERVER_CERT="${CERT_DIR}/ldap-srvr.crt"
readonly SERVER_KEY="${CERT_DIR}/ldap-srvr.key"
readonly SLAPD_DEFAULT="/etc/default/slapd"
echo "=== 1. Checking prerequisite certificates ==="
for file in "${CA_CERT}" "${SERVER_CERT}" "${SERVER_KEY}"; do
if [[ ! -f "${file}" ]]; then
echo "Error: Required certificate or key not found: ${file}" >&2
exit 1
fi
done
echo "=== 2. Preparing and injecting dynamic TLS configuration ==="
TMP_LDIF=$(mktemp)
trap 'rm -f "${TMP_LDIF:-}"' EXIT
cat <<EOF >"${TMP_LDIF}"
dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: ${CA_CERT}
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: ${SERVER_CERT}
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: ${SERVER_KEY}
-
replace: olcLocalSSF
olcLocalSSF: 256
-
replace: olcSecurity
olcSecurity: ssf=128
EOF
# Apply modifications via SASL/EXTERNAL on local socket
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f "${TMP_LDIF}"
echo "=== 3. Enabling ldaps:// endpoint in /etc/default/slapd ==="
# Append ldaps:/// to SLAPD_SERVICES if not already declared
sudo sed -i '/^SLAPD_SERVICES=/{
/ldaps:\/\//! s/"$/ ldaps:\/\/\/"/
}' "${SLAPD_DEFAULT}"
echo "=== 4. Restarting slapd service ==="
# Single restart to take into account olcLocalSSF and new network endpoints
sudo systemctl restart slapd
echo ""
echo "Success: TLS has been enabled for OpenLDAP."
echo "CA certificate: ${CA_CERT}"
echo "Server certificate: ${SERVER_CERT}"
echo "SSF thresholds: local=256, minimum network=128"
echo "Listening sockets:"
sudo ss -tlnp 'sport = :389 or sport = :636'
SCRIPT_EOF
bash enable-ldap-tls.sh
Success: TLS has been enabled for OpenLDAP.
CA certificate: /etc/ldap/certs/labCA.crt
Server certificate: /etc/ldap/certs/ldap-srvr.crt
SSF thresholds: local=256, minimum network=128
Listening sockets:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 2048 0.0.0.0:389 0.0.0.0:* users:(("slapd",pid=4004,fd=7))
LISTEN 0 2048 0.0.0.0:636 0.0.0.0:* users:(("slapd",pid=4004,fd=10))
LISTEN 0 2048 [::]:389 [::]:* users:(("slapd",pid=4004,fd=8))
LISTEN 0 2048 [::]:636 [::]:* users:(("slapd",pid=4004,fd=11))
|
||||||||||
|
Q196. |
Comment activer l'authentification forte SASL/GSSAPI à partir du |
|||||||||
|
Comme pour le client Kerberos, le démon cat << 'SCRIPT_EOF' >enable-ldap-gssapi.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Global variables
readonly DOMAIN="lab.local"
readonly REALM="${DOMAIN^^}"
readonly LDAP_HOST="ldap-srvr.${DOMAIN}"
readonly KEYTAB_FILE="/etc/ldap/ldap.keytab"
readonly SLAPD_DEFAULT="/etc/default/slapd"
echo "=== 1. Checking prerequisite keytab ==="
if [[ ! -f "${KEYTAB_FILE}" ]]; then
echo "Error: Keytab file '${KEYTAB_FILE}' not found." >&2
echo "Ensure the service principal was created and extracted with ktadd." >&2
exit 1
fi
echo "=== 2. Installing Cyrus SASL GSSAPI plugin ==="
sudo apt update -qq
sudo DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends \
libsasl2-modules-gssapi-mit
echo "=== 3. Configuring environment and keytab path ==="
# Update or append KRB5_KTNAME in /etc/default/slapd
if grep -q "KRB5_KTNAME" "${SLAPD_DEFAULT}"; then
sudo sed -i "s|^#\?KRB5_KTNAME=.*|KRB5_KTNAME=${KEYTAB_FILE}|" "${SLAPD_DEFAULT}"
else
echo "KRB5_KTNAME=${KEYTAB_FILE}" | sudo tee -a "${SLAPD_DEFAULT}" >/dev/null
fi
echo "=== 4. Declaring SASL host and realm in cn=config ==="
TMP_LDIF=$(mktemp)
trap 'rm -f "${TMP_LDIF:-}"' EXIT
cat <<EOF >"${TMP_LDIF}"
dn: cn=config
changetype: modify
replace: olcSaslHost
olcSaslHost: ${LDAP_HOST}
-
replace: olcSaslRealm
olcSaslRealm: ${REALM}
EOF
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f "${TMP_LDIF}"
echo "=== 5. Restarting slapd service ==="
# Single restart to load KRB5_KTNAME and initialize the SASL GSSAPI provider
sudo systemctl restart slapd
echo ""
echo "Success: slapd is configured to accept Kerberos (GSSAPI) binds."
echo "Configured realm: ${REALM}"
echo "SASL hostname: ${LDAP_HOST}"
echo "Supported SASL mechanisms:"
# Queried over ldapi:/// to comply with olcSecurity (ssf=128) via olcLocalSSF (256)
sudo ldapsearch -Q -Y EXTERNAL \
-H ldapi:/// \
-b "" \
-s base supportedSASLMechanisms |\
grep -E "^supportedSASLMechanisms:"
SCRIPT_EOF
bash enable-ldap-gssapi.sh |
||||||||||
|
Q197. |
Comment faire correspondre les principaux Kerberos aux entrées du DIT ? |
|||||||||
|
Insérez deux expressions rationnelles
cat << 'SCRIPT_EOF' >configure-identity-mapping.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Global variables
readonly DOMAIN="lab.local"
readonly REALM="${DOMAIN^^}"
# Dynamically extract Base DN (e.g. lab.local -> dc=lab,dc=local)
readonly BASE_DN="dc=${DOMAIN%%.*},dc=${DOMAIN#*.}"
readonly USERS_OU="ou=users,${BASE_DN}"
readonly HOSTS_OU="ou=hosts,${BASE_DN}"
echo "=== 1. Checking slapd daemon availability ==="
if ! systemctl is-active --quiet slapd; then
echo "Error: The slapd service is not running." >&2
echo "Start it with 'sudo systemctl start slapd' before applying configuration." >&2
exit 1
fi
echo "=== 2. Preparing and injecting identity mapping rules (olcAuthzRegexp) ==="
TMP_LDIF=$(mktemp)
trap 'rm -f "${TMP_LDIF:-}"' EXIT
# Translation rules breakdown:
# - replace: ensures idempotency by overwriting existing rules safely
# - cn=[^,]+: matches the realm component regardless of case
# - Rule {0} (Users): [^/,]+ captures the UID while strictly forbidding '/' to prevent overlap with service principals
# - Rule {1} (Hosts): host/([^,]+) extracts the machine FQDN into cn=$1
cat <<EOF >"${TMP_LDIF}"
dn: cn=config
changetype: modify
replace: olcAuthzRegexp
olcAuthzRegexp: {0}uid=([^/,]+),cn=[^,]+,cn=gssapi,cn=auth uid=\$1,${USERS_OU}
olcAuthzRegexp: {1}uid=host/([^,]+),cn=[^,]+,cn=gssapi,cn=auth cn=\$1,${HOSTS_OU}
EOF
# Inject rules into cn=config using local SASL/EXTERNAL authentication
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f "${TMP_LDIF}"
echo "=== 3. Verifying active mapping rules in cn=config ==="
sudo ldapsearch -Q -Y EXTERNAL -H ldapi:/// \
-b "cn=config" \
"(olcAuthzRegexp=*)" olcAuthzRegexp || true
echo ""
echo "Success: Identity mapping (SASL/GSSAPI -> LDAP) is configured."
echo "Expected transformations:"
echo " Kerberos 'user@${REALM}' -> LDAP 'uid=user,${USERS_OU}'"
echo " Kerberos 'host/machine@${REALM}' -> LDAP 'cn=machine,${HOSTS_OU}'"
echo ""
echo "Verification command (after 'kinit <user>'):"
echo " ldapwhoami -H ldap://ldap-srvr.${DOMAIN} -Y GSSAPI"
SCRIPT_EOF
bash configure-identity-mapping.sh |
||||||||||
|
Q198. |
Depuis le client, comment configurer les outils LDAP (ldapsearch, ldapmodify, ldapwhoami) pour qu'ils fassent confiance à l'autorité de certification locale et se connectent par défaut au serveur ? Recherchez dans la page de manuel |
|||||||||
|
Sans ce fichier, les outils clients ne connaissent pas le chemin de l'autorité de certification locale et considèrent le certificat du serveur comme non fiable, même si celui-ci a déjà été correctement installé côté serveur. Lancez cette commande depuis le client. ldapsearch -H ldaps://ldap-srvr.lab.local \
-D "cn=admin,dc=lab,dc=local" -y ~/.ldap_admin_pass \
-b dc=lab,dc=local
ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1) additional info: error:0A000086:SSL routines::certificate verify failed (self-signed certificate in certificate chain) Créez, toujours sur le client, le fichier sudo mkdir -p /etc/ldap cat << EOF | sudo tee /etc/ldap/ldap.conf >/dev/null URI ldaps://ldap-srvr.lab.local BASE dc=lab,dc=local TLS_CACERT /etc/ssl/certs/labCA.crt TLS_REQCERT demand SASL_NOCANON on EOF
Copiez le mot de passe de l'administrateur LDAP depuis le serveur sur le client pour valider l'authentification. scp .ldap_admin_pass clnt:~ Relancez la même requête : la connexion TLS aboutit désormais, l'entrée racine restant introuvable puisque l'arbre n'a pas encore été construit. ldapsearch -H ldaps://ldap-srvr.lab.local \
-D "cn=admin,dc=lab,dc=local" -y ~/.ldap_admin_pass \
-b dc=lab,dc=local
# extended LDIF # # LDAPv3 # base <dc=lab,dc=local> with scope subtree # filter: (objectclass=*) # requesting: ALL # # search result search: 2 result: 32 No such object # numResponses: 1
|
||||||||||
|
Q199. |
Comment vérifier que le service répond bien en TLS ? |
|||||||||
|
Toutes les commandes de cette question s'exécutent depuis le client. Vérifiez d'abord que le seuil SSF rejette bien un accès anonyme en clair. ldapsearch -LLL -x -H ldap://ldap-srvr.lab.local \ -b dc=lab,dc=local ldap_bind: Confidentiality required (13) additional info: confidentiality required Vérifiez ensuite qu'un bind administrateur authentifié via ldapsearch \
-D "cn=admin,dc=lab,dc=local" -y ~/.ldap_admin_pass
# extended LDIF # # LDAPv3 # base <dc=lab,dc=local> with scope subtree # filter: (objectclass=*) # requesting: ALL # # search result search: 2 result: 32 No such object |
La sous-partie Section 4.1, « Installer et initialiser le service d'annuaire » a montré que le contexte de nommage dc=lab,dc=local est déclaré dans la configuration cn=config, mais que l'entrée racine du DIT n'existe pas encore. Cette sous-partie construit l'arborescence retenue pour cette maquette : les trois unités organisationnelles ou=users, ou=groups et ou=hosts, puis peuple l'annuaire avec les comptes de la famille Skywalker et une entrée représentant l'hôte client.
|
Q200. |
Comment créer l'entrée racine du DIT et les trois unités d'organisation retenues pour cette maquette ? |
|||
|
Les opérations de cette question s'exécutent sur le serveur, via la connexion locale SASL/ Créez le script cat << 'SCRIPT_EOF' >build-ldap-tree.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Global variables
readonly DOMAIN="lab.local"
readonly BASE_DN="dc=${DOMAIN%%.*},dc=${DOMAIN#*.}"
readonly TREE_LDIF="${HOME}/build-tree.ldif"
echo "=== 1. Generating the DIT root entry and organizational units ==="
cat <<EOF >"${TREE_LDIF}"
dn: ${BASE_DN}
objectClass: top
objectClass: dcObject
objectClass: organization
o: Lab SysAdmNet
dc: ${DOMAIN%%.*}
dn: ou=users,${BASE_DN}
objectClass: organizationalUnit
ou: users
dn: ou=groups,${BASE_DN}
objectClass: organizationalUnit
ou: groups
dn: ou=hosts,${BASE_DN}
objectClass: organizationalUnit
ou: hosts
EOF
echo "=== 2. Importing the tree with local SASL/EXTERNAL authentication ==="
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f "${TREE_LDIF}"
echo ""
echo "Success: root entry and organizational units users/groups/hosts created."
SCRIPT_EOF
bash build-ldap-tree.sh
Vérifiez la structure obtenue. sudo ldapsearch -LLL -Y EXTERNAL -H ldapi:/// \
-b dc=lab,dc=local \
-s children dn
SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 dn: ou=users,dc=lab,dc=local dn: ou=groups,dc=lab,dc=local dn: ou=hosts,dc=lab,dc=local
|
||||
|
Q201. |
Comment générer les comptes de la famille Skywalker avec un mot de passe aléatoire condensé en |
|||
|
Chaque compte utilisateur reçoit un groupe primaire dédié (private user group). Un groupe secondaire partagé Créez le script cat << 'SCRIPT_EOF' >generate-skywalker-accounts.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Global variables
readonly DOMAIN="lab.local"
readonly BASE_DN="dc=${DOMAIN%%.*},dc=${DOMAIN#*.}"
readonly USERS_OU="ou=users,${BASE_DN}"
readonly GROUPS_OU="ou=groups,${BASE_DN}"
readonly IMPORT_LDIF="${HOME}/import-skywalkers.ldif"
readonly START_UID=10000
readonly SHARED_GID=100
# uid:cn:sn:givenName
readonly ACCOUNTS=(
"padme:Padme Amidala:Amidala:Padme"
"anakin:Anakin Skywalker:Skywalker:Anakin"
"leia:Leia Organa:Organa:Leia"
"luke:Luke Skywalker:Skywalker:Luke"
)
echo "=== 1. Preparing passwords and password hashes ==="
: >"${IMPORT_LDIF}"
UID_NUMBER=${START_UID}
for ACCOUNT in "${ACCOUNTS[@]}"; do
IFS=':' read -r UID_ CN SN GIVEN_NAME <<<"${ACCOUNT}"
PASS_FILE="${HOME}/.ldap_${UID_}_pass"
# Reuse existing password if present to maintain sync with Kerberos KDC
if [[ -f "${PASS_FILE}" ]]; then
USER_PASS=$(head -n 1 "${PASS_FILE}")
else
# 18 random bytes yield exactly 24 base64 characters without padding
USER_PASS=$(openssl rand -base64 18)
( umask 077 && printf '%s' "${USER_PASS}" >"${PASS_FILE}" )
fi
USER_HASH="$(sudo slappasswd -o module-load=argon2.la -h "{ARGON2}" -s "${USER_PASS}")"
echo "=== 2. Appending entries for ${UID_} (uidNumber ${UID_NUMBER}) ==="
cat <<EOF >>"${IMPORT_LDIF}"
dn: cn=${UID_},${GROUPS_OU}
objectClass: posixGroup
cn: ${UID_}
gidNumber: ${UID_NUMBER}
dn: uid=${UID_},${USERS_OU}
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: ${UID_}
cn: ${CN}
sn: ${SN}
givenName: ${GIVEN_NAME}
mail: ${UID_}@${DOMAIN}
uidNumber: ${UID_NUMBER}
gidNumber: ${UID_NUMBER}
homeDirectory: /ahome/${UID_}
loginShell: /bin/bash
userPassword: ${USER_HASH}
shadowLastChange: $(( $(date +%s) / 86400 ))
EOF
UID_NUMBER=$((UID_NUMBER + 1))
done
echo "=== 3. Appending the shared secondary group ==="
cat <<EOF >>"${IMPORT_LDIF}"
dn: cn=users,${GROUPS_OU}
objectClass: posixGroup
cn: users
gidNumber: ${SHARED_GID}
memberUid: padme
memberUid: anakin
memberUid: leia
memberUid: luke
EOF
echo "=== 4. Removing existing entries if present (idempotency) ==="
# Delete leaf entries first (users and private groups), then shared group
for ACCOUNT in "${ACCOUNTS[@]}"; do
IFS=':' read -r UID_ _ <<<"${ACCOUNT}"
sudo ldapdelete -Y EXTERNAL -H ldapi:/// "uid=${UID_},${USERS_OU}" 2>/dev/null || true
sudo ldapdelete -Y EXTERNAL -H ldapi:/// "cn=${UID_},${GROUPS_OU}" 2>/dev/null || true
done
sudo ldapdelete -Y EXTERNAL -H ldapi:/// "cn=users,${GROUPS_OU}" 2>/dev/null || true
echo "=== 5. Importing accounts with local SASL/EXTERNAL authentication ==="
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f "${IMPORT_LDIF}"
echo ""
echo "Success: 4 Skywalker accounts imported (uidNumber ${START_UID}-$((UID_NUMBER - 1)))."
echo "Individual passwords saved in \${HOME}/.ldap_<uid>_pass (mode 400)."
SCRIPT_EOF
bash generate-skywalker-accounts.sh
Vérifiez le résultat. sudo ldapsearch -LLL -Y EXTERNAL -H ldapi:/// \
-b ou=users,dc=lab,dc=local \
"(objectClass=posixAccount)" uid uidNumber homeDirectory
dn: uid=padme,ou=users,dc=lab,dc=local uid: padme uidNumber: 10000 homeDirectory: /ahome/padme dn: uid=anakin,ou=users,dc=lab,dc=local uid: anakin uidNumber: 10001 homeDirectory: /ahome/anakin dn: uid=leia,ou=users,dc=lab,dc=local uid: leia uidNumber: 10002 homeDirectory: /ahome/leia dn: uid=luke,ou=users,dc=lab,dc=local uid: luke uidNumber: 10003 homeDirectory: /ahome/luke |
||||
|
Q202. |
Comment déclarer l'hôte client dans l'unité |
|||
|
Les expressions cat << 'SCRIPT_EOF' >register-lab-host.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Global variables
readonly DOMAIN="lab.local"
# Dynamically compute Base DN (e.g. lab.local -> dc=lab,dc=local)
readonly BASE_DN="dc=${DOMAIN%%.*},dc=${DOMAIN#*.}"
readonly HOSTS_OU="ou=hosts,${BASE_DN}"
readonly CLIENT_HOSTNAME="clnt.${DOMAIN}"
readonly HOST_DN="cn=${CLIENT_HOSTNAME},${HOSTS_OU}"
echo "=== 1. Checking slapd service and parent DIT structure ==="
if ! systemctl is-active --quiet slapd; then
echo "Error: The slapd service is not running." >&2
echo "Start it with 'sudo systemctl start slapd' before registering hosts." >&2
exit 1
fi
# Verify that the parent organizational unit (ou=hosts) exists
if ! sudo ldapsearch -Q -Y EXTERNAL -H ldapi:/// -b "${BASE_DN}" -s one "(ou=hosts)" dn | grep -q "^dn:"; then
echo "Error: Parent container '${HOSTS_OU}' not found." >&2
echo "Ensure the base directory tree (add_tree.ldif) has been populated." >&2
exit 1
fi
echo "=== 2. Generating host entry LDIF ==="
TMP_LDIF=$(mktemp)
trap 'rm -f "${TMP_LDIF:-}"' EXIT
cat <<EOF >"${TMP_LDIF}"
dn: ${HOST_DN}
objectClass: top
objectClass: device
cn: ${CLIENT_HOSTNAME}
description: Client workstation for autofs/LDAP/NFSv4 lab setup
EOF
echo "=== 3. Removing existing entry if present (idempotency) ==="
sudo ldapdelete -Y EXTERNAL -H ldapi:/// "${HOST_DN}" 2>/dev/null || true
echo "=== 4. Importing the host entry with local SASL/EXTERNAL authentication ==="
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f "${TMP_LDIF}"
echo "=== 5. Verifying host registration in the directory ==="
sudo ldapsearch -Q -Y EXTERNAL -H ldapi:/// \
-b "${HOSTS_OU}" \
-s base \
-b "${HOST_DN}" \
cn description
echo ""
echo "Success: Host entry '${HOST_DN}' registered."
echo "Kerberos identity mapping link:"
echo " Kerberos 'host/${CLIENT_HOSTNAME}@${DOMAIN^^}' -> LDAP '${HOST_DN}'"
SCRIPT_EOF
bash register-lab-host.sh
|
||||
|
Q203. |
Comment vérifier le cloisonnement des données imposé par les listes de contrôle d'accès (ACL) déjà configurées dans la Section 4.1, « Installer et initialiser le service d'annuaire » ? |
|||
|
Les règles Recherchez l'attribut ldapsearch -LLL -x -H ldapi:/// \
-D "uid=luke,ou=users,dc=lab,dc=local" \
-y ~/.ldap_luke_pass \
-b dc=lab,dc=local \
uid
dn: dc=lab,dc=local dn: ou=users,dc=lab,dc=local dn: ou=groups,dc=lab,dc=local dn: ou=hosts,dc=lab,dc=local dn: cn=padme,ou=groups,dc=lab,dc=local dn: uid=padme,ou=users,dc=lab,dc=local uid: padme dn: cn=anakin,ou=groups,dc=lab,dc=local dn: uid=anakin,ou=users,dc=lab,dc=local uid: anakin dn: cn=leia,ou=groups,dc=lab,dc=local dn: uid=leia,ou=users,dc=lab,dc=local uid: leia dn: cn=luke,ou=groups,dc=lab,dc=local dn: uid=luke,ou=users,dc=lab,dc=local uid: luke dn: cn=users,ou=groups,dc=lab,dc=local dn: cn=clnt.lab.local,ou=hosts,dc=lab,dc=local L'utilisateur ldapsearch -LLL -x -H ldapi:/// \
-D "uid=luke,ou=users,dc=lab,dc=local" \
-y ~/.ldap_luke_pass \
-b dc=lab,dc=local \
userPassword
dn: dc=lab,dc=local dn: ou=users,dc=lab,dc=local dn: ou=groups,dc=lab,dc=local dn: ou=hosts,dc=lab,dc=local dn: cn=padme,ou=groups,dc=lab,dc=local dn: uid=padme,ou=users,dc=lab,dc=local dn: cn=anakin,ou=groups,dc=lab,dc=local dn: uid=anakin,ou=users,dc=lab,dc=local dn: cn=leia,ou=groups,dc=lab,dc=local dn: uid=leia,ou=users,dc=lab,dc=local dn: cn=luke,ou=groups,dc=lab,dc=local dn: uid=luke,ou=users,dc=lab,dc=local userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTcxNjgsdD01LHA9MSR2ZkxXckNiNlV Bb3BCSFhVSG5Ga2ZBJE1DbVJhRE5XbWNtcG5UaDlLSDEvRWp5QXVnbUtodDU2MHU1NXlLWW00V3c= dn: cn=users,ou=groups,dc=lab,dc=local dn: cn=clnt.lab.local,ou=hosts,dc=lab,dc=local L'utilisateur |
Le paquet nslcd et ses dépendances ont déjà été installés sans configuration sur le client dans la partie Section 3.3, « Installer les paquets TLS et générer les certificats », uniquement pour disposer du compte système nslcd nécessaire au dépôt de la clé privée LDAP. Le fichier /etc/nslcd.conf qui en résulte ne porte donc que des valeurs par défaut, sans aucune information de connexion à l'annuaire. L'unité ou=users existant désormais (Section 4.3, « Construire l'arbre et peupler l'annuaire »), cette sous-partie effectue la première configuration réelle de nslcd, directement avec un compte de service dédié, nslcd-proxy, dont les droits restent ceux d'un compte authentifié ordinaire au regard des listes de contrôle d'accès déjà en place (Section 4.1, « Installer et initialiser le service d'annuaire »). Elle complète également, à présent que l'entrée hôte existe dans ou=hosts, la vérification du bind SASL/GSSAPI laissée en suspens dans la Section 4.2, « Sécuriser les échanges avec TLS et l'authentification SASL/GSSAPI ».
|
Q204. |
Comment créer le compte de service |
|||
|
Ce compte n'a besoin d'aucun attribut POSIX (pas de connexion interactive, pas de répertoire personnel) : la classe d'objet cat << 'SCRIPT_EOF' >create-nslcd-proxy.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Global variables
readonly DOMAIN="lab.local"
readonly BASE_DN="dc=${DOMAIN%%.*},dc=${DOMAIN#*.}"
readonly USERS_OU="ou=users,${BASE_DN}"
readonly PROXY_UID="nslcd-proxy"
readonly PROXY_DN="uid=${PROXY_UID},${USERS_OU}"
readonly PASS_FILE="${HOME}/.ldap_${PROXY_UID}_pass"
TMP_LDIF=$(mktemp)
trap 'rm -f "${TMP_LDIF:-}"' EXIT
echo "=== 1. Preparing the password and its Argon2 hash ==="
# Reuse existing password if present to maintain sync with the client configuration
if [[ -f "${PASS_FILE}" ]]; then
PROXY_PASS=$(head -n 1 "${PASS_FILE}")
else
# 18 random bytes yield exactly 24 base64 characters without padding
PROXY_PASS=$(openssl rand -base64 18)
( umask 077 && printf '%s' "${PROXY_PASS}" >"${PASS_FILE}" )
fi
PROXY_HASH=$(sudo slappasswd -o module-load=argon2.la -h "{ARGON2}" -s "${PROXY_PASS}")
echo "=== 2. Generating the LDIF entry ==="
cat <<EOF >"${TMP_LDIF}"
dn: ${PROXY_DN}
objectClass: inetOrgPerson
uid: ${PROXY_UID}
cn: NSLCD Proxy Service
sn: Service
description: Read-only service account for NSS/PAM resolution
userPassword: ${PROXY_HASH}
EOF
echo "=== 3. Removing the existing entry if present (idempotency) ==="
sudo ldapdelete -Y EXTERNAL -H ldapi:/// "${PROXY_DN}" 2>/dev/null || true
echo "=== 4. Importing the service account with local SASL/EXTERNAL authentication ==="
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f "${TMP_LDIF}"
echo ""
echo "Success: service account '${PROXY_UID}' created."
echo "Bind DN: ${PROXY_DN}"
echo "Password saved in: ${PASS_FILE} (mode 400)."
SCRIPT_EOF
bash create-nslcd-proxy.sh
=== 1. Preparing the password and its Argon2 hash === === 2. Generating the LDIF entry === === 3. Removing the existing entry if present (idempotency) === === 4. Importing the service account with local SASL/EXTERNAL authentication === SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 adding new entry "uid=nslcd-proxy,ou=users,dc=lab,dc=local" Success: service account 'nslcd-proxy' created. Bind DN: uid=nslcd-proxy,ou=users,dc=lab,dc=local Password saved in: /home/etu/.ldap_nslcd-proxy_pass (mode 400). |
||||
|
Q205. |
Comment transférer ce mot de passe sur le client, puis appliquer la première configuration réelle de |
|||
|
Transférez d'abord le fichier de mot de passe, comme cela a déjà été fait pour scp ~/.ldap_nslcd-proxy_pass clnt:~ Le script de reconfiguration s'exécute intégralement avec sudo bash ... : à l'intérieur du script, sudo mv ~/.ldap_nslcd-proxy_pass /root/ sudo chmod 400 /root/.ldap_nslcd-proxy_pass Reprenez la démarche de la question Comment installer le paquet cat << 'SCRIPT_EOF' >configure-nslcd-proxy.sh
#!/usr/bin/env bash
set -euo pipefail
echo "=== 1. Installing debconf-utils ==="
sudo apt update -qq
sudo apt install -y --no-install-recommends debconf-utils
readonly PROXY_DN="uid=nslcd-proxy,ou=users,dc=lab,dc=local"
readonly PROXY_PASS=$(head -n 1 /root/.ldap_nslcd-proxy_pass)
echo "=== 2. Purging the default /etc/nslcd.conf left by the unconfigured install ==="
# The nslcd package was installed without configuration (generate.certs step):
# nslcd.config seeds debconf FROM this existing file when present, which would
# override the values set below. Removing it forces a clean re-seed.
sudo rm -f /etc/nslcd.conf
sudo debconf-set-selections <<EOF
nslcd nslcd/ldap-uris string ldaps://ldap-srvr.lab.local
nslcd nslcd/ldap-base string dc=lab,dc=local
nslcd nslcd/ldap-auth-type select simple
nslcd nslcd/ldap-starttls boolean false
nslcd nslcd/ldap-reqcert select demand
nslcd nslcd/ldap-cacertfile string /etc/ssl/certs/labCA.crt
nslcd nslcd/ldap-binddn string ${PROXY_DN}
nslcd nslcd/ldap-bindpw password ${PROXY_PASS}
libnss-ldapd libnss-ldapd/nsswitch multiselect passwd, group, shadow
EOF
echo "=== 3. Reconfiguring packages non-interactively ==="
sudo dpkg-reconfigure -f noninteractive nslcd libnss-ldapd
echo "=== 4. Restarting nslcd service ==="
sudo systemctl restart nslcd
echo ""
echo "Success: nslcd configured and restarted."
SCRIPT_EOF
sudo bash configure-nslcd-proxy.sh
Affichez la liste des paramètres effectivement appliqués, comme dans le support Introduction aux annuaires LDAP avec OpenLDAP, avec la commande debconf-get-selections. sudo debconf-get-selections | grep ^nslcd nslcd nslcd/ldap-auth-type select simple nslcd nslcd/ldap-base string dc=lab,dc=local nslcd nslcd/ldap-binddn string uid=nslcd-proxy,ou=users,dc=lab,dc=local nslcd nslcd/ldap-bindpw password nslcd nslcd/ldap-cacertfile string /etc/ssl/certs/labCA.crt nslcd nslcd/ldap-reqcert select demand nslcd nslcd/ldap-sasl-authcid string nslcd nslcd/ldap-sasl-authzid string nslcd nslcd/ldap-sasl-krb5-ccname string nslcd nslcd/ldap-sasl-mech select nslcd nslcd/ldap-sasl-realm string nslcd nslcd/ldap-sasl-secprops string nslcd nslcd/ldap-starttls boolean false nslcd nslcd/ldap-uris string ldaps://ldap-srvr.lab.local Vérifiez enfin que la résolution des comptes reste transparente pour le système. id luke uid=10003(luke) gid=10003(luke) groupes=10003(luke),100(users)
|
||||
|
Q206. |
Maintenant que l'entrée |
|||
|
Le principal machine sudo kinit -k -t /etc/krb5.keytab host/clnt.lab.local && \ sudo klist Ticket cache: FILE:/tmp/krb5cc_0
Default principal: host/clnt.lab.local@LAB.LOCAL
Valid starting Expires Service principal
02/09/2026 15:39:17 03/09/2026 01:39:17 krbtgt/LAB.LOCAL@LAB.LOCAL
renew until 03/09/2026 15:39:17
Utilisez ce ticket pour effectuer un bind SASL/GSSAPI vers le serveur, avec la commande déjà annoncée à la fin de la Section 4.2, « Sécuriser les échanges avec TLS et l'authentification SASL/GSSAPI ». sudo ldapwhoami -H ldap://ldap-srvr.lab.local -Y GSSAPI SASL/GSSAPI authentication started SASL username: host/clnt.lab.local@LAB.LOCAL SASL SSF: 256 SASL data security layer installed. dn:cn=clnt.lab.local,ou=hosts,dc=lab,dc=local La règle
|

![[Note]](/images/note.png)
![[Attention]](/images/caution.png)