Stronghold connects to Active Directory (AD) over LDAP: it verifies domain user passwords, resolves their groups, including nested ones, and assigns policies by group. The LDAP secrets engine with the ad schema changes passwords of domain service accounts.

Goal

After the setup:

  • AD users log in to Stronghold with their domain accounts through the ldap method over LDAPS.
  • Stronghold permissions are assigned by AD group membership, including nested groups.
  • Stronghold rotates AD service account passwords and hands them out to applications.

Prerequisites

Before you start, prepare the following:

  • An AD domain, for example corp.example.com with domain controllers dc01.corp.example.com and dc02.corp.example.com.
  • LDAPS (port 636) enabled on the domain controllers with a certificate issued by the corporate CA.
  • The root CA certificate in PEM (Base-64) format, for example corp-ca.pem.
  • Network access from Stronghold nodes to the domain controllers on port 636.
  • A Stronghold token with permissions to configure auth methods, secrets engines, policies, and Identity.

The examples assume users are in OU=Users,DC=corp,DC=example,DC=com, groups in OU=Groups,DC=corp,DC=example,DC=com, and service accounts in OU=Service Accounts,DC=corp,DC=example,DC=com. Replace the DNs with your own.

Step 1. Prepare AD accounts

Create the accounts that Stronghold uses in AD and check the directory search.

  1. Create a svc-stronghold-bind account for searching users and groups. Regular domain user permissions to read the directory are enough. Set a non-expiring password.

  2. If you plan to rotate passwords, create a svc-stronghold-rotator account and delegate the Reset password permission on the service account OU to it. Do not grant it domain administrator rights.

  3. Check search over LDAPS:

    LDAPTLS_CACERT=./corp-ca.pem ldapsearch -x -H ldaps://dc01.corp.example.com -D "svc-stronghold-bind@corp.example.com" -W -b "OU=Users,DC=corp,DC=example,DC=com" "(sAMAccountName=jsmith)" sAMAccountName userPrincipalName memberOf

Step 2. Configure the LDAP method

Enable the ldap method, connect it to the domain controllers and check login.

  1. Enable the auth method:

    d8 stronghold auth enable ldap
  2. Configure the connection. Users log in with sAMAccountName; groups are resolved including nesting through the LDAP_MATCHING_RULE_IN_CHAIN rule (OID 1.2.840.113556.1.4.1941):

    d8 stronghold write auth/ldap/config url="ldaps://dc01.corp.example.com:636,ldaps://dc02.corp.example.com:636" certificate=@corp-ca.pem insecure_tls=false starttls=false binddn="CN=svc-stronghold-bind,OU=Service Accounts,DC=corp,DC=example,DC=com" bindpass='<BIND_PASSWORD>'
    d8 stronghold write auth/ldap/config userdn="OU=Users,DC=corp,DC=example,DC=com" userattr="sAMAccountName" groupdn="OU=Groups,DC=corp,DC=example,DC=com" groupfilter="(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={{.UserDN}}))" groupattr="cn" username_as_alias=true

    <BIND_PASSWORD> is the password of the svc-stronghold-bind account. The first command configures the connection, the second one configures the user and group search.

    Key parameters:

    ParameterValue for AD
    urlComma-separated list of domain controllers, port 636.
    certificateCertificate of the root CA that issued the controller certificates.
    insecure_tls, starttlsfalse for ldaps://. When connecting via ldap:// on port 389, set starttls=true.
    userattrsAMAccountName: the short login name, for example jsmith.
    groupfilterFinds all groups the user belongs to directly or through nested groups.
    groupattrcn: the group name in Stronghold.

    Instead of groupfilter, you can set use_token_groups=true: Stronghold reads the user’s computed tokenGroups attribute, which contains all security groups, including nested ones.

  3. To find users by UPN (jsmith@corp.example.com), add the upndomain parameter to the configuration. Keep userattr=sAMAccountName:

    d8 stronghold write auth/ldap/config upndomain="corp.example.com"

    The user still enters the name without the domain, for example jsmith. Stronghold searches userdn as binddn for the object with userPrincipalName=jsmith@corp.example.com, binds with the DN of the found object and uses this DN in the group search.

    A write to auth/ldap/config changes only the parameters passed in the command. The other parameters keep their current values.

  4. Check login:

    d8 stronghold login -method=ldap username=jsmith

Step 3. Map AD groups to policies

For a single login method, LDAP method groups are enough. The group name must match the group’s cn in AD:

d8 stronghold write auth/ldap/groups/SG-Stronghold-Admins policies=admin
d8 stronghold write auth/ldap/groups/SG-Developers policies=dev-read

If AD groups are also used by other login methods, or you need to apply MFA to them, create external Identity groups:

GROUP_ID=$(d8 stronghold write -field=id identity/group name="ad-stronghold-admins" type="external" policies="admin")
LDAP_ACCESSOR=$(d8 stronghold auth list -format=json | jq -r '."ldap/".accessor')
d8 stronghold write identity/group-alias name="SG-Stronghold-Admins" mount_accessor="$LDAP_ACCESSOR" canonical_id="$GROUP_ID"

For details on external groups, see Identity.

Step 4. Rotate service account passwords

The LDAP secrets engine with the ad schema changes passwords of AD service accounts and hands them out to applications.

AD allows changing a password (the unicodePwd attribute) only over a secure connection. Use ldaps:// or starttls=true.

  1. Enable the secrets engine and configure it with the ad schema:

    d8 stronghold secrets enable ldap
    d8 stronghold write ldap/config url="ldaps://dc01.corp.example.com:636" certificate=@corp-ca.pem insecure_tls=false binddn="CN=svc-stronghold-rotator,OU=Service Accounts,DC=corp,DC=example,DC=com" bindpass='<ROTATOR_PASSWORD>' userdn="OU=Service Accounts,DC=corp,DC=example,DC=com" schema=ad

    <ROTATOR_PASSWORD> is the current password of the svc-stronghold-rotator account.

  2. Rotate the svc-stronghold-rotator password so that only Stronghold knows it:

    d8 stronghold write -f ldap/rotate-root
  3. Create a static role for the application service account:

    d8 stronghold write ldap/static-role/svc-jenkins dn="CN=svc-jenkins,OU=Service Accounts,DC=corp,DC=example,DC=com" username="svc-jenkins" rotation_period="24h"
  4. The application reads the current password:

    d8 stronghold read ldap/static-cred/svc-jenkins

For shared accounts lent to people for a limited time, use a library:

d8 stronghold write ldap/library/helpdesk service_account_names="svc-helpdesk1@corp.example.com,svc-helpdesk2@corp.example.com" ttl=2h max_ttl=8h
d8 stronghold write -f ldap/library/helpdesk/check-out
d8 stronghold write ldap/library/helpdesk/check-in service_account_names="svc-helpdesk1@corp.example.com"

The service_account_names values are matched against the attribute set in the userattr parameter; for the ad schema it defaults to userPrincipalName, so use the full name such as svc-helpdesk1@corp.example.com.

Access policies for static-cred and library are similar to those in Integration with ALD Pro.

Verification

Check the result:

  1. Log in as a user from a group nested in SG-Stronghold-Admins and check that the token got the admin policy:

    d8 stronghold login -method=ldap username=jsmith
    d8 stronghold token lookup
  2. Read the static role password and check the bind:

    LDAPTLS_CACERT=./corp-ca.pem ldapsearch -x -H ldaps://dc01.corp.example.com -D "svc-jenkins@corp.example.com" -w '<STATIC_CRED_PASSWORD>' -b "DC=corp,DC=example,DC=com" -s base

    <STATIC_CRED_PASSWORD> is the password value from the ldap/static-cred/svc-jenkins response.

Troubleshooting

On a bind error, AD returns code 49 and a subcode in the data field of the error message. The subcode indicates the cause.

SymptomLikely causeWhat to do
Invalid Credentials, data 52eWrong user password or bindpassCheck the password. After rotate-root, only Stronghold knows the binddn password.
data 525User not foundCheck userdn, userattr, or upndomain.
data 775The account is locked in ADUnlock it in AD. Also check the Stronghold lockout (lockout_threshold) described in LDAP auth method.
data 533, data 532, data 701The account is disabled, or the password or account has expiredEnable the account or change the password in AD.
x509: certificate signed by unknown authorityThe CA certificate is missing or wrongPass the corporate root CA certificate in certificate.
Login succeeds but group policies are missinggroupfilter finds no groups, or group names do not matchRun ldapsearch with the (member:1.2.840.113556.1.4.1941:=<USER_DN>) filter. Group names in Stronghold must match cn.
rotate-role returns Unwilling To Perform or Insufficient Access RightsNo secure connection, binddn lacks the Reset password permission, or the password does not meet the domain password policyUse LDAPS, check the delegation, and configure a Stronghold password policy with the password_policy parameter.

Cleanup

Disable the auth method and the secrets engine:

d8 stronghold auth disable ldap
d8 stronghold secrets disable ldap

Disabling the secrets engine does not change passwords. Change the service account passwords with AD tools.

Additional resources