ALD Pro is a Russian directory service built on FreeIPA: user and group data is stored in 389 Directory Server, and authentication is handled by MIT Kerberos. Stronghold connects to ALD Pro as a regular LDAP server and, optionally, as a Kerberos key distribution center.

Goal

After the setup:

  • ALD Pro domain users log in to Stronghold with their domain accounts through the ldap method.
  • Stronghold permissions are assigned by membership in ALD Pro groups.
  • Users with a valid Kerberos ticket log in without entering a password (the kerberos method, optional; Stronghold EE only).
  • Stronghold rotates passwords of domain service accounts through the LDAP secrets engine.

LDAP login flow with ALD Pro

Prerequisites

Before you start, prepare the following:

  • A deployed ALD Pro domain, for example ald.example.com with domain controllers dc01.ald.example.com and dc02.ald.example.com.
  • Network access from Stronghold nodes to the domain controllers on port 636 (LDAPS), and on port 88 for Kerberos.
  • The domain root CA certificate. On any domain-joined host it is located at /etc/ipa/ca.crt.
  • A Stronghold token with permissions to configure auth methods, secrets engines, policies, and Identity.
  • The ldapsearch and jq utilities on the administrator’s workstation.

The examples use the base DN dc=ald,dc=example,dc=com. Replace it with your domain’s DN.

Step 1. Prepare a service account for searches

Stronghold searches for users and groups as a separate read-only account. Do not use domain administrator accounts for this.

  1. Create a system account (sysaccount) in ALD Pro. In FreeIPA, this is done with an LDIF file as cn=Directory Manager:

    dn: uid=stronghold-bind,cn=sysaccounts,cn=etc,dc=ald,dc=example,dc=com
    changetype: add
    objectclass: account
    objectclass: simplesecurityobject
    uid: stronghold-bind
    userPassword: <BIND_PASSWORD>
    passwordExpirationTime: 20380119031407Z
    nsIdleTimeout: 0
    ldapmodify -x -H ldaps://dc01.ald.example.com -D "cn=Directory Manager" -W -f stronghold-bind.ldif

    <BIND_PASSWORD> is the password of the stronghold-bind account.

    If your installation does not use system accounts, create a regular domain user with a non-expiring password and use its DN uid=stronghold-bind,cn=users,cn=accounts,dc=ald,dc=example,dc=com.

  2. Copy the domain CA certificate to the workstation you use to configure Stronghold:

    scp admin@dc01.ald.example.com:/etc/ipa/ca.crt ./ald-ca.crt
  3. Check that the account can see users and groups over LDAPS:

    LDAPTLS_CACERT=./ald-ca.crt ldapsearch -x -H ldaps://dc01.ald.example.com -D "uid=stronghold-bind,cn=sysaccounts,cn=etc,dc=ald,dc=example,dc=com" -W -b "cn=users,cn=accounts,dc=ald,dc=example,dc=com" "(uid=jsmith)" uid memberOf

    The output must contain the user’s uid attribute and the list of groups in 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 to ALD Pro. Groups are searched by the member attribute of group objects:

    d8 stronghold write auth/ldap/config url="ldaps://dc01.ald.example.com:636,ldaps://dc02.ald.example.com:636" certificate=@ald-ca.crt insecure_tls=false starttls=false binddn="uid=stronghold-bind,cn=sysaccounts,cn=etc,dc=ald,dc=example,dc=com" bindpass='<BIND_PASSWORD>'
    d8 stronghold write auth/ldap/config userdn="cn=users,cn=accounts,dc=ald,dc=example,dc=com" userattr="uid" groupdn="cn=groups,cn=accounts,dc=ald,dc=example,dc=com" groupfilter="(&(objectClass=groupOfNames)(member={{.UserDN}}))" groupattr="cn" username_as_alias=true

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

    Key parameters:

    ParameterValue for ALD Pro
    urlComma-separated list of domain controllers. Stronghold tries them in order if a connection fails.
    certificateDomain CA certificate from /etc/ipa/ca.crt.
    insecure_tlsfalse: server certificate verification is mandatory.
    starttlsfalse for ldaps://. If you use ldap:// on port 389, set it to true.
    userdn, userattrFreeIPA user container and the uid login attribute.
    groupdn, groupfilter, groupattrGroup container and a filter that finds groups where the user is listed in member. The group name is taken from cn.
    username_as_aliasThe entity alias name matches the domain username.

    Alternatively, read groups from the memberOf attribute of the user object:

    d8 stronghold write auth/ldap/config groupdn="cn=users,cn=accounts,dc=ald,dc=example,dc=com" groupfilter="(&(objectClass=person)(uid={{.Username}}))" groupattr="memberOf"

    In this case the group list also includes roles, privileges, HBAC rules, and other objects the user is a member of. The member variant returns only groups from cn=groups,cn=accounts.

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

  3. Check login with a domain account:

    d8 stronghold login -method=ldap username=jsmith

    Specify the username without the domain. If no policies are assigned to groups yet, the token gets only the default policy.

Step 3. Map ALD Pro groups to policies

Use one of the two approaches. Do not assign policies to the same group in both ways: it complicates auditing permissions.

With LDAP method groups

The group name in Stronghold must match the cn of the group in ALD Pro:

d8 stronghold write auth/ldap/groups/stronghold-admins policies=admin
d8 stronghold write auth/ldap/groups/developers policies=dev-read

This approach suits setups where Stronghold uses only one login method.

With external Identity groups

External groups let you assign policies once and use them for several login methods (for example, ldap and kerberos), and also apply MFA rules to the group.

  1. Create an external group and save its ID:

    GROUP_ID=$(d8 stronghold write -field=id identity/group name="ald-stronghold-admins" type="external" policies="admin")
  2. Get the accessor of the ldap/ method:

    LDAP_ACCESSOR=$(d8 stronghold auth list -format=json | jq -r '."ldap/".accessor')
  3. Create a group alias. The name value must match the group name in ALD Pro:

    d8 stronghold write identity/group-alias name="stronghold-admins" mount_accessor="$LDAP_ACCESSOR" canonical_id="$GROUP_ID"

Stronghold updates the entity’s membership in external groups on every login and token renewal. Group changes in ALD Pro apply to issued tokens when they are renewed.

Step 4. Configure Kerberos single sign-on (optional)

The kerberos method accepts an SPNEGO header from a client with a valid Kerberos ticket and gets the user’s groups from LDAP. The method parameters are described in the auth methods API reference.

The kerberos auth method is available only in Stronghold EE. Test the scenario on a staging environment before rolling it out.

  1. Create a service principal in ALD Pro for the Stronghold HTTP address and get a keytab:

    kinit admin
    ipa service-add HTTP/stronghold.ald.example.com
    ipa-getkeytab -s dc01.ald.example.com -p HTTP/stronghold.ald.example.com -k stronghold.keytab
  2. Enable the method. The method reads the SPNEGO token from the Authorization request header and returns the WWW-Authenticate header, so allow both headers for the mount:

    d8 stronghold auth enable -passthrough-request-headers=Authorization -allowed-response-headers=www-authenticate kerberos

    If the method is already enabled, set the same headers for the existing mount:

    d8 stronghold auth tune -passthrough-request-headers=Authorization -allowed-response-headers=www-authenticate kerberos/
  3. Pass the Base64-encoded keytab:

    base64 -w0 stronghold.keytab > stronghold.keytab.b64
    d8 stronghold write auth/kerberos/config keytab=@stronghold.keytab.b64 service_account="HTTP/stronghold.ald.example.com" add_group_aliases=true

    With add_group_aliases=true, the method passes the user’s LDAP groups to Identity as group aliases, so external Identity groups work for the kerberos method. Each write to auth/kerberos/config replaces the whole configuration: pass keytab, service_account and add_group_aliases in one command.

    The service_account value is the name of the principal in the keytab, in the same form as ipa-getkeytab -p (HTTP/<FQDN>). Do not set remove_instance_name=true for this principal: the option removes the instance (the part after /) from the principal names in the keytab and from the user name at login, and the entry HTTP/stronghold.ald.example.com would stop matching.

  4. Configure group lookup in LDAP. The auth/kerberos/config/ldap parameters are the same as those of auth/ldap/config:

    d8 stronghold write auth/kerberos/config/ldap url="ldaps://dc01.ald.example.com:636,ldaps://dc02.ald.example.com:636" certificate=@ald-ca.crt insecure_tls=false binddn="uid=stronghold-bind,cn=sysaccounts,cn=etc,dc=ald,dc=example,dc=com" bindpass='<BIND_PASSWORD>'
    d8 stronghold write auth/kerberos/config/ldap userdn="cn=users,cn=accounts,dc=ald,dc=example,dc=com" userattr="uid" groupdn="cn=groups,cn=accounts,dc=ald,dc=example,dc=com" groupfilter="(&(objectClass=groupOfNames)(member={{.UserDN}}))" groupattr="cn"

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

  5. Assign policies to groups:

    d8 stronghold write auth/kerberos/groups/stronghold-admins policies=admin

    If you use external Identity groups, create a second alias for each group with the kerberos/ method accessor instead of this step. The method creates group aliases only if add_group_aliases=true is set in auth/kerberos/config.

  6. Check login from a domain-joined machine:

    kinit jsmith
    curl --negotiate -u : -X POST https://stronghold.ald.example.com/v1/auth/kerberos/login

    The request must be POST: a GET to this path always returns 401 Negotiate. The response contains a Stronghold token in the auth.client_token field.

    Login with d8 stronghold login -method=kerberos and in the web UI is not supported: no login handler is registered for kerberos in the CLI, and the web UI can only enable the method and has no login form for it. Use the API, for example with curl.

Step 5. Rotate service account passwords

The LDAP secrets engine changes passwords of existing domain accounts and hands them out to applications. For ALD Pro, use the openldap schema: the password is stored in the userPassword attribute.

In FreeIPA, a password set by another account is marked as expired by default, and the user must change it at the next login. The account that Stronghold uses to change passwords must be listed in passSyncManagersDNs of the cn=ipa_pwd_extop,cn=plugins,cn=config entry; otherwise rotated passwords expire immediately.

  1. Create a stronghold-rotator account in ALD Pro with permission to change passwords of the target service accounts.

  2. Enable the secrets engine and configure the connection:

    d8 stronghold secrets enable ldap
    d8 stronghold write ldap/config url="ldaps://dc01.ald.example.com:636" certificate=@ald-ca.crt insecure_tls=false binddn="uid=stronghold-rotator,cn=users,cn=accounts,dc=ald,dc=example,dc=com" bindpass='<ROTATOR_PASSWORD>' userdn="cn=users,cn=accounts,dc=ald,dc=example,dc=com" userattr="uid" schema=openldap

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

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

    d8 stronghold write -f ldap/rotate-root

    The new password cannot be retrieved after rotation. If Stronghold loses access to ALD Pro, reset the password with domain tools.

Static role

A static role binds one domain account to a name in Stronghold and changes its password on a schedule:

d8 stronghold write ldap/static-role/svc-gitlab dn="uid=svc-gitlab,cn=users,cn=accounts,dc=ald,dc=example,dc=com" username="svc-gitlab" rotation_period="24h"

The application reads the current password:

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

The ttl field in the response shows the time until the next rotation. To rotate immediately:

d8 stronghold write -f ldap/rotate-role/svc-gitlab

Account library (check-out)

A library lends an account from a shared pool for a limited time and changes its password after it is returned. It suits shared accounts used by on-call engineers.

d8 stronghold write ldap/library/ops-shared service_account_names="svc-ops1,svc-ops2" ttl=1h max_ttl=4h

The service_account_names values are matched against the attribute set in userattr (here uid), so specify the uid value, not the full DN.

Check-out, status, and check-in:

d8 stronghold write -f ldap/library/ops-shared/check-out
d8 stronghold read ldap/library/ops-shared/status
d8 stronghold write ldap/library/ops-shared/check-in service_account_names="svc-ops1"

Example policy for the application and on-call engineers:

path "ldap/static-cred/svc-gitlab" {
  capabilities = ["read"]
}

path "ldap/library/ops-shared/check-out" {
  capabilities = ["update"]
}

path "ldap/library/ops-shared/check-in" {
  capabilities = ["update"]
}

path "ldap/library/ops-shared/status" {
  capabilities = ["read"]
}

Verification

Check the result:

  1. Log in as a user from the stronghold-admins group and check the token policies:

    d8 stronghold login -method=ldap username=jsmith
    d8 stronghold token lookup

    policies (or identity_policies when you use external groups) must contain the admin policy.

  2. Make sure a user outside the group gets only default.

  3. Read the static role password and run ldapsearch -D "uid=svc-gitlab,..." with it: the bind must succeed.

Troubleshooting

Common errors and how to fix them:

SymptomLikely causeWhat to do
ldap operation failed: failed to bind as user or Invalid Credentials at loginWrong user password, wrong userdn/userattr, or wrong binddn passwordCheck search and bind with the ldapsearch command from step 1. Check that the user is in cn=users,cn=accounts.
x509: certificate signed by unknown authorityThe domain CA is missing from certificate, or a different certificate is specifiedPass /etc/ipa/ca.crt in certificate. Do not enable insecure_tls.
x509: certificate is valid for ..., not ...The address in url does not match the name in the controller certificateUse the controllers’ FQDNs, not IP addresses.
Login succeeds but the token gets only defaultgroupfilter finds no groups, or group names do not match auth/ldap/groups/<GROUP_NAME> and the aliasesRun ldapsearch with the groupfilter filter, substituting the user DN. Compare the groups’ cn with the names in Stronghold.
A user cannot log in after several failed attempts, although the password is correctStronghold lockout was triggered (lockout_threshold, 5 attempts for 15 minutes by default), or the account is locked in ALD ProWait for lockout_duration to pass or check the account status in ALD Pro. Lockout settings are described in LDAP auth method.
An application cannot log in with the password from static-credALD Pro marked the password as expiredAdd the Stronghold account to passSyncManagersDNs and run rotate-role.

Cleanup

Disable the auth methods and the secrets engine:

d8 stronghold auth disable kerberos
d8 stronghold auth disable ldap
d8 stronghold secrets disable ldap

Disabling the secrets engine does not change account passwords. Change them with ALD Pro tools, then delete the stronghold-bind and stronghold-rotator accounts.

Additional resources