You can configure SystemLink to use the Lightweight Directory Access Protocol (LDAP) for user authentication. Use LDAP attributes and groups to map users to roles and workspaces in SystemLink’s role based access control system.
5.1 Assumptions and Prerequisites¶
-
A server running SystemLink.
- Refer to Installing and Configuring SystemLink Server and Clients for the basics of setting up a SystemLink server.
-
Administrator desktop access to the SystemLink server.
-
An LDAP server accessible to the SystemLink server.
-
A bind user and bind password for the LDAP server.
- SystemLink supports anonymous connections. In this case the bind user and bind password are not needed.
-
Familiarity with the LDAP attributes available to your organization.
- If you do not know what LDAP attributes and groups are available to you, talk with your LDAP system administrator.
- Tools such ADExplorer can be helpful to explore the attributes assigned to your users.
5.2 Enabling LDAP in SystemLink¶
5.2.1 Configuring Authentication¶
-
Log into the server running SystemLink and open NI Web Sever Configuration.
-
Go to the Authentication tab and enable Connect to an LDAP server.
-
Enter the LDAP URL for your sever. Refer to 5.3.1 LDAP URLs for details on how to structure this URL.
-
Enter the bind user. Refer to 5.3.2 Bind User for details on usernames that can be used here.
-
Enter the bind password.
-
Click Apply and Restart.
You may now log in to the SystemLink server using your LDAP credentials. To access systems and data in SystemLink, configure workspace and role mappings. Refer to Mapping LDAP Attributes and Groups to SystemLink Workspaces and Roles for details.
5.3 LDAP URL and Bind User¶
You must provide a URL and bind user and bind password to establish an authenticated connection between your SystemLink server and LDAP server.
5.3.1 LDAP URL¶
The LDAP URL follows a standard scheme.
URL Scheme
<ldap>://<server-dns>:<port>/<target-entry-dn>?<username-attribute>?<scope>?<filter>
protocol: ldap
or ldaps
for secure connections. SystemLink does not support STARTTLS, which allows connections over non-TLS and then initiates a TLS handshake.
server-dns: The LDAP server SystemLink is connecting to.
port: The port of the LDAP server. If your LDAP server is backed by Windows Active Directory, you may point to the global catalog on port 3268 to enable login from multiple domains (forest).
target-entry-dn: The base search distinguished name (DN) for the LDAP directory.
user-name-attribute: The attribute that determines the SystemLink username for login.
scope: The scope of the directory search. Scope defaults to sub
but can be set to one
to restrict users to the base DN.
filter: The LDAP search filter. This defaults to objectClass=*
to find find all objects in the directory. This allows you to restrict login to users who have a specific attribute.
Basic LDAP URL Example
Example: ldap://example.com:389/dc=example,dc=com?sAMAccountName
In this example the server is example.com
, the port is 389, the base search DN is dc=example,dc=com
and the LDAP attribute used for user login is sAMAccountName
.
Example LDAP URLs specifying different usernames for the user Jane Doe
URL: ldap://example.com/dc=example,dc=com?sAMAccountName
Username: jdoe
URL: ldap://example.com/dc=example,dc=com?userPrincipalName
Username: jane.doe@example.com
jdoe
is used because the sAMAccountName
attribute is specified in the LDAP URL.If a username attribute is not specified NI Web Server Configuration will automatically add uid
as the attribute. Depending on your LDAP directory setup this attribute may not be available.
5.3.2 Bind User¶
The bind user and bind password are used to authenticate with the LDAP server. Provide a distinguishedName
or a userPrincipalName
to specify the bind user. If the password for this user changes, NI Web Server Configuration must be updated for users to continue to log into SystemLink.
Bind user formats for the user Jane Doe
distinguishedName: cn=jdoe,dc=example,dc=com
userPrincipalName: jane.doe@example.com
5.4 Mapping LDAP Groups, Users, and Attributes to Workspaces and Roles¶
To add a user or collection of users to a workspace and assign a role you must complete the role mapping workflow.
-
Log in to the SystemLink web application with a user mapped to the Server Administrator role.
-
Go to Access Control > Workspaces and click the gear icon in the top right.
-
Create a new workspace or edit an existing workspace.
-
Go to the Role Mappings tab.
-
Click +MAPPING and select one of the available LDAP mapping types: LDAP Group, LDAP User, or LDAP Attribute.
-
If you have have selected LDAP Attribute enter a valid key and value. For LDAP User or LDAP Group enter a valid value.
5.4.1 LDAP Group Mapping¶
LDAP Group mapping queries the objectClass
of group
(Active Directory specific), groupOfName
, and groupOfUniqueNames
to match either member
or uniqueMember
attributes of the group.
!!! note Group names should be declared using the full LDAP distinguishedName
of the group.
cn=Users,dc=example,dc=com
5.4.2 LDAP User Mapping¶
You may specify a LDAP username when creating workspace and role mappings. The username you specify is the same as the usernames for logging into SystemLink.
5.4.3 LDAP Attribute Mapping¶
You can use any available LDAP attribute to create a workspace and role mapping. In this case both the name and value of the attribute must match exactly for the mapping to be successful.
Example LDAP attribute mapping
c
with the value US
to the Data Maintainer Role in the Default workspace. Last update: August 23, 2022
As specified in RFC4511 the Bind operation is the “authenticate” operation. It (and the Unbind operation as well) has
this name for historical reason.
When you open a connection to an LDAP server you’re in an anonymous connection state. What this exactly means
is defined by the server implementation, not by the protocol. Think of this as a public access to the server data
(even if what public data mean is still a server matter). In ldap3 you establish the connection to the server
with the open()
method of the Connection object. The bind()
method will open the connection if not already open.
The Bind operation allows credentials to be exchanged between the client and server to establish a new
authorization state.
The Bind request typically specifies the desired authentication identity. Some Bind mechanisms also allow the client
to specify the authorization identity. If the authorization identity is not specified, the server derives it from the
authentication identity in an implementation-specific manner.
If you want to provide authentication information you must use the Bind operation to specify an identity to use to
access the data. Keep in mind that either the authentication details than the authorization details
are a local server matter. The LDAP protocol doesn’t specify how the identity must be stored on the server nor how the
authorization ACLs are specified.
The Bind operation specify 4 different methods to authenticate to the server, as specified in RFC4513:
-
Simple Bind: you provide user credentials by the means of a username (in a dn form) and a password.
-
Anonymous Bind: the user and password are passed as empty strings.
-
Unauthenticated simple Bind: you pass a username without a password. This method, even if specified in the protocol,
should not be used because is higly insecure and should be forbidden by the server. It was used in the past for tracing purpose. -
SASL (Simple Authentication and Security Layer): this defines multiple mechanisms that each server can provide to allow access to the server.
Before trying a mechanism you should check that the server supports it. The LDAP server publish its allowed SASL mechanism in the DSE information
that can be read anonymously with theget_info=ALL
parameter of the Server object.
The Bind method returns True if the bind is successful, False if something goes wrong while binding. In this case you
can inspect the result
attribute of the Connection object to get the error description.
Simple Bind
You perform a Simple Bind operation as in the following example (using the default synchronous strategy):
# import class and constants from ldap3 import Server, Connection, ALL # define the server s = Server('servername', get_info=ALL) # define an unsecure LDAP server, requesting info on DSE and schema # define the connection c = Connection(s, user='user_dn', password='user_password') # perform the Bind operation if not c.bind(): print('error in bind', c.result)
The server and the connection are created with the default parameters:
s = Server(host='servername', port=389, use_ssl=False, get_info='ALL') c = Connection(s, user='user_dn', password='user_password', auto_bind='NONE', version=3, authentication='SIMPLE', \ client_strategy='SYNC', auto_referrals=True, check_names=True, read_only=False, lazy=False, raise_exceptions=False)
Refer to the Server and Connection docs for info about the default parameters.
Anonymous Bind
Anonymous bind performs a simple bind with the user name and the user password set to empty strings. The ldap3 library has
a specific authentication option to do that:
# import class and constants from ldap3 import Server, Connection, ALL # define the server s = Server('servername', get_info=ALL) # define an unsecure LDAP server, requesting info on DSE and schema # define the connection c = Connection(s) # define an ANONYMOUS connection # perform the Bind operation if not c.bind(): print('error in bind', c.result)
The server and the connection are created with the default parameters:
s = Server(host='servername', port=389, use_ssl=False, get_info='ALL') c = Connection(s, auto_bind='NONE', version=3, authentication='ANONYMOUS', client_strategy='SYNC', auto_referrals=True, \ check_names=True, read_only=False, lazy=False, raise_exceptions=False)
To use SSL basic authentication change the server definition to:
s = Server('servername', use_ssl=True, get_info=ALL) # define a secure LDAP server on the default 636 port
StartTLS
If you want to raise the transport layer security to an encrypted state you can perform the StartTLS extended operation. With this
mechanism you can wrap the plain socket in an SSL encrypted socket:
From now on the communication transport is encrypted. You should properly configure the Server object addind a Tls object
with the relevant ssl configuration:
t = Tls(local_private_key_file='client_private_key.pem', local_certificate_file='client_cert.pem', validate=ssl.CERT_REQUIRED, version=ssl.PROTOCOL_TLSv1, ca_certs_file='ca_certs.b64') s = Server('servername', tls=t, get_info=ALL)
Please refer to the SSLTLS section for more information.
SASL
Three SASL mechanisms are currently implemented in the ldap3 library: EXTERNAL, DIGEST-MD5, GSSAPI (Kerberos, via the gssapi package) and PLAIN. DIGEST-MD5 is
implemented even if it is deprecated and moved to historic (RFC6331, July 2011) because it is “insecure and unsuitable for use in protocols”
(as stated by the RFC).
To query the SASL mechanism available on the server you must read the information published by the server. The ldap3 library
has a convenient way to do that:
from ldap3 import Server, Connection, ALL s = Server('servername', get_info=ALL) # define an unsecure LDAP server, requesting info on DSE and schema c = Connection(s) c.open() # establish connection without performing any bind (equivalent to ANONYMOUS bind) print(s.info.supported_sasl_mechanisms)
Print out a list of the SASL mechanism supported by the server:
['EXTERNAL', 'DIGEST-MD5', 'GSSAPI']
External
You can use the EXTERNAL mechanism when you’re on a secure (TLS) channel. You can provide an authorization identity string in sasl_credentials
or let the
server trust the credential provided when establishing the secure channel:
from ldap3 import Server, Connection, Tls, SASL,EXTERNAL tls = Tls(local_private_key_file = 'key.pem', local_certificate_file = 'cert.pem', validate = ssl.CERT_REQUIRED, version = ssl.PROTOCOL_TLSv1, ca_certs_file = 'cacert.b64') server = Server(host = test_server, port = test_port_ssl, use_ssl = True, tls = tls) c = Connection(server, auto_bind = True, version = 3, client_strategy = test_strategy, authentication = SASL, sasl_mechanism = EXTERNAL, sasl_credentials = 'username')
Digest-MD5
To use the DIGEST-MD5 mechanism you must pass a 4-value or 5-value tuple as sasl_credentials: (realm, user, password, authz_id, enable_protection). You can pass None
for ‘realm’, ‘authz_id’ and ‘enable_protection’ if not used:
from ldap3 import Server, Connection, SASL, DIGEST_MD5 server = Server(host = test_server, port = test_port) c = Connection(server, auto_bind = True, version = 3, client_strategy = test_strategy, authentication = SASL, sasl_mechanism = DIGEST_MD5, sasl_credentials = (None, 'username', 'password', None, ENCRYPT))
Username is not required to be an LDAP entry, but it can be any identifier recognized by the server (i.e. email, principal, …). If
you pass None as ‘realm’ the default realm of the LDAP server will be used.
enable_protection
is an optional argument, which is only relevant for Digest-MD5 authentication. This argument enable or disable signing/encryption
(Integrity or Confidentiality protection) when performing LDAP queries.
LDAP signing is a way to prevent replay attacks without encrypting the LDAP traffic. Microsoft publicly recommend to enforce LDAP signing when talking to
an Active Directory server : https://support.microsoft.com/en-us/help/4520412/2020-ldap-channel-binding-and-ldap-signing-requirements-for-windows
LDAP encryption is a way to prevent eavesdropping, it is especially useful to send/receive sensitive data (e.g password change for a user). Active Directory supports Digest-MD5 encryption : https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/a98c1f56-8246-4212-8c4e-d92da1a9563b.
-
When
enable_protection
is set to SIGN, LDAP requests are signed and signature of LDAP responses is verified. -
When
enable_protection
is set to ENCRYPT, LDAP requests are encrypted and LDAP responses are decrypted and their signature is verified. -
When
enable_protection
is set to any other value or not set, LDAP requests are not signed.
Using DIGEST-MD5 is considered deprecated (RFC6331, July 2011) and should not be used.
Kerberos
Kerberos authentication uses the gssapi
package. You must install it and configure your Kerberos environment to use the GSSAPI mechanism:
from ldap3 import Server, Connection, Tls, SASL, KERBEROS import ssl tls = Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLSv1_2) server = Server('<servername>', use_ssl=True, tls=tls) c = Connection( server, authentication=SASL, sasl_mechanism=KERBEROS) c.bind() print(c.extend.standard.who_am_i())
You can specify which Kerberos client principal should be used with the user
parameter when declaring the Connection:
c = Connection( server, user='ldap-client/client.example.com', authentication=SASL, sasl_mechanism=KERBEROS)
By default the library attempts to bind against the service principal for the domain you attempted to connect to.
If your target LDAP service uses a round-robin DNS, it’s likely that the hostname you connect to won’t match. In this case,
you can either specify a hostname explicitly as the first element of the sasl_credentials
connection parameter,
or pass an appropriate value of the ReverseDnsSetting
enum as the first element to do a reverse DNS lookup:
# Override server hostname for authentication c = Connection( server, sasl_credentials=('ldap-3.example.com',), authentication=SASL, sasl_mechanism=KERBEROS) # Perform a reverse DNS lookup to determine the hostname to authenticate against regardless of server specification. c = Connection(server, sasl_credentials=(ReverseDnsSetting.REQUIRE_RESOLVE_ALL_ADDRESSES,), authentication=SASL, sasl_mechanism=KERBEROS) # Only perform a reverse DNS lookup to determine the hostname to authenticate against if the server's host is # specified as an IP address c = Connection(server, sasl_credentials=(ReverseDnsSetting.REQUIRE_RESOLVE_IP_ADDRESSES_ONLY,), authentication=SASL, sasl_mechanism=KERBEROS) # Perform a reverse DNS lookup to determine the hostname to authenticate against, but if that lookup fails, proceed # and attempt to use the server host as is. # This is useful when working with serverpools where some servers are resolvable through reverse dns and need it, # while other servers are not resolvable and do not need it. c = Connection(server, sasl_credentials=(ReverseDnsSetting.OPTIONAL_RESOLVE_ALL_ADDRESSES,), authentication=SASL, sasl_mechanism=KERBEROS)
Note
ldap3 currently support SASL authentication and data security layers for encryption. If your server requires a string Security Strength Factor (SSF), you must enable data security layers using session_security=ENCRYPT
.
from ldap3 import KERBEROS, ENCRYPT, Connection c = Connection(server, authentication=SASL, sasl_mechanism=KERBEROS, session_security=ENCRYPT)
Plain
The PLAIN SASL mechanism sends data in clear text, so it must rely on other means of securing the connection between the client and the LDAP server.
As stated in RFC4616 the PLAIN mechanism should not be used without adequate data security protection as this mechanism affords no integrity or confidentiality
protections itself. The mechanism is intended to be used with data security protections provided by application-layer protocol,
generally through its use of Transport Layer Security (TLS) services.
To use the PLAIN mechanism you must pass a 3-value tuple as sasl_credentials: (authorization_id, authentication_id, password). You can pass None
for authorization_id if it is not used:
from ldap3 import Server, Connection, SASL, PLAIN server = Server(host = test_server, port = test_port, use_ssl=True) c = Connection(server, auto_bind=True, authentication=SASL, sasl_mechanism=PLAIN, sasl_credentials=(None, 'username', 'password'))
NTLM
The ldap3 library supports an additional method to bind to Active Directory servers via the NTLM method:
# import class and constants from ldap3 import Server, Connection, SIMPLE, SYNC, ALL, SASL, NTLM # define the server and the connection s = Server('servername', get_info=ALL) c = Connection(s, user="AUTHTEST\\Administrator", password="password", authentication=NTLM) # perform the Bind operation if not c.bind(): print('error in bind', c.result)
This authentication method is specific for Active Directory and uses a proprietary authentication protocol named SICILY
that breaks the LDAP RFC but can be used to access AD.
When binding via NTLM, it is also possible to authenticate with an LM:NTLM hash rather than a password:
c = Connection(s, user="AUTHTEST\\Administrator", password="E52CAC67419A9A224A3B108F3FA6CB6D:8846F7EAEE8FB117AD06BDD830B7586C", authentication=NTLM)
- You can enable LDAP Channel Binding over TLS with the argument
channel_binding=TLS_CHANNEL_BINDING
:: -
- c = Connection(s, user=”AUTHTEST\Administrator”, password=”E52CAC67419A9A224A3B108F3FA6CB6D:8846F7EAEE8FB117AD06BDD830B7586C”
-
channel_binding=TLS_CHANNEL_BINDING, authentication=NTLM)
This option is only relevant for NTLM authentication done over TLS.
It also supports confidentiality when performing LDAP Queries using the following:
c = Connection(s, user=”AUTHTEST\Administrator”, password=”E52CAC67419A9A224A3B108F3FA6CB6D:8846F7EAEE8FB117AD06BDD830B7586C”, authentication=NTLM, session_security=ENCRYPT)
LDAPI (LDAP over IPC)
If your LDAP server provides a UNIX socket connection you can use the ldapi: (Interprocess Communication) scheme to access it from the
same machine:
>>> # accessing OpenLDAP server in a root user session >>> s = Server('ldapi:///var/run/slapd/ldapi') >>> c = Connection(s, authentication=SASL, sasl_mechanism=EXTERNAL, sasl_credentials='') >>> c.bind() True >>> c.extend.standard.who_am_i() dn:cn=config
Using the SASL EXTERNAL mechanism allows you to provide to the server the credentials of the logged user.
While accessing your LDAP server via a UNIX socket you can perform any usual LDAP operation. This should be faster than using a TCP connection.
You don’t need to use SSL when connecting via a socket because all the communication is in the server memory and is not exposed on the wire.
Bind as a different user while the Connection is open
LDAP protocol allows to bind as a different user while the connection is open. In this case you can use the rebind() method
that let you change the user and the authentication method while the connection is open:
# import class and constants from ldap3 import Server, Connection, ALL, LDAPBindError # define the server s = Server('servername', get_info=ALL) # define an unsecure LDAP server, requesting info on DSE and schema # define the connection c = Connection(s, user='user_dn', password='user_password') # perform the Bind operation if not c.bind(): print('error in bind', c.result) # Bind again with another user if not c.rebind(user='different_user_dn', password='different_user_password') print('error in rebind', c.result)
If credentials are invalid or the server doesn’t allow you to rebind the server could abruptly close the connection.
This condition is checked by the rebind()
method and an LDAPBindError exception will be raised if caught.
If you want an exception raised when credentials are invalid you must use the raise_exceptions=True
parameter in the Connection() definition. Keep
in mind that network errors always raise an exception, even if raise_exceptions is set to False.
Extended logging
To get an idea of what’s happening when you perform a Simple Bind operation using the StartTLS security feature this is
the extended log from a session to an OpenLdap server from a Windows client with dual stack IP:
# Initialization: INFO:ldap3:ldap3 library initialized - logging emitted with loglevel set to DEBUG - available detail levels are: OFF, ERROR, BASIC, PROTOCOL, NETWORK, EXTENDED DEBUG:ldap3:ERROR:detail level set to EXTENDED DEBUG:ldap3:BASIC:instantiated Server: <Server(host='openldap', port=389, use_ssl=False, get_info='NO_INFO')> DEBUG:ldap3:BASIC:instantiated Usage object DEBUG:ldap3:BASIC:instantiated <SyncStrategy>: <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - closed - <no socket> - tls not started - not listening - No strategy - async - real DSA - not pooled - cannot stream output> DEBUG:ldap3:BASIC:instantiated Connection: <Connection(server=Server(host='openldap', port=389, use_ssl=False, get_info='NO_INFO'), user='cn=admin,o=test', password='<stripped 8 characters of sensitive data>', auto_bind='NONE', version=3, authentication='SIMPLE', client_strategy='SYNC', auto_referrals=True, check_names=True, collect_usage=True, read_only=False, lazy=False, raise_exceptions=False)> DEBUG:ldap3:NETWORK:opening connection for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - closed - <no socket> - tls not started - not listening - SyncStrategy> DEBUG:ldap3:BASIC:reset usage metrics DEBUG:ldap3:BASIC:start collecting usage metrics DEBUG:ldap3:BASIC:address for <ldap://openldap:389 - cleartext> resolved as <[<AddressFamily.AF_INET6: 23>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('fe80::215:5dff:fe8f:2f0d%20', 389, 0, 20)]> DEBUG:ldap3:BASIC:address for <ldap://openldap:389 - cleartext> resolved as <[<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('192.168.137.104', 389)]> DEBUG:ldap3:BASIC:obtained candidate address for <ldap://openldap:389 - cleartext>: <[<AddressFamily.AF_INET6: 23>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('fe80::215:5dff:fe8f:2f0d%20', 389, 0, 20)]> with mode IP_V6_PREFERRED DEBUG:ldap3:BASIC:obtained candidate address for <ldap://openldap:389 - cleartext>: <[<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('192.168.137.104', 389)]> with mode IP_V6_PREFERRED # Opening the connection (trying IPv6 then IPv4): DEBUG:ldap3:BASIC:try to open candidate address [<AddressFamily.AF_INET6: 23>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('fe80::215:5dff:fe8f:2f0d%20', 389, 0, 20)] DEBUG:ldap3:ERROR:<socket connection error: [WinError 10061] No connection could be made because the target machine actively refused it.> for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - closed - <local: [::]:49610 - remote: [None]:None> - tls not started - not listening - SyncStrategy> DEBUG:ldap3:BASIC:try to open candidate address [<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('192.168.137.104', 389)] DEBUG:ldap3:NETWORK:connection open for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:BASIC:refreshing server info for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:BASIC:start START TLS operation via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:BASIC:instantiated Tls: <Tls(validate=0)> # Starting TLS - wrapping the socket in an ssl socket: DEBUG:ldap3:BASIC:starting tls for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:BASIC:start EXTENDED operation via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:PROTOCOL:EXTENDED request <{'name': '1.3.6.1.4.1.1466.20037', 'value': None}> sent via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:PROTOCOL:new message id <1> generated DEBUG:ldap3:NETWORK:sending 1 ldap message for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:EXTENDED:ldap message sent via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy>: >>LDAPMessage: >> messageID=1 >> protocolOp=ProtocolOp: >> extendedReq=ExtendedRequest: >> requestName=b'1.3.6.1.4.1.1466.20037' DEBUG:ldap3:NETWORK:sent 31 bytes via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:NETWORK:received 14 bytes via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:NETWORK:received 1 ldap messages via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:EXTENDED:ldap message received via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy>: <<LDAPMessage: << messageID=1 << protocolOp=ProtocolOp: << extendedResp=ExtendedResponse: << resultCode='success' << matchedDN=b'' << diagnosticMessage=b'' DEBUG:ldap3:PROTOCOL:EXTENDED response <[{'referrals': None, 'dn': '', 'type': 'extendedResp', 'result': 0, 'description': 'success', 'responseName': None, 'responseValue': b'', 'message': ''}]> received via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:BASIC:done EXTENDED operation, result <True> DEBUG:ldap3:BASIC:tls started for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls not started - listening - SyncStrategy> DEBUG:ldap3:NETWORK:socket wrapped with SSL using SSLContext for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: [None]:None - remote: [None]:None> - tls not started - listening - SyncStrategy> DEBUG:ldap3:BASIC:refreshing server info for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:BASIC:done START TLS operation, result <True> # Performing Bind operation with Simple Bind method: DEBUG:ldap3:BASIC:start BIND operation via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:PROTOCOL:performing simple BIND for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:PROTOCOL:simple BIND request <{'name': 'cn=admin,o=test', 'authentication': {'sasl': None, 'simple': '<stripped 8 characters of sensitive data>'}, 'version': 3}> sent via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:PROTOCOL:new message id <2> generated DEBUG:ldap3:NETWORK:sending 1 ldap message for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:EXTENDED:ldap message sent via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy>: >>LDAPMessage: >> messageID=2 >> protocolOp=ProtocolOp: >> bindRequest=BindRequest: >> version=3 >> name=b'cn=admin,o=test' >> authentication=AuthenticationChoice: >> simple=b'<stripped 8 characters of sensitive data>' DEBUG:ldap3:NETWORK:sent 37 bytes via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:NETWORK:received 14 bytes via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:NETWORK:received 1 ldap messages via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:EXTENDED:ldap message received via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy>: <<LDAPMessage: << messageID=2 << protocolOp=ProtocolOp: << bindResponse=BindResponse: << resultCode='success' << matchedDN=b'' << diagnosticMessage=b'' DEBUG:ldap3:PROTOCOL:BIND response <{'referrals': None, 'dn': '', 'type': 'bindResponse', 'result': 0, 'description': 'success', 'saslCreds': None, 'message': ''}> received via <ldap://openldap:389 - cleartext - user: cn=admin,o=test - unbound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:BASIC:refreshing server info for <ldap://openldap:389 - cleartext - user: cn=admin,o=test - bound - open - <local: 192.168.137.1:49611 - remote: 192.168.137.104:389> - tls started - listening - SyncStrategy> DEBUG:ldap3:BASIC:done BIND operation, result <True>
These are the usage metrics of this session:
Connection Usage: Time: [elapsed: 0:00:01.908938] Initial start time: 2015-06-02T09:37:49.451263 Open socket time: 2015-06-02T09:37:49.451263 Close socket time: Server: Servers from pool: 0 Sockets open: 1 Sockets closed: 0 Sockets wrapped: 1 Bytes: 96 Transmitted: 68 Received: 28 Messages: 4 Transmitted: 2 Received: 2 Operations: 2 Abandon: 0 Bind: 1 Add: 0 Compare: 0 Delete: 0 Extended: 1 Modify: 0 ModifyDn: 0 Search: 0 Unbind: 0 Referrals: Received: 0 Followed: 0 Restartable tries: 0 Failed restarts: 0 Successful restarts: 0
As you can see there have been two operation, one for the bind and one for the startTLS (an extendend operation). One socket
has been open and has been wrapped in SSL. All the communication stream took 96 bytes in 4 LDAP messages.
Introduction
This document describes the necessary architecture in order to properly enable ldap authentication and group authorization on a datapower. The first section considers general architecture, limitations on the solution, and caveats to progress. Finally, actual build configuration is covered as well as how to implement ‘secure’ ldap instead of just ldap.
General Architecture
This document assumes that you have setup a proper Active Directory domain using windows server 2012R2. Other versions of windows may work, but were not tested with this configuration. Also note that if secure LDAP is desired, that the domain server MUST be configured with certificate services, and that the signing cert’s common name MUST match its host name in order to work.
Directory Setup
In order for the datapower to easily lookup group memberships, it is required to setup a new OU, called ‘groups’ at the top of the active directory tree. This makes it more efficient to lookup usernames, as the system doesn’t have to peruse the entire directory tree. For further segregation by environment the figure below delineates a typical structure:
The TLD (top level domain) is the domain, followed by the “OU” of groups, which has two Common Names, (groups actually), followed by further divisions into prod, dev, and Qa environments which can have separate permissions (with the appropriate modifications of the configuration files as noted later in this documents). Breaking things down this way enables fairly complex rule possibilities, while still maintaining a relatively simple structure.
Additionally, it is important to note that case is VITAL when referencing items within the directory. Windows seems to return UPPERCASE attributes, yet seems to expect ‘lower case’ when sending things.
Bind Credentials to perform Searches
Windows server 2012 and others do not allow for anonymous binds for security reasons. It is strongly recommended that anonymous binding NOT be enabled, but rather to setup a low privilege user, with a sufficiently complex password, to allow binding to the directory.
It is imperative that the account NOT be set to expire, since this would cause the datapower devices to fail ALL users until the credential was updated on ALL datapowers involved in central authentication. For the purposes of this document, it is assumed that a standard ‘domain’ level user will be created, and will be named ‘datapower’.
It is also important to NOT fill out ANY fields such as ‘first name’ last name, etc., or the processes described in here will FAIL, since the bind strings will use the first name, middle initial, and last name as a LOGIN name! ONLY when these fields are left blank will this solution work.
Secure vs Insecure LDAP for initial configuration
While the initial temptation to use ‘secure’ ldap may be strong, it is advised against until a configuration has been fully tested. Below are the reasons why:
- Secure ldap prevents easy debugging of packet traces
- Secure ldap can have issues of its own, such as crypto mismatch, cert issues, that can make it confusing to find out if your configuration is valid
- Secure LDAP also requires additional configuration on datapower side, that can cause logins to fail, which might be falsely attributed to a defective rbm file.
It is recommended that secure ldap ONLY be enabled after a configuration has been fully vetted and tested.
Authorization File / Group Mapping
The primary method for enabling remote login / group assignments is via an xml file called the ‘rbm’ or ‘role based management’ file. This file is created, edited, and then uploaded to the datapower, where it enforces group and user authorizations (for the purposes of this document, ONLY group memberships are used).
It is VITAL that there always be a fallback account on the local machine, in case remote system are prevented from logging in. Failure to enable ‘local user bypass’ will cause the machine to be locked out, and may possibly require an RMA from IBM, or a command line reset of permissions. It is recommended that ‘user timeouts’ be disabled, and a console be logged into with the ‘admin’ account, should remediation become necessary.
It is rather confusing, but all local user groups are IGNORED once the custom rbm is uploaded, and is only valid for LOCAL user accounts. There is NO correlation between local groups and AD(active directory) groups!
Caveats
There are many peculiarities of setting up LDAP on a datapower. Beware the following:
- The “Bind” CN used for searching the ldap directory should have NO information filled out in ANY of its fields or the solution here will not work. Windows will use “Thomas J Munn” instead of ‘tmunn’ as a login if there is First, Last, or Middle initial provided. It also will use ‘spaces’ and ALL characters listed in the user attribute fields.
- For returning the actual ‘username’ that everyone expects, the ‘ sAMAccountName’ attribute (without the backticks!). The CASE IS CRITICAL on this.
- This solution can ONLY work for users with 1 group. Multiple group memberships within the same OU (e.g. OU=groups), will FAIL. For further subdivision, see section on segregating the directory by OU for more complex groupings.
- The CSR (signing authority) needs to match the DNS name of the server presenting itself as the LDAP server. If they don’t match, SSL will fail
- A dedicated ‘datapower’ user is recommended, because binding to an AD directory anonymously is not recommended.
Configuration
In this section, basic configuration of the datapower is covered. For clarity, the CLI will be used, and only where such is not possible will the GUI be used. In such cases, screen shots will be provided, with textual descriptions below them. Things to be typed in will use the style below
Command 1 parameter
Additionally, parameters that need to be change will be in italics. This might include things like the domain name, and appropriate suffixes.
Basic LDAP Authentication and Authorization sections
The sections below detail the necessary changes to make the datapower use LDAP for authorization and group membership information.
CLI Stanzas- Ldap Search Parameters
ldap-search-parameters “ldapsearch”
base-dn “CN=Users,dc=munnster,dc=com“
filter-prefix “sAMAccountName=”
exit
ldap-search-parameters “SearchForGroup“
base-dn “OU=groups,dc=munnster,dc=com“
filter-prefix “(&(member=”
filter-suffix “)(|(cn=admin)(cn=readonly)(cn=sysadmin)))”
exit
Note that the three groups as noted in the ‘filter suffix’ means that you can ONLY be a member of the ‘admin, readonly, or sysadmin’ groups. More groups can be added assuming parenthesis are balanced properly.
RBM CLI Parameters
The CLI parameters below assume are for reference only, it is advised to use the “GUI” to work here. Directly referencing the ‘xml’ file without using the GUI can be dangerous, so it is encouraged to use GUI.
rbm
admin-state enabled
au-method ldap
au-server-host maafu.munnster.com
au-server-port 389
au-ldap-search
au-ldap-bind-dn “CN=Datapower,CN=Users,dc=munnster,dc=com“
au-ldap-bind-password “supersecretpasswordhere“
au-ldap-parameters ldapsearch
mc-method xmlfile
mc-ldap-search
mc-server-host maafu.munnster.com
mc-server-port 389
mc-ldap-bind-dn “CN=Datapower,CN=Users,dc=munnster,dc=com“
mc-ldap-bind-password “supersecretpassword“
mc-ldap-parameters SearchForGroup
mc-info-url “local:///RBMLDAP-ibm.xml”
ldap-version v3
fallback-local login
exit
A couple of lines bear some explanation. The ‘au-server-host’ is the ldap server, the ‘bind dn’ is the ‘common name’ of the userid doing the bind to the directory.
‘fallback-login local’ means if you don’t do things right, you can get back into the datapower. Please note that the RBMLDAP-ibm.xml should be uploaded and added via the GUI tool. This section is mostly for reference when doing the ‘gui’ portion of the configuration.
RBM Settings Dialogue
RBM Settings dialogue
- Go to ‘administration, rbm settings. Ensure things are ‘enabled’ and that RBM on cli and restrict admin login are ‘off’
- Go to the ‘authentication’ tab, since most of this was done in the CLI, refer to CLI section for actual parameters to type in. CASE IS CRITICAL. The picture on the next page details the necessary settings to choose.
The next section details changes necessary to the authentication tab:
Necessary settings for authentication. See CLI section for proper values!
Credentials (authorization) process from GUI:
RBM Settings Dialogue
RBM Upload Process
The section following this page details how to setup the RBM info file, and upload it to the server. First is the file itself. It is described in the following pages. Second is to have the datapower ‘process’ it to ensure that there are no errors, and final is to ‘accept’ the profile change to apply the Role Based Management mapping file. This file contains the groups in ldap and changes them to datapower permissions. CASE IS CRITICAL, SYNTAX IS CRITICAL. It is recommended that if frequent edits are necessary to have a program that will properly format the file. Human editing beyond initial configuration is strongly discouraged. Also if you have any errors, ALL ACCESS to device is lost, save local accounts!
RBMLDAP-ibm.xml listing
<aaa:AAAInfo xmlns:dpfunc=”http://www.datapower.com/extensions/functions” xmlns:aaa=”http://www.datapower.com/AAAInfo”>
<aaa:FormatVersion>1</aaa:FormatVersion>
<aaa:Filename>local:///RBMLDAP-ibm.xml</aaa:Filename>
<aaa:Summary>Map Users in LDAP for Cap1.</aaa:Summary>
<!– Determine credential from output of the extract-identity phase. –>
<!– Specify credential (if any) to use when there is no authenticated identity. –>
<!– Map credentials to different credentials. –>
<!– Ensure that you change to match Cap1 standards, as this file is formatted for the lab! –>
<!– This first line defines the LDAP groups to map –>
<!– CASE IS VITAL if you do incorrect case, the system WILL NOT authorize properly. –>
<aaa:MapCredentials>
<aaa:InputCredential>CN=admin,OU=Groups,DC=munnster,DC=com</aaa:InputCredential>
<!– This next line outputs the local group on datapower from output from previous line –>
<aaa:OutputCredential>*/*/*?Access=x+d+a+w+r</aaa:OutputCredential>
</aaa:MapCredentials>
<aaa:MapCredentials>
<aaa:InputCredential>CN=readonly,OU=Groups,DC=munnster,DC=com</aaa:InputCredential>
<aaa:OutputCredential>*/*/*?Access=r</aaa:OutputCredential>
</aaa:MapCredentials>
</aaa:AAAInfo>
Some explanations.
The line ‘aaa:InputCredential” is the ldap bind that will return the ‘admin’ group of which people have been made members of. As noted earlier, it would be the ou created in the earlier section. Obviously the DC=blah, DC=com needs to be changed to match your domain suffix. Names of groups are CASE SENSITIVE. Separate access within a group can be added by using appropriate syntax and a ‘space’ between the two permission statements. MULTIPLE GROUPS WILL NOT MATCH!!!
Having built this file, the subsequent screens will show how to get this working. Again, prior to doing this CREATE A BACKUP ADMIN USER.
It is recommended that an XML syntax checker be applied to any file being edited, such as Notepad ++ with the XML syntax checker.
GUI Upload Process for RBMINFO.XML file
From the ‘+’ on the previous page diagram, choose ‘new rbm file’
Choose ‘upload’, and using naming convention as noted, choose file, and continue.
RBM upload Step 2
choose the ‘next’ button
Choose the “next” button again
Choose ‘next’
Your final screen should look like the above screen.
The final step is to commit. Be sure to ‘overwrite’ existing file, or the system will force you to create a new file and things will FAIL. Also note that taking too long will cause the system to ‘time out’ and lose all of your work!
Final Committ Dialogue
Choose ‘commit’ and then ‘save’ and apply. This concludes the configuration of the RBM. If everything is working, you should have an authenticating datapower.
Troubleshooting Techniques
This section details how to troubleshoot problems. Frequently, the system will either have case problems, bind issues, or certificate issues. Packet captures and dissection are necessary in order to properly diagnose issues.
Here are some tools that can help with LDAP issues:
· Wireshark: For processing packet captures, or capturing on the ‘server’ end. Datapower can also capture, but not process, packets.
· Enable CLI packet capture on datapower:
packet-capture-advanced all temporary:///captureLDAP.pcap -1 10000 9000 “port 389”
no packet-capture-advanced all temporary:///captureLDAP.pcapng
The second line is not to be typed in until debug is done. Packet captures can be retrieved by going to the file manager, and downloading them and dissecting with datapower.
· Format check XML with Notepad++ and XML module: this enables XML syntax checking and helps with errors in XML.
· CASE matters! It can vary widely depending on which version of windows, or LDAP server you are using. ANY errors in case will prevent things from working
· Separate configuration from Secure LDAP config phases. This document does not detail how to enable secure LDAP, but only describes a basic LDAP configuration. LDAP configuration with ssl requires advanced knowledge of certs/ssl and is beyond the scope of this document.
· Finally, if help beyond this is required, CALL IBM. They helped me configure this.
· LDAP Browser: Jexplorer works nicely. Good way to test passwords/uids. Also helps find CN’s etc.
U Use Putty for ssh connections or even better use SecureCRT!
YTThis document in ‘word’ format:
Word Document of this posting
This describes how to configure SSSD to authenticate with a Windows Server
using id_provider=ldap
.
It is recommended to use the AD provider when connecting to an AD server,
for performance and ease of use reasons. Please see Integrating with a Windows server using the AD provider for
a reference. There are two reasons where you might still want to use the
LDAP provider, though. One is if you are using a very old SSSD version,
the other reason is if you cannot or do not want join your GNU/Linux clients
to the AD domain.
Windows Server Setup¶
The domain to be configured is ad.example.com
using realm
AD.EXAMPLE.COM
, the Windows server is server.ad.example.com
, and the
client host where SSSD is running is client.ad.example.com
. Reboot
Windows during installation and setup when prompted and complete the
needed steps as Administrator.
Operating System Installation¶
- Boot from the Windows installation media
- Install Windows Server using the hostname
server.ad.example.com
- Make sure
server.ad.example.com
is in DNS
Domain Configuration¶
- In
Server Manager
add theActive Directory Domain Services
role - Create a new domain named
ad.example.com
- If you want to use POSIX attributes such as
uidNumber
inServer
add the
ManagerIdentity Management for UNIX
Role Service for
Active Directory Domain Services
, use the domain name
for the NIS domain name
Enabling LDAP Searches¶
In order to allow SSSD to do LDAP searches for user information in AD
SSSD must be configured to bind with SASL/GSSAPI or DN/password. GSSAPI
is recommended for security reasons. However, using GSSAPI probably
mean you join the computer to the domain — at that point, it probably
makes sense to use the AD provider instead.
Using SASL/GSSAPI Binds for LDAP Searches¶
Create the service keytab for the host running SSSD on AD. Either do
this with Samba, or using Windows. Samba is recommended.
Creating Service Keytab with Samba¶
On the GNU/Linux client with properly configured /etc/krb5.conf
(see
below) and suitable /etc/samba/smb.conf
:
[global] workgroup = EXAMPLE client signing = yes client use spnego = yes kerberos method = secrets and keytab log file = /var/log/samba/%m.log password server = AD.EXAMPLE.COM realm = EXAMPLE.COM security = ads
net ads join -U Administrator
- Or do
kinit Administrator
first and use-k
instead of-U Administrator
- Additional principals can be created later with
net ads keytab add
if needed.
You don’t need a Domain Administrator account to do this, you just need an
account with sufficient rights to join a machine to the domain. This is a
notable advantage of this approach over generating the keytab directly on
the AD controller. If you’re using NFS you may want to specify a different
createupn argument here. This does not cause any problems for sssd. This
would be done using:
# net ads join createupn="nfs/client.ad.example.com@AD.EXAMPLE.COM" -U Administrator
Creating Service Keytab on AD¶
Do not do this step if you’ve already created a keytab using Samba.
On the Windows server:
-
Open
Users & Computers
snap-in — Create a newComputer
object
namedclient
(i.e., the name of the host running SSSD) -
On the command prompt:
# setspn -A host/client.ad.example.com@AD.EXAMPLE.COM client # setspn -L client # ktpass /princ host/client.ad.example.com@AD.EXAMPLE.COM /out client-host.keytab /crypto all /ptype KRB5_NT_PRINCIPAL -desonly /mapuser AD\\client$ /pass \*
-
This sets the machine account password and UPN for the principal
-
If you create additional keytabs for the host add
-setpass -setupn
for
the above command to prevent resetting the machine password (thus changing
kvno) and to prevent overwriting the UPN -
Transfer the keytab created in a secure manner to the client as
/etc/krb5.keytab
and make sure its permissions are correct:# chown root:root /etc/krb5.keytab # chmod 0600 /etc/krb5.keytab # restorecon /etc/krb5.keytab
See the GNU/Linux Client Setup
section for verifying the keytab file and
the example sssd.conf below for the needed SSSD configuration.
Using DN/Password Binds for LDAP Searches¶
This method allows you to use SSSD against AD without joining the domain. Not
generally recommended but see the example sssd.conf below.
Adding a Group¶
- Open
Administrative Tools
->Active Directory Users and Computers
- Browse to
ad.example.com
, then toUsers
- Right click on
Users
andCreate a New Group
namedunixusers
- Double click on the
unixusers
group then switch to theUNIX
tab
Attributes - Select the NIS Domain created earlier
- Set the
GID
as appropriate
Adding a User¶
- Open
Administrative Tools
->Active Directory Users and Computers
- Browse to
ad.example.com
, then toUsers
- Right click on
Users
andCreate a New User
namedaduser
- Make sure
User must change password at next logon
andAccount is
are unchecked
disabled - Double click on the
aduser
group then switch to theUNIX
tab
Attributes - Select the NIS Domain created earlier
- Set the
UID
as appropriate - Set the
Login Shell
to/bin/bash
- Set the
Home Directory
to/home/aduser
- Set
Primary Group Name/GID
tounixusers
GNU/Linux Client Setup¶
- Install
sssd
package on the GNU/Linux client machine - Make configuration changes to the files below
- Start the
sssd
service
/etc/krb5.conf¶
Make the following changes to your krb5.conf
:
[logging] default = FILE:/var/log/krb5libs.log [libdefaults] default_realm = AD.EXAMPLE.COM dns_lookup_realm = true dns_lookup_kdc = true ticket_lifetime = 24h renew_lifetime = 7d rdns = false forwardable = yes # You may also want either of: # allow_weak_crypto = true # default_tkt_enctypes = arcfour-hmac [realms] # Define only if DNS lookups are not working # AD.EXAMPLE.COM = { # kdc = server.ad.example.com # admin_server = server.ad.example.com # } [domain_realm] # Define only if DNS lookups are not working # .ad.example.com = AD.EXAMPLE.COM # ad.example.com = AD.EXAMPLE.COM
Make sure kinit aduser@AD.EXAMPLE.COM
works properly. Add the
Windows server IP/hostname to /etc/hosts
only if needed.
If using SASL/GSSAPI to bind to AD also test that the keytab is working
properly:
# klist -ke # kinit -k CLIENT$@AD.EXAMPLE.COM
If you generated your keytab with a different createupn argument, it’s
possible this won’t work and the following works instead. This is absolutely
fine as far as sssd is concerned, and you can instead generate a ticket
for the UPN you have created:
# kinit -k -t /etc/krb5.keytab nfs/client.ad.example.com@AD.EXAMPLE.COM
Now using this credential you’ve just created try fetching data from the
server with ldapsearch
(in case of issues make sure
/etc/openldap/ldap.conf
does not contain any unwanted settings):
# /usr/bin/ldapsearch -H ldap://server.ad.example.com/ -Y GSSAPI -N -b "dc=ad,dc=example,dc=com" "(&(objectClass=user)(sAMAccountName=aduser))"
By using the credential from the keytab, you’ve verified that this credential
has sufficient rights to retrieve user information.
After both kinit
and ldapsearch
work properly proceed to actual
SSSD configuration.
/etc/sssd/sssd.conf¶
Example sssd.conf
configuration, additional options can be added as
needed:
[sssd] domains = ad.example.com services = nss, pam [nss] [pam] [domain/ad.example.com] # Unless you know you need referrals, turn them off ldap_referrals = false # Uncomment if you need offline logins # cache_credentials = true enumerate = false id_provider = ldap auth_provider = krb5 chpass_provider = krb5 access_provider = ldap # Uncomment if service discovery is not working #ldap_uri = ldap://server.ad.example.com/ # Comment out if not using SASL/GSSAPI to bind ldap_sasl_mech = GSSAPI # Uncomment and adjust if the default principal host/fqdn@REALM is not available #ldap_sasl_authid = nfs/client.ad.example.com@AD.EXAMPLE.COM # Define these only if anonymous binds are not allowed and no keytab is available # Enabling use_start_tls is very important, otherwise the bind password is transmitted # over the network in the clear #ldap_id_use_start_tls = True #ldap_default_bind_dn = CN=binduser,OU=user accounts,DC=ad,DC=example,DC=com #ldap_default_authtok_type = password #ldap_default_authtok = bindpass ldap_schema = rfc2307bis ldap_user_search_base = ou=user accounts,dc=ad,dc=example,dc=com ldap_user_object_class = user ldap_user_home_directory = unixHomeDirectory ldap_user_principal = userPrincipalName ldap_group_search_base = ou=groups,dc=ad,dc=example,dc=com ldap_group_object_class = group ldap_access_order = expire ldap_account_expire_policy = ad ldap_force_upper_case_realm = true # Uncomment if dns discovery of your AD servers isn't working. #krb5_server = server.ad.example.com krb5_realm = AD.EXAMPLE.COM # Probably required with sssd 1.8.x and newer krb5_canonicalize = false # Perhaps you need to redirect to certain attributes? # ldap_user_object_class = user # ldap_user_name = sAMAccountName # ldap_user_uid_number = msSFU30UidNumber # ldap_user_gid_number = msSFU30GidNumber # ldap_user_gecos = displayName # ldap_user_home_directory = msSFU30HomeDirectory # ldap_user_shell = msSFU30LoginShell # ldap_user_principal = userPrincipalName # ldap_group_object_class = group # ldap_group_name = cn # ldap_group_gid_number = msSFU30GidNumber
NSS/PAM Configuration¶
Depending on your distribution you have different options how to enable SSSD.
Fedora/RHEL¶
Use authconfig
to enable SSSD, install oddjob-mkhomedir
to make sure
home directory creation works with SELinux:
# authconfig --enablesssd --enablesssdauth --enablemkhomedir --update
Debian/Ubuntu¶
Install libnss-sss
and libpam-sss
to have SSSD added as
NSS/PAM provider in /etc/nsswitch.conf
and /etc/pam.d/common-*
configuration files. Add pam_mkhomedir.so
to PAM session configuration
manually. Restart SSSD after these changes.
Configure NSS/PAM manually¶
Manual configuration can be done with the following changes. The PAM
example file paths are from Debian/Ubuntu in Fedora/RHEL corresponding
manual configuration should be done in /etc/pam.d/system-auth
and
/etc/pam.d/password-auth
.
/etc/nsswitch.conf¶
More maps will be available later (see at least tickets #359 and #901).
You don’t have to copy the file as below, but please make sure sss
is present on the lines as below:
# # /etc/nsswitch.conf # passwd: files sss shadow: files sss group: files sss hosts: files dns bootparams: files ethers: files netmasks: files networks: files protocols: files rpc: files services: files sss netgroup: files sss publickey: files automount: files sss aliases: files
/etc/pam.d/common-auth¶
Right after the pam_unix.so
line, add:
auth sufficient pam_sss.so use_first_pass
/etc/pam.d/common-account¶
Right after the pam_unix.so
line, add:
account [default=bad success=ok user_unknown=ignore] pam_sss.so
/etc/pam.d/common-password¶
Right after the pam_unix.so
line, add:
password sufficient pam_sss.so use_authtok
/etc/pam.d/common-session¶
Just before the pam_unix.so
line, add:
session optional pam_mkhomedir.so
Right after the pam_unix.so
line, add:
session optional pam_sss.so
Understanding Kerberos & Active Directory¶
It is important to understand that (unlike GNU/Linux MIT based KDC) Active
Directory based KDC divides Kerberos principals into two groups:
- User Principals — usually equals to the sAMAccountname attribute of
the object in AD. In short, User Principal is entitled to obtain TGT
(ticket granting ticket). User Principals could be hence used to
generate a TGT viakinit -k <principalname>
- Service Principals — represents which Kerberized service can be
used on the computer in question. Service principals CANNOT be
used to obtain a TGT -> cannot be used to grant an access to Active
Directory controller for example.
Each user object in Active Directory (understand that a computer object
in AD is de-facto user object as well) can have:
- maximum of 2 User Principal Names (UPN). One is pre-defined by its
sAMAccountName
LDAP attribute (mentioned above, for computer
objects it has a form of<hostname>$
) and second by its
UserPrincipalName
string attribute - many Service Principal Names (typically one for each Kerberized
service we want to enable on the computer) defined by the
ServicePrincipalName
(SPN) list attribute. The attributes can be
seen/set using the ADSIedit snap-in for example.
Optional Final Test¶
You may have made iterative changes to your setup while learning about
SSSD. To make sure that your setup actually works, and you’re not relying
on cached credentials, or cached LDAP information, you may want to clear
out the local cache. Obviously this will erase local credentials, and all
cached user information, so you should only do this for testing, and while
on the network with network access to the AD servers:
service sssd stop; rm -f /var/lib/sss/db/*; service sssd start
If all looks well on your system after this, you know that sssd is able
to use the kerberos and ldap services you’ve configured.
LDAP and why it is used
Lightweight Directory Access Process (LDAP) serves as a user authentication mechanism tailored for directory services. This protocol is commonly employed by applications to retrieve resource data such as user profiles and various system elements. LDAP enables the identification of a client’s attributes with servers such as Active Directory, OpenLDAP, and Open DJ. Introduced in 1993, LDAP version 3 has been the Internet standard for directory services since 1997. LDAP configuration is important for security hardening and compliance.
LDAP is frequently used as a centralized repository for storing user credentials, such as usernames and passwords. This centralized storage mechanism enables multiple applications and services to interface with the LDAP server for user authentication purposes.
By leveraging LDAP, organizations can establish a unified authentication system, streamlining access management across various platforms and services. This approach enhances security by centralizing user credentials management, simplifying administrative tasks, and ensuring consistent authentication procedures across the network. Additionally, LDAP’s flexibility allows for seamless integration with diverse systems, contributing to its widespread adoption as a fundamental component of identity management solutions.
What is LDAP authentication (LDAP simple bind)?
The most common method for authenticating LDAP clients is through simple bind authentication. In this process, the client either binds anonymously, using an empty bind Distinguished Name (DN) or provides a DN along with a password. The Directory Proxy Server binds to a data source to verify these credentials and authenticate the client.
LDAP authentication can be illustrated effectively with an example involving an employee attempting to access their organization’s network. Since the most common action performed in such scenarios is logging into the network, the predominant function utilized is ‘read’. The LDAP protocol is specifically engineered to efficiently manage a large volume of read operations like these.
There are two main components in the LDAP authentication mechanism:
- LDAP Directory Server Setup – where the employee profile details are stored (username and password in this case).
- LDAP client – the employee’s computer. It should have an LDAP client option setup. If the employee is going to authenticate through a web application, then the server hosting the application must be configured with an LDAP client.
LDAP authentication process has two stages:
- Identifying the user’s directory attribute – The user entry is identified by its DN, which is the path to its details. In the first stage of the authentication, the user must obtain its DN and password. The user name usually requested in the login form is used to find the user’s DN. A function called DN Resolution takes the user name (or email in some cases) and runs a search to find all the user entries to find his DN. Once the correct user DN is resolved, the next step is to validate the user’s password.
- Password validation – The user’s password is checked by a command called ‘bind’. The LDAP directory server receives a request to authenticate the user bypassing its DN and password. If the credentials are correct, the directory server returns success, and the connection is established.
2 Security settings that do not have any impact on LDAP simple bind are:
- Ensure ‘Network security: LDAP client signing requirements’ is set to ‘Negotiate signing’ or higher
- Ensure ‘Domain controller: LDAP server signing requirements’ is set to ‘Require signing’
What is LDAP channel binding?
Security setting ‘Ensure ‘Domain controller: LDAP server channel binding token requirements’ is set to ‘Always’ discusses channel binding.
This configuration option determines whether the LDAP server (Domain Controller) mandates the verification of Channel Binding Tokens (CBT) included in LDAP bind requests transmitted over SSL/TLS, commonly referred to as LDAPS.
The recommended state for this setting is: Always.
Mandating Channel Binding Tokens (CBT) serves as a preventive measure against attackers attempting to exploit captured authentication credentials (such as OAuth tokens or session identifiers) by deterring their reuse in separate TLS sessions. Additionally, this practice strengthens defenses against “man-in-the-middle” attacks when employing LDAP authentication over SSL/TLS (LDAPS).
When first deploying this setting, you may initially want to only set it to the alternate setting of When supported (instead of Always) on all Domain Controllers. This alternate, interim setting enables support for LDAP client channel binding but does not require it. Then set one DC that is not currently being targeted by LDAP clients to Always, and test each of the critical LDAP clients against that DC (and remediating as necessary), before deploying Always to the rest of the DCs.
By automated configuration hardening of this setting organizations can guarantee that the desired configurations are uniformly applied across all Domain Controllers. It eliminates the potential for human error that may occur with manual configuration, reducing the risk of misconfigurations that could compromise security. Automated configuration hardening enables organizations to swiftly implement changes and updates across their infrastructure, enhancing overall agility and responsiveness. Automated testing mechanisms can be integrated to validate configurations and assess their impact on critical LDAP clients, ensuring that any necessary adjustments are promptly identified and remediated.
Harden Cipher Suites for Robust TLS/SSL Encryption
What is LDAP signing?
LDAP Signing is a security feature that ensures the integrity of communications between LDAP clients and domain controllers. LDAP signing is a feature of the Simple Authentication and Security Layer (SASL). SASL provides several mechanisms to increase the security of an LDAP connection, including user authentication, anti-tampering (message signing), and confidentiality (encryption). SASL is a communication layer that operates within LDAP on the default AD data ports (TCP port 389 and TCP port 3268)
Domain Controller: LDAP Server Signing Requirements
How to Enable LDAP Signing Configuration
For enabling LDAP signing in the server and the client you can either use Group Policy Object (GPO) or a registry key.
How to configure a server LDAP signing GPO:
- Go to ‘Default Domain Controller Policy’ > ‘Computer Configuration’ > ‘Policies’ > ‘Windows Settings’ > ‘Security Settings’ > ‘Local Policies’, and then select ‘Security Options’.
- Right-click ‘Domain controller: LDAP server signing requirements’, and select Properties.
- Enable ‘Define this policy setting’, select ‘Require signing’ in the ‘Define this policy setting list’, and then select OK.
How to configure LDAP Singing at a client’s local computer policy:
- Go to ‘Local Computer Policy’ > ‘Computer Configuration’ > ‘Policies’ > ‘Windows Settings’ > ‘Security Settings’ > ‘Local Policies’ and select ‘Security Options’.
- Right-click ‘Network security: LDAP client signing requirements’ and select ‘Properties’.
- In the dialog box, select ‘Require signing’ in the list, and then select OK.
How to configure a server LDAP signing using a registry key:
Note! We recommend backing up your registry before pushing any changes, as mistakes can have devastating results.
You need to create an LDAPServerIntegrity registry entry of the REG_DWORD type under the following registry subkey:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<InstanceName>\Parameters
*instance name= the name of the AD LDS you want to configure.
LDAP Vulnerabilities Affecting Domain Controllers
CVE-2023-21676: A security flaw within LDAP poses a risk of enabling a remote code execution by an authenticated attacker on Windows Server installations functioning as Domain Controllers. This vulnerability presents a low-complexity attack vector accessible over the network.
CVE-2023-21557: is a vulnerability in LDAP that could allow an unauthenticated attacker could send a specially crafted request to a vulnerable LDAP server. Successful exploitation could result in bypassing a buffer length check which could be leveraged to achieve information leak.
To improve the security of your directory server you can implement several configuration rules. These rules are best practices, and recommended also by the Center for Internet Security (CIS):
- Ensure ‘LDAP server signing requirements’ is set to ‘Require signing’. – This policy setting will make clients use data signing.
- Ensure ‘Extended Protection for LDAP Authentication (Domain Controllers only)’ is set to ‘Enabled: Enabled (recommended: always) – this setting controls LDAP authentication over SSL/TLS.
- Ensure ‘Network security: LDAP client signing requirements’ is set to ‘Negotiate signing’ or higher – this policy sets the level of signing requested by the client who issues LDAP request.
LDAP Configuration and the MITRE ATT&CK Framework
Configuring LDAP for optimal security is a recommended best practice by the MITRE ATT&CK framework stating few techniques and mitigations
T1087– Adversaries may attempt to get a listing of domain accounts. This information can help adversaries determine which domain accounts exist to aid in follow-on behavior such as targeting specific accounts which possess particular privileges. Commands such as net user /domain and net group /domain of the Net utility, dscacheutil -q groupon macOS, and ldapsearch on Linux can list domain users and groups. PowerShell cmdlets including Get-ADUser and Get-ADGroupMember may enumerate members of Active Directory groups.
T1482- MITRE ATTACK framework clasify LDAP configuration settings under the technique- Domain Trust Discovery– , stating that Adversaries may attempt to gather information on domain trust relationships that may be used to identify lateral movement opportunities in Windows multi-domain/forest environments. Domain trusts can be enumerated using LDAP. the Windows utility Nltest is known to be used by adversaries to enumerate domain trusts.
Changing configurations as part of server hardening is critical for security but may cause severe damage. We strongly recommend using automation to prevent any possible outages in your production. The alternative for automation is using manual tools (GPO for example), testing the impact of the change in a lab environment, and hoping you covered all possible scenarios. By using automation in hardening, you will not need to test. You will get a full impact report to help you make the best decision for both security and operations.