Fournisseur LDAP
Introduction
LDAP Provider permet aux applications qui emploient le protocole LDAP pour l’authentification des utilisateurs de bénéficier des systèmes d’authentification modernes (par exemple, OIDC) sans avoir à réécrire l’application.
Un modèle de déploiement connu sous le nom de « façade » est généralement utilisé pour mettre à jour et moderniser les applications basées sur le protocole LDAP. Lorsqu’une application emploie ce motif, elle a recours à un orchestrateur agissant en tant proxy IAP (Identity-Aware Proxy) et à un orchestrateur agissant en tant que serveur LDAP (LDAP Provider), l’application est en quelque sorte prise en sandwich entre les deux orchestrateurs.
Au cours de la mise en œuvre, la configuration LDAP de l’application est mise à jour pour pointer vers l’orchestrateur, agissant en tant que serveur LDAP à la place du serveur LDAP actuellement configuré (par exemple, Active Directory). Cette modification de la configuration est la seule mise à jour requise sur le serveur de l’application, aucune modification du code de l’application n’est nécessaire.
Grâce à ce motif, les requêtes des utilisateurs finaux transitent par l’orchestrateur configuré en tant que proxy d’application, de sorte qu’elles peuvent être authentifiées à l’aide de vos fournisseurs d’identité favoris, prendre en charge la résilience et être autorisées sur la base de la politique configurée.
Une fois l’utilisateur authentifié, l’orchestrateur utilise les extensions de services configurées pour se connecter à l’application en arrière-plan, si bien que l’utilisateur final ne voit pas la page de connexion de l’application native. L’application utilise généralement les informations d’identification reçues et les authentifie par le biais d’une requête de liaison LDAP. Lorsque l’orchestrateur reçoit cette requête de liaison, il authentifie l’utilisateur en fonction du type de requête de liaison (par exemple, Simple, SASL) reçu et de la manière dont l’orchestrateur a été configuré.
Par exemple, l’orchestrateur agissant en tant que proxy IAP est en mesure de :
- Créer un utilisateur unique.
- Communiquer cet utilisateur unique à l’application protégée.
- L’application enverra ensuite une requête de liaison en utilisant ces informations d’identification.
- L’orchestrateur agissant en tant que serveur LDAP authentifie la requête à l’aide des données stockées au sujet de l’utilisateur unique (par exemple, le mot de passe unique haché).
Une fois authentifiée, l’application envoie généralement une requête de recherche au fournisseur LDAP pour obtenir les attributs de l’utilisateur authentifié (par exemple, l’identifiant de l’employé, l’adresse électronique, le nom). Dès réception de la réponse, l’application achève d’exécuter toutes les actions nécessaires à l’affichage de la page demandée à l’utilisateur.
Ce modèle de déploiement prend en charge de nombreuses applications nécessitant le protocole LDAP et peut facilement être adapté à votre environnement. Par ailleurs, bien que nous soulignons que cette solution peut être déployée sur deux orchestrateurs distincts, l’un agissant en tant que proxy IAP et l’autre en tant que serveur LDAP, il peut également s’agir du même orchestrateur ou d’ensembles d’orchestrateurs afin de prendre en charge les déploiements à haute disponibilité (HA, Highly Available).
Fonctionnalités prises en charge
Opérations LDAP
Liaison
L’opération de liaison est utilisée pour authentifier la connexion LDAP. Les méthodes d’authentification suivantes sont actuellement prises en charge :
Consultez le RFC pour plus d’informations.
Simple
L’authentification simple consiste à envoyer au serveur LDAP le nom de domaine complet du client et son mot de passe en texte clair (RFC 2251 et RFC 2829). Étant donné que le mot de passe n’est pas protégé et qu’il peut être facilement lu sur le réseau, il est recommandé d’utiliser le protocole TLS pour la connexion.
Consultez le RFC pour plus d’informations.
SASL
La couche SASL (Simple Authentication and Security Layer) permet au protocole LDAP de prendre en charge l’authentification enfichable. Cela permet aux clients et aux serveurs LDAP de négocier des mécanismes d’authentification personnalisés, standards ou non, pour l’authentification. Par ailleurs, les mécanismes SASL peuvent prendre en charge la négociation du chiffrement et de la vérification de l’intégrité de la couche SASL. Lorsqu’elle est négociée, cette couche peut contribuer à réduire le risque de connexion compromise.
Bien que le fournisseur LDAP permette d’effectuer des liaisons SASL sur une connexion protégée par protocole TLS, il n’autorise pas l’utilisation de mécanismes de cryptage et de vérification de l’intégrité de la couche SASL sur une telle connexion.
Une fois qu’un mécanisme de cryptage/de vérification de l’intégrité de la couche SASL est utilisé pour une connexion, le client NE DOIT PAS envoyer de requête de liaison supplémentaire. Si une requête de liaison supplémentaire est reçue après l’utilisation de la couche SASL, une réponse de « refus d’exécution (53) » sera renvoyée.
Consultez le RFC pour plus d’informations.
Les mécanismes SASL suivants sont actuellement pris en charge :
GSS-SPNEGO
GSS-SPNEGO est un mécanisme de pseudo-sécurité qui permet aux pairs GSS-API de déterminer en bande si leurs informations d’identification prennent en charge un ensemble commun d’un ou de plusieurs mécanismes GSS-API. Ce mécanisme s’avère utile pour les applications qui partagent plusieurs mécanismes avec le fournisseur LDAP. Pour le moment, seul le protocole NTLM est pris en charge.
Consultez le RFC pour plus d’informations.
NTLM
NTLM désigne le protocole d’authentification NT LAN Manager, généralement utilisé dans les environnements Windows pour l’authentification entre les clients et les serveurs, et qui peut éventuellement assurer la sécurité de la session lorsque le client l’exige. Avec le protocole LDAP, la sécurité de la session est assurée par un mécanisme de cryptage et de vérification de l’intégrité de la couche SASL.
Versions NTLM prises en charge :
- v2
Veuillez vous référer aux spécifications pour plus d’informations
Unbind
Unbind permet de signaler la fermeture de la connexion à la session.
Consultez le RFC pour plus d’informations.
Search
La fonction Search est utilisée pour demander à un serveur de renvoyer un ensemble d’entrées correspondant à un critère de recherche. Si la requête porte sur le Root DSE (un nom de domaine vide), le fournisseur LDAP renverra les entrées basées sur les attributs demandés. Les attributs sont pris en charge immédiatement et leurs valeurs seront basées sur la configuration de l’orchestrateur :
supportedLDAPVersion
: la valeur sera toujours3
(seul LDAP v3 est pris en charge).supportedSASLMechanisms
: les valeurs seront basées sur lesquelles les mécanismes SASL activés.
Les requêtes RootDSE peuvent être effectuées dans le cadre de connexions non authentifiées, car les clients utilisent généralement la réponse pour déterminer les méthodes d’authentification qui sont mutuellement prises en charge.
Si le client exige des attributs spécifiques du Root DSE dans la réponse, des valeurs supplémentaires
peuvent être définies via la configuration rootDSE.attributes
. Toutes les valeurs définies ici
seront incluses dans les requêtes de recherche Root DSE.
Pour les autres requêtes de recherche, la connexion DOIT être authentifiée. Dès réception, le
traitement est reporté vers l’extension de services Search
pour consolider et filtrer
les sources de données et renvoyer les résultats au client.
Veuillez vous référer aux spécifications pour plus d’informations
Extension - StartTLS
StartTLS permet de mettre à niveau la connexion TCP vers TLS à l’aide de la configuration tls
fournie. Les clients LDAP
peuvent utiliser StartTLS pour sécuriser leur connexion si le fournisseur LDAP a été
configuré avec ldap://
au lieu de ldaps://
.
Veuillez vous référer aux spécifications pour plus d’informations
Configuration
enabled
(obligatoire) : indique si le fournisseur LDAP est activé ou non. Cette valeur DOIT être définie sur
true
pour que le fournisseur LDAP puisse démarrer.
uri
(obligatoire) : l’adresse utilisée par le fournisseur LDAP. Elle DOIT commencer
par ldap://
ou ldaps://
. Si vous utilisez ldaps
, le protocole tls
doit
être défini et utilisé dans la configuration. Les ports par défaut seront utilisés s’ils ne sont pas spécifiés dans l’uri
: ldap://
avec la valeur par défaut 389
ou ldaps://
avec la valeur par défaut 636
. Exemples ci-dessous :
ldaps ://:636
: le fournisseur LDAP écoutera sur toutes les interfaces du port 636 en utilisant le protocole TLS.ldap://127.0.0.1:389
: le fournisseur LDAP écoutera l’interface Loopback sur le port 389 et n’utilisera pas le protocole TLS.ldap ://0.0.0.0
:le fournisseur LDAP écoutera toutes les interfaces sur le port 389 par défaut et n’utilisera pas le protocole TLS.
readDeadline
(facultatif, la valeur par défaut est de 5m
) : durée maximale pendant laquelle
les connexions établies seront maintenues sans recevoir de données. Si ce temps est
écoulé, la connexion sera interrompue.
writeDeadline
(facultatif, la valeur par défaut est de 5m
) : durée maximale pendant laquelle
les connexions établies seront maintenues sans envoyer de données. Si ce temps est
écoulé, la connexion sera interrompue.
tls
(facultatif) : le nom de la configuration TLS spécifiée à la racine de la
configuration de l’orchestrateur.
Root DSE
rootDSE.attributes
(facultatif) : les attributs personnalisés qui doivent être renvoyés
par le fournisseur LDAP lorsqu’une recherche Root DSE est reçue.
Search
search.searchSE
(facultatif) : l’extension de services lancée lors de la réception d’une recherche
non-Root DSE. Bien qu’elle soit facultative, cette
extension de service est nécessaire pour la plupart des applications.
Authentification
authentication.allowAnonymous
(facultatif, la valeur par défaut est « false »
) : indique si le
fournisseur LDAP autorise l’authentification anonyme lors de la réception d’une recherche LDAP.
Authentification simple
authentication.methods.simple.enabled
(facultatif, la valeur par défaut est « false »
) : indique si
le fournisseur LDAP autorise les requêtes de liaison utilisant le choix d’authentification simple.
authentication.methods.simple.authenticateSE
(facultatif, requis lorsque l’authentification simple est activée) : l’extension de services requise qui est lancée lorsqu’une requête de liaison par
authentification simple est reçue.
SASL
authentication.methods.sasl.enabled
(facultatif, la valeur par défaut est « false »
) : indique si
le fournisseur LDAP prend en charge les requêtes de liaison utilisant un mécanisme SASL. Si cette configuration est désactivée, tous les
mécanismes SASL le seront également, qu’ils aient été activés ou non.
GSS-SPNEGO
authentication.methods.sasl.mechanisms.gssspnego.enabled
(facultatif, la valeur par défaut est « false »
) : indique si le fournisseur LDAP prend en charge le
mécanisme GSS-SPNEGO. Actuellement, lorsque cette configuration est activée, le protocole NTLM doit l’être également. Si
cette configuration est désactivée, le protocole NTLM le sera également.
authentication.methods.sasl.mechanisms.gssspnego.ntlm.enabled
(facultatif, la valeur par défaut est « false »
) :
indique si le fournisseur LDAP prend en charge la négociation NTLM dans le cadre du mécanisme GSS-SPNEGO.
authentication.methods.sasl.mechanisms.gssspnego.ntlm.disableRequiring128bitEncryption
(facultatif, la valeur par défaut est « false »
) :
indique si le fournisseur LDAP désactive le cryptage 128 bits requis pour le protocole NTLM.
authentication.methods.sasl.mechanisms.gssspnego.ntlm.netbiosDomainName
(facultatif) : définit le nom de domaine NetBIOS utilisé par le protocole NTLM.
authentication.methods.sasl.mechanisms.gssspnego.ntlm.netbiosMachineName
(facultatif) :
définit le nom de la machine NetBIOS utilisée par le protocole NTLM. Il s’agit généralement du nom du serveur
sur lequel le fournisseur LDAP est hébergé.
authentication.methods.sasl.mechanisms.gssspnego.ntlm.dnsDomainName
(facultatif) :
définit le nom de domaine complet du domaine du serveur.
authentication.methods.sasl.mechanisms.gssspnego.ntlm.dnsForestName
(facultatif) :
définit le nom de domaine complet de la forêt du serveur. Le champ DnsForestName est vide sur les machines qui
ne sont pas reliées à un domaine.
authentication.methods.sasl.mechanisms.gssspnego.ntlm.dnsMachineName
(facultatif) :
définit le nom de domaine complet du serveur.
authentication.methods.sasl.mechanisms.gssspnego.ntlm.getHashedCredentialsSE
(obligatoire) :
L’extension de services lancée lors de la vérification du
message d’authentification NTLM. Il est chargé de récupérer les hachages NT et LM de
l’utilisateur fourni.
Exemples de configuration
Fournisseur LDAP avec TLS
tls:
ldapTLS:
certFile: /etc/maverics/certs/ldap.crt
keyFile: /etc/maverics/certs/ldap.key
maverics:
certFile: /etc/maverics/certs/maverics.crt
keyFile: /etc/maverics/certs/maverics.key
http:
address: :443
tls: maverics
ldapProvider:
enabled: true
uri: ldaps://ldap.example.com:636
tls: ldapTLS
Ajout de la prise en charge de l’authentification simple
tls:
ldapTLS:
certFile: /etc/maverics/certs/ldap.crt
keyFile: /etc/maverics/certs/ldap.key
maverics:
certFile: /etc/maverics/certs/maverics.crt
keyFile: /etc/maverics/certs/maverics.key
http:
address: :443
tls: maverics
ldapProvider:
enabled: true
uri: ldaps://ldap.example.com:636
tls: ldapTLS
authentication:
methods:
simple:
enabled: true
authenticateSE:
funcName: Authenticate
file: /etc/maverics/extensions/simple.go
Ajout de la prise en charge des requêtes de recherche non-Root DSE
tls:
ldapTLS:
certFile: /etc/maverics/certs/ldap.crt
keyFile: /etc/maverics/certs/ldap.key
maverics:
certFile: /etc/maverics/certs/maverics.crt
keyFile: /etc/maverics/certs/maverics.key
http:
address: :443
tls: maverics
ldapProvider:
enabled: true
uri: ldaps://ldap.example.com:636
tls: ldapTLS
search:
searchSE:
funcName: Search
file: /etc/maverics/extensions/search.go
Ajout de la prise en charge des attributs personnalisés à renvoyer lors d’une requête Root DSE
tls:
ldapTLS:
certFile: /etc/maverics/certs/ldap.crt
keyFile: /etc/maverics/certs/ldap.key
maverics:
certFile: /etc/maverics/certs/maverics.crt
keyFile: /etc/maverics/certs/maverics.key
http:
address: :443
tls: maverics
ldapProvider:
enabled: true
uri: ldaps://ldap.example.com:636
tls: ldapTLS
rootDSE:
attributes:
subschemaSubentry:
- "CN=Aggregate,CN=Schema,CN=Configuration,DC=acme,DC=local"
namingContexts:
- "DC=acme,DC=local"
- "CN=Configuration,DC=acme,DC=local"
- "CN=Schema,CN=Configuration,DC=acme,DC=local"
- "DC=ForestDnsZones,DC=acme,DC=local"
- "DC=DomainDnsZones,DC=acme,DC=local"
defaultNamingContext:
- "DC=acme,DC=local"
schemaNamingContext:
- "CN=Schema,CN=Configuration,DC=acme,DC=local"
configurationNamingContext:
- "CN=Configuration,DC=acme,DC=local"
rootDomainNamingContext:
- "DC=acme,DC=local"
Ajout de la prise en charge de l’authentification SASL / GSS-SPNEGO/ NTLM
tls:
ldapTLS:
certFile: /etc/maverics/certs/ldap.crt
keyFile: /etc/maverics/certs/ldap.key
maverics:
certFile: /etc/maverics/certs/maverics.crt
keyFile: /etc/maverics/certs/maverics.key
http:
address: :443
tls: maverics
ldapProvider:
enabled: true
uri: ldaps://ldap.example.com:636
tls: ldapTLS
authentication:
sasl:
enabled: true
mechanisms:
gssspnego:
enabled: true
ntlm:
enabled: true
disableRequiring128bitEncryption: false
netbiosDomainName: ACME
netbiosMachineName: MAVERICS01
dnsDomainName: acme.org
dnsForestName: acme.org
dnsMachineName: MAVERICS01.acme.org
getHashedCredentialsSE:
funcName: NTLMGetHashedCredentials
file: /etc/maverics/extensions/gss_spnego_ntlm.go
Terminer la configuration
tls:
ldapTLS:
certFile: /etc/maverics/certs/ldap.crt
keyFile: /etc/maverics/certs/ldap.key
maverics:
certFile: /etc/maverics/certs/maverics.crt
keyFile: /etc/maverics/certs/maverics.key
http:
address: :443
tls: maverics
ldapProvider:
enabled: true
uri: ldaps://ldap.example.com:636
readDeadline: 5m
writeDeadline: 5m
tls: ldapTLS
rootDSE:
attributes:
subschemaSubentry:
- "CN=Aggregate,CN=Schema,CN=Configuration,DC=acme,DC=local"
namingContexts:
- "DC=acme,DC=local"
- "CN=Configuration,DC=acme,DC=local"
- "CN=Schema,CN=Configuration,DC=acme,DC=local"
- "DC=ForestDnsZones,DC=acme,DC=local"
- "DC=DomainDnsZones,DC=acme,DC=local"
defaultNamingContext:
- "DC=acme,DC=local"
schemaNamingContext:
- "CN=Schema,CN=Configuration,DC=acme,DC=local"
configurationNamingContext:
- "CN=Configuration,DC=acme,DC=local"
rootDomainNamingContext:
- "DC=acme,DC=local"
search:
searchSE:
funcName: Search
file: /etc/maverics/extensions/search.go
authentication:
allowAnonymous: false
methods:
simple:
enabled: true
authenticateSE:
funcName: Authenticate
file: /etc/maverics/extensions/simple.go
sasl:
enabled: true
mechanisms:
gssspnego:
enabled: true
ntlm:
enabled: true
disableRequiring128bitEncryption: false
netbiosDomainName: ACME
netbiosMachineName: MAVERICS01
dnsDomainName: acme.org
dnsForestName: acme.org
dnsMachineName: MAVERICS01.acme.org
getHashedCredentialsSE:
funcName: NTLMGetHashedCredentials
file: /etc/maverics/extensions/gss_spnego_ntlm.go
Exemples d’extensions de services
/etc/maverics/extensions/search.go
import (
"strings"
"github.com/strata-io/go-ntlm"
"github.com/strata-io/service-extension/orchestrator"
)
// This is an example of an Search Service Extension and shows a simple way to verify
// the LDAP connection between the application and the Orchestrator. It is configured
// to return hard-coded attributes for a test user, regardless of the Search request
// received.
//
// A production version will be dependant on the application and environment; however,
// typically, attributes originate from the end-users Session and would NOT be
// hardcoded. For example, if the policy requires a user to be authenticated
// using Entra ID, then the attributes would likely come from Entra ID, and stored
// on the cache via the Orchestrator servicing as the proxy for the application.
func Search(api orchestrator.Orchestrator, dn string, filter string, reqAttrs []string) (map[string]map[string]interface{}, error) {
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "Search",
"msg", "processing request",
"dn", dn,
"filter", filter,
"attributes", strings.Join(reqAttrs, ","),
)
result := make(map[string]map[string]interface{})
// User attributes would typically originate from the users Session and would NOT
// be hardcoded. For example, if the policy requires a user to be authenticated
// using Entra ID, then the attributes would likely come from Entra ID.
userAttrs := make(map[string]interface{})
userAttrs["Name"] = "Test User"
userAttrs["mail"] = "[email protected]"
userAttrs["givenName"] = "Test"
userAttrs["sn"] = "User"
result["CN=Test,CN=Domain Users,CN=Users,DC=acme,DC=local"] = userAttrs
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "Search",
"msg", "request processed",
"dn", dn,
"filter", filter,
"attributes", strings.Join(reqAttrs, ","),
)
return result, nil
}
/etc/maverics/extensions/simple.go
import (
"strings"
"github.com/strata-io/go-ntlm"
"github.com/strata-io/service-extension/orchestrator"
)
// This is an example Simple Authentication Service Extension that shows a quick way
// to test a Bind request using the Simple Authentication method between the
// application and the Orchestrator. In this example, only a user with the username
// of `bob` and the password of `th3Bu!ld3R` to be authenticated.
//
// A production version of this Service Extension would typically retrieve the
// password hash of the user from either `api.Cache` or `api.Session` and would
// compute the hash of the password received and compare it with the one stored. The
// user is typically a short-lived / one-time user created by an Orchestrator serving
// as the identity-aware proxy, after the end-user has been authenticated by
// the configured IdP.
func Authenticate(api orchestrator.Orchestrator, username string, password string) (bool, error) {
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "Authenticate",
"msg", "authenticating user",
"username", username,
)
if strings.EqualFold(username, "bob") && password == "th3Bu!ld3R" {
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "Authenticate",
"msg", "user authenticated",
"username", username,
)
return true, nil
}
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "Authenticate",
"msg", "user not authenticated",
"username", username,
)
return false, nil
}
/etc/maverics/extensions/gss_spnego_ntlm.go
import (
"fmt"
"github.com/strata-io/go-ntlm"
"github.com/strata-io/service-extension/orchestrator"
)
var (
// THIS IS FOR TESTING PURPOSES ONLY. Do not use this pattern in a production
// deployment.
testUsers = map[string][]byte{
fmt.Sprintf("%s:%s", ntlm.ToUnicode("acme"), ntlm.ToUnicode("user123")): ntlm.ToUnicode("abc123"),
}
)
// This is an example of an NTLM NTLMGetHashedCredentials Service Extension and shows
// a simple way to test users while verifying the LDAP connection between the
// application and the Orchestrator.
//
// A production version of this Service Extension would typically retrieve the
// NT and LM hashes of the user from either `api.Cache` or `api.Session`, which would
// typically be computed by an Orchestrator serving as the application proxy. The
// user is typically a short-lived / one-time user created after the end-user has
// been authenticated and authorized.
func NTLMGetHashedCredentials(
api orchestrator.Orchestrator,
user []byte,
domain []byte,
) (ntHash []byte, lmHash []byte, err error) {
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "NTLMGetHashedCredentials",
"msg", "retrieving NT and LM hashes for user",
"domain", fmt.Sprintf("%q", user[:]),
"user", fmt.Sprintf("%q", user[:]),
)
key := fmt.Sprintf("%s:%s", string(domain), string(user))
pass, ok := testUsers[key]
if !ok {
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "NTLMGetHashedCredentials",
"msg", "invalid user",
"domain", fmt.Sprintf("%q", domain[:]),
"user", fmt.Sprintf("%q", user[:]),
)
return nil, nil, fmt.Errorf("invalid user: %s", key)
}
ntHash = ntlm.NTOWFv2(pass, user, domain)
lmHash = ntlm.LMOWFv2(pass, user, domain)
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "NTLMGetHashedCredentials",
"msg", "NT and LM hashes found for user",
"domain", fmt.Sprintf("%q", domain[:]),
"user", fmt.Sprintf("%q", user[:]),
)
return ntHash, lmHash, err
}
Télémétrie
bientôt disponible.
Glossaire
- SASL : Simple Authentication and Security Layer.
- GSS : Generic Security Service API.
- SPNEGO : Mécanisme de négociation GSSAPI simple et protégé.
- NTLM : New Technology LAN Manager.
- OID : Object Identifier.