LDAP
Spinnaker supports using LDAP for authentication.
Lightweight Directory Access Protocol (LDAP) is a standard way many organizations maintain user credentials and group memberships. Spinnaker uses the standard “bind” approach for user authentication. This is a fancy way of saying that Gate uses your username and password to login to the LDAP server, and if the connection is successful, you’re considered authenticated. Note that there is a fair bit of crossover between the authorization and authentication settings.
Notes
Make sure you’re using Halyard 1.20 or later as that adds the ability to set the manager user/password settings. You can use a prior version, but you will need to use a
gate-local.yml
to define the manager properties instead of being able to use Halyard.If the manager Domain Name (DN) is NOT set, all searches attempt to use the user currently logging in. You’ll often see errors in the log files tied to “bind failures” and LDAP error codes.
Escaping things like spaces is handled by the library. You do NOT need to use LDAP escape codes to handle spaces.
We highly suggest the use of SSL for the LDAP connection (
ldaps://...
). Otherwise, user passwords are passed in clear text over the wire.Ports commonly used or referenced:
- 636 - LDAP with SSL (
ldaps
) - 389 - LDAP
- 3268 - Active Directory (AD) Global Directory non-SSL port
- 3269 - AD Global Directory SSL port
- 636 - LDAP with SSL (
When a port is not specified, ldap://host/whatever
implies port 389 by default. ldaps://host/whatever
uses port 636 by default.
How to determine the “User DN”
- Extract the Root DN from the
url
(ldaps://my.server/a/b/c
→a/b/c
)- If
com.netflix.spinnaker.fiat.roles.ldap.LdapUserRolesProvider
log level is at debug, you should seeRoot DN: <the actual root DN extracted>
- If
- If
--user-search-filter
is provided:- Search LDAP:
- Search in
--user-search-base
OR the root (would bea/b/c
in this example) if user-search-base is not set. - Filtered by
--user-search-filter="(d={0})"
whereuid=<the username as typed in>
, such asjdoe
. - Start at the rootDn and use sub tree searches
- Search in
- Return root DN computed + the found user DN
- Search LDAP:
- If
user-search-filter
is not provided:- Calculate the user DN using
user-dn-pattern
. In the case below, the userjdoe
would have a full DN ofuid=jdoe,ou=users,dc=mydomain,dc=net
. - Return root DN computed + user DN
- Calculate the user DN using
For example, given the following parameters:
- Root DN is
dc=my-organization,dc=com
user-dn-pattern
isuid={0},ou=users
- User with the id
jdoe
The full, unique DN would be uid=jdoe,ou=users,dc=my-organization,dc=com
.
When jdoe
is trying to log in, this full user DN is constructed and passed to the LDAP server with their password. The server hashes the password and compares it to its own hashed version. If successful, the bind (aka connection) is successful and Gate creates a session.
Testing with ldapsearch
In the above example, you could test with:
//Search using manager DN, manager password on url with base of "X"
# When: --user-search-filter=(uid={0}) --user-search-base=DC=USERS,OU=Y,O=io
ldapsearch -D "MANAGER_DN" -w 'MANAGER_PASSWORD' -H ldaps://1.2.3.4 -x -b "DC=USERS,OU=Y,O=io" "(UID=USERNAME)"
Without a user-search-base
//Search using manager DN, manager password on url with base of "X"
# When: --user-search-filter=(uid={0})
ldapsearch -D "MANAGER_DN" -w 'MANAGER_PASSWORD' -H ldaps://1.2.3.4 -x "(UID=USERNAME})"
Without a user-search-filter
//Search using manager DN, manager password on url with base of "X"
# When: --user-dn-pattern=(uid={0},ou=users)
ldapsearch -D "MANAGER_DN" -w 'MANAGER_PASSWORD' -H ldaps://1.2.3.4/OU=Y,O=io -x "(CN=USERNAME,OU=users,OU=Y,O=IO))"
Configure LDAP using Halyard
You can use hal config
to enable and configure LDAP. Here’s an example:
hal config security authn ldap edit --user-dn-pattern="uid={0},ou=users" \
--url=ldaps://ldap.my-organization.com:636/dc=my-organization,dc=com
hal config security authn ldap enable
You can also use --user-search-base
(optional) and --user-search-filter
if the simpler --user-dn-pattern
does not match what your organization uses for userDn
. We don’t explore this use case here, but you can read up more on LDAP search filters here .
Active Directory
We recommend NOT using the
--user-dn-pattern
argument for AD. The following issue has been reported in an issue ticket:“userDnPattern should remain unset - AD groups store user DNs in the memberOf attribute; finding DNs from sAMAccountNames is easily doable but not with a simple, single-level pattern. The DN contains the the CN, and that can’t really be constructed without sub searches. userSearchFilter takes precedence if there’s no user-dn-pattern set.”
Here’s the raw settings that will eventually be there in Gate as an example.
ldap:
enabled: true
url: ldaps://somethingsomething:686/ou=users,ou=company,o=com
userSearchFilter: (&(objectClass=user)(sAMAccountName={0}))
managerDn: CN=SVC_LDAP_USER_RO,OU=service-users,OU=company,O=com
managerPassword: super-secret-password
The managerUser will then find a user in your ou=users,ou=company,o=com directory via a subtree search. You should be able to set the user-search-base parameter vs. including it on the URL to have it specified separately.
Last, here are the Halyard commands to configure these:
hal config security authn ldap enable
hal config security authn ldap edit \
--manager-dn 'CN=blah,OU=blah,OU=blah,O=blah' \
--user-search-filter '(&(objectClass=user)(sAMAccountName={0}))' \
--url ldaps://blah:686/searchbase
## This one will prompt you for the password don't set it on the command
hal config security authn ldap edit --manager-password
Next steps
Now that you’ve authenticated the user, proceed to setting up their authorization .
Troubleshooting
Review the general authentication workflow .
Use an Incognito window . Close all Incognito windows between attempts.
I’m getting the “Bad Credentials” exception mentioned above, but my username and password is correct!
Ensure that the fully qualified user DN is correct. Confirm with your LDAP administrator how your organization’s LDAP directory is structured.
Last modified May 4, 2021: rest of migration (700781a)