You sometimes need to stop a user from authenticating without deleting the LDAP entry. 389 Directory Server supports manual inactivation through nsAccountLock, role-based suspension, and automatic denial after a configured period of inactivity through the Account Policy plug-in.
In this guide, we'll check account state, lock and unlock individual users, apply nsAccountLock with LDIF, lock roles that contain multiple users, record lastLoginTime, configure accountInactivityLimit, test inactivity denial, reactivate blocked accounts, and audit disabled or inactive users. Failed-password lockout belongs in account lockout, not here.
Before you start:
- TLS, STARTTLS, and LDAPS — trust the Directory Server CA before password binds over LDAPS or STARTTLS
- Manage users and groups — create users and review basic
dsidm accountcommands - Roles vs groups — managed roles and
nsRoleDNmembership - Class of Service — subtree-wide virtual attribute assignment
- Global, subtree, and user password policy — password expiration and grace logins
- Account lockout — failed-bind counters and recovery
lastLoginTime tracking, and Account Policy inactivity. It does not configure failed-password lockout, password-expiration grace logins, or pass-through authentication.
Tested on: Rocky Linux 10.2; 389 Directory Server 3.2.0.
Account-lifecycle tests use instance ldap2 with suffix dc=lab,dc=example,dc=com, LDAP port 1389, and LDAPS port 1636. That keeps Account Policy plug-in work away from other chapters exercising ldap1. Test users are uid=lockout-test, uid=inactive-test, and uid=user1 under ou=people,dc=lab,dc=example,dc=com. Bind Directory Manager with -D "cn=Directory Manager" -y /root/ldap2-dm.pw when dsidm or dsconf prompts for a password on this instance.
Set the lab CA before LDAPS user binds and ldapsearch checks:
export LDAPTLS_CACERT=/root/ldap2-ca-chain.pemThat variable tells OpenLDAP clients which CA certificate to trust for LDAPS on this instance.
Use ldaps://127.0.0.1:1636 for authentication tests in this guide. Do not copy plaintext ldap:// password binds to remote or production servers. The TLS chapter shows how to install a trusted CA; on this disposable loopback lab you may need LDAPTLS_REQCERT=allow when the instance uses a self-signed certificate.
Record the Account Policy baseline
Save the plug-in state before you change anything:
dsconf -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 plugin account-policy statusThat command reports whether the Account Policy plug-in is enabled on ldap2.
Sample output on the tested host before the exercise:
Plugin 'Account Policy Plugin' is disabledI also read the plug-in configuration entry so I know whether login timestamps are being recorded:
dsconf -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 plugin account-policy config-entry show "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config"Sample output excerpt:
alwaysrecordlogin: noAlso confirm whether lab users, the SuspendedUsers role, policy entries, or CoS definitions from an earlier run already exist before you create duplicates.
Understand the different account lock states
| State | Trigger | Main mechanism |
|---|---|---|
| Manual account inactivation | Administrator action | nsAccountLock |
| Role-based inactivation | Administrator locks a role | Role membership |
| Failed-password lockout | Repeated incorrect passwords | Password-policy counters |
| Inactivity lockout | No successful login within a time limit | Account Policy plug-in |
| Account-age expiration | Time since account creation | Account Policy plug-in |
| Password-expiry inactivation | Time after password expiration | Account Policy plug-in |
Manual inactivation writes or manages nsAccountLock. When that attribute is true, Directory Server rejects the user's bind. The Account Policy plug-in instead evaluates configured time attributes during authentication and denies access when the limit has been exceeded, often without setting nsAccountLock.
Failed-password lockout is documented in account lockout.
Check the current status of a user account
Inspect the administrative account state with dsidm account entry-status:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 account entry-status "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com"Sample output:
Entry DN: uid=lockout-test,ou=people,dc=lab,dc=example,dc=com
Entry Creation Date: 20260716064103Z (2026-07-16 06:41:03)
Entry Modification Date: 20260716064103Z (2026-07-16 06:41:03)
Entry State: activatedRequest verbose details when your build supports them:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 account entry-status -V "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com"On the tested 389 DS 3.2.0 build, -V returned the same fields as the default view. Use it when your documentation or automation expects the verbose flag.
Also inspect the operational attributes that drive automatic policies with ldapsearch:
ldapsearch -LLL -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "uid=inactive-test,ou=people,dc=lab,dc=example,dc=com" -s base nsAccountLock lastLoginTime createTimestamp passwordExpirationTime acctPolicySubentryBefore Account Policy configuration, lastLoginTime and acctPolicySubentry may be absent. An automatically inactive account may not have nsAccountLock: true; the plug-in can reject the bind based on its time-state attributes alone.
Disable one user account manually
Lock the account with dsidm account lock:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 account lock "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com"Sample output:
Entry uid=lockout-test,ou=people,dc=lab,dc=example,dc=com is lockedVerify the state:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 account entry-status "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com"Sample output excerpt:
Entry State: directly locked through nsAccountLockTest the user bind over LDAPS:
ldapwhoami -x -H ldaps://127.0.0.1:1636 -D "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com" -y /root/lockout-test-ldap2.pwSample output:
ldap_bind: Server is unwilling to perform (53)
additional info: Account inactivated. Contact system administrator.The LDAP entry remains present and searchable according to its ACIs, but the user cannot authenticate.
Disable an account with nsAccountLock
The equivalent LDIF method sets the attribute directly:
cat > /tmp/disable-lockout-test.ldif <<'EOF'
dn: uid=lockout-test,ou=people,dc=lab,dc=example,dc=com
changetype: modify
replace: nsAccountLock
nsAccountLock: true
EOFApply the inactivation change with ldapmodify:
ldapmodify -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -f /tmp/disable-lockout-test.ldifA successful modify exits silently. Confirm the attribute:
ldapsearch -LLL -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com" -s base nsAccountLockSample output:
dn: uid=lockout-test,ou=people,dc=lab,dc=example,dc=com
nsAccountLock: truedsidm account lock is preferable for routine administration because it provides a clearer account-management interface. Use direct LDAP modification when you automate through LDIF, repair account state, or work with tooling that does not provide dsidm.
Reactivate a manually disabled account
Unlock the account:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 account unlock "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com"Sample output:
Entry uid=lockout-test,ou=people,dc=lab,dc=example,dc=com is unlocked
The entry was directly lockedVerify status:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 account entry-status "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com"Sample output excerpt:
Entry State: activatedTest authentication over LDAPS:
ldapwhoami -x -H ldaps://127.0.0.1:1636 -D "uid=lockout-test,ou=people,dc=lab,dc=example,dc=com" -y /root/lockout-test-ldap2.pwSample output:
dn: uid=lockout-test,ou=people,dc=lab,dc=example,dc=comIf you manage the attribute directly, set nsAccountLock to false or remove it, then confirm that no other policy still denies the bind.
Disable multiple users through a role
Use a managed role when several users should be disabled together. Role creation and membership belong in roles vs groups. I'll create the role and assign members before locking it:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "dc=lab,dc=example,dc=com" ldap2 role create-managed --cn SuspendedUsersSample output:
Successfully created SuspendedUsersI write an LDIF that adds the suspended role to user1:
cat > /tmp/user1-suspended-role.ldif <<'EOF'
dn: uid=user1,ou=people,dc=lab,dc=example,dc=com
changetype: modify
add: nsRoleDN
nsRoleDN: cn=SuspendedUsers,dc=lab,dc=example,dc=com
EOFI apply the role membership change with ldapmodify:
ldapmodify -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -f /tmp/user1-suspended-role.ldifnsRoleDN with ACIs before using a managed role for account inactivation. If users can remove the locked role from their own entries, an already authenticated user may remove nsRoleDN and reactivate themselves. Filtered and nested roles require equivalent protection for the attributes that determine role membership.
This lab uses:
cn=SuspendedUsers,dc=lab,dc=example,dc=comLock the role:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 role lock "cn=SuspendedUsers,dc=lab,dc=example,dc=com"Sample output:
Entry cn=SuspendedUsers,dc=lab,dc=example,dc=com is lockedMember binds are rejected while the role is locked:
ldap_bind: Server is unwilling to perform (53)
additional info: Account inactivated. Contact system administrator.Reactivate members by unlocking the role:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 role unlock "cn=SuspendedUsers,dc=lab,dc=example,dc=com"Locking a role causes Directory Server to compute nsAccountLock: true for its members. It does not need to write a physical nsAccountLock value to every member entry. The role definition itself remains the membership definition. Verify the exact role DN returned by dsidm role list or an explicit ldapsearch before you lock it.
Find manually disabled accounts
Search for explicit manual locks:
ldapsearch -LLL -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "dc=lab,dc=example,dc=com" "(nsAccountLock=true)" dn uid nsAccountLockThis search finds nsAccountLock=true entries, including some role-related states. It does not necessarily identify users denied only by Account Policy time evaluation. When a bind still fails, check dsidm account entry-status and the operational attributes on the user entry.
Understand the Account Policy plug-in
Automatic inactivity follows this flow:
User attempts an LDAP bind
|
v
Account Policy plug-in reads the configured state attribute
|
v
Current time - stored timestamp is compared with accountInactivityLimit
|
+--> Within limit: authentication continues
|
└--> Limit exceeded: authentication is denied| Attribute or setting | Scope | Purpose |
|---|---|---|
stateAttrName |
Instance plug-in configuration | Primary timestamp checked by the plug-in |
altStateAttrName |
Instance plug-in configuration | Alternate timestamp |
specAttrName |
Instance plug-in configuration | Identifies policy entries |
limitAttrName |
Instance plug-in configuration | Names the limit attribute in policy entries |
checkAllStateAttrs |
Instance plug-in configuration | Evaluates both state attributes when enabled |
alwaysRecordLogin |
Instance plug-in configuration | Updates the login timestamp after successful authentication |
acctPolicySubentry |
Per user or virtual through CoS | Refers the account to a policy entry |
accountInactivityLimit |
Per policy entry | Maximum permitted age in seconds |
lastLoginTime |
Per user operational attribute | Records the last successful login |
createTimestamp |
Per entry operational attribute | Records when the LDAP entry was created |
passwordExpirationTime |
Per user operational attribute | Records password expiry |
stateAttrName, altStateAttrName, specAttrName, and limitAttrName belong to the single Account Policy plug-in configuration entry for the Directory Server instance. Individual policy entries supply different accountInactivityLimit values, but they do not independently select different state attributes. Reconfiguring the plug-in from lastLoginTime to createTimestamp changes evaluation for every account that uses that plug-in configuration.
With alwaysRecordLogin=yes, the plug-in writes lastLoginTime directly to successfully authenticating user entries. This setting is required when acctPolicySubentry is supplied virtually through CoS or roles rather than stored physically on each user. In a high-authentication environment, these timestamp updates add directory writes and potentially replication traffic, so load-test before enabling login tracking broadly.
Track the last successful login without disabling accounts
Enable the plug-in:
dsconf -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 plugin account-policy enableSample output:
Enabled plugin 'Account Policy Plugin'Configure login tracking on the plug-in configuration entry:
dsconf -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 plugin account-policy config-entry set "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config" --always-record-login yes --state-attr lastLoginTimeSample output:
Successfully changed the cn=config,cn=Account Policy Plugin,cn=plugins,cn=configRestart the instance so the plug-in reads the updated configuration:
dsctl ldap2 restartAuthenticate as a test user over LDAPS, then read lastLoginTime:
ldapwhoami -x -H ldaps://127.0.0.1:1636 -D "uid=inactive-test,ou=people,dc=lab,dc=example,dc=com" -w 'InactiveTest!2026'I read lastLoginTime on the same account to confirm the plug-in recorded the successful bind:
ldapsearch -LLL -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "uid=inactive-test,ou=people,dc=lab,dc=example,dc=com" -s base lastLoginTimeSample output:
dn: uid=inactive-test,ou=people,dc=lab,dc=example,dc=com
lastLoginTime: 20260716070904ZDirectory Server can use the Account Policy plug-in solely to maintain lastLoginTime; no expiration or inactivity policy is required for this tracking-only mode.
Automatically inactivate accounts after inactivity
Configure the plug-in for inactivity evaluation. The important --alt-state-attr 1.1 setting disables the createTimestamp fallback so existing users without lastLoginTime are not treated as already inactive:
dsconf -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 plugin account-policy config-entry set "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config" --always-record-login yes --state-attr lastLoginTime --alt-state-attr 1.1 --spec-attr acctPolicySubentry --limit-attr accountInactivityLimitRestart the instance after changing plug-in configuration:
dsctl ldap2 restartCreate the account inactivity policy
cat > /tmp/account-inactivity-policy.ldif <<'EOF'
dn: cn=Account Inactivity Policy,dc=lab,dc=example,dc=com
objectClass: top
objectClass: ldapsubentry
objectClass: extensibleObject
objectClass: accountpolicy
cn: Account Inactivity Policy
accountInactivityLimit: 1814400
EOFI add the policy entry to the directory with ldapadd:
ldapadd -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -f /tmp/account-inactivity-policy.ldif1814400 seconds equals 21 days.
Apply the policy with CoS
The continuous lab assigns the inactivity policy through CoS to users below ou=people,dc=lab,dc=example,dc=com. Skip the alternative direct-assignment section below. The test intentionally ages only inactive-test; other users in the same scope remain active while their timestamps are within the limit. Do not also add a direct acctPolicySubentry value to the same user.
Create the template and pointer definition:
cat > /tmp/account-policy-cos.ldif <<'EOF'
dn: cn=AccountInactivityTemplate,dc=lab,dc=example,dc=com
objectClass: top
objectClass: ldapsubentry
objectClass: extensibleObject
objectClass: cosTemplate
cn: AccountInactivityTemplate
acctPolicySubentry: cn=Account Inactivity Policy,dc=lab,dc=example,dc=com
dn: cn=AccountInactivityDefinition,ou=people,dc=lab,dc=example,dc=com
objectClass: top
objectClass: ldapsubentry
objectClass: cosSuperDefinition
objectClass: cosPointerDefinition
cn: AccountInactivityDefinition
cosTemplateDn: cn=AccountInactivityTemplate,dc=lab,dc=example,dc=com
cosAttribute: acctPolicySubentry default operational-default
EOFI add the CoS template and pointer definition so users under ou=people inherit the policy:
ldapadd -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -f /tmp/account-policy-cos.ldifVerify the virtual association on the test user:
ldapsearch -LLL -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "uid=inactive-test,ou=people,dc=lab,dc=example,dc=com" -s base acctPolicySubentrySample output:
dn: uid=inactive-test,ou=people,dc=lab,dc=example,dc=com
acctPolicySubentry: cn=Account Inactivity Policy,dc=lab,dc=example,dc=comCoS allows one policy to apply across a subtree without adding a physical acctPolicySubentry value to every user. Keep the detailed CoS architecture in Class of Service.
Alternative: Assign the policy directly instead of using CoS
Direct assignment is an alternative to CoS. Skip the entire CoS template and definition procedure when using this method. The example below assigns the policy physically to inactive-test.
cat > /tmp/assign-inactivity-policy.ldif <<'EOF'
dn: uid=inactive-test,ou=people,dc=lab,dc=example,dc=com
changetype: modify
add: acctPolicySubentry
acctPolicySubentry: cn=Account Inactivity Policy,dc=lab,dc=example,dc=com
EOFI apply the direct policy assignment with ldapmodify:
ldapmodify -x \
-H ldaps://127.0.0.1:1636 \
-D "cn=Directory Manager" \
-y /root/ldap2-dm.pw \
-f /tmp/assign-inactivity-policy.ldifUse direct acctPolicySubentry references for small exceptions or different limits per user. Use CoS when the same policy applies to many users in a subtree. Do not run both procedures in the same lab run.
Test automatic account inactivity
Set an old lastLoginTime on the test account so the policy limit is already exceeded:
cat > /tmp/old-last-login.ldif <<'EOF'
dn: uid=inactive-test,ou=people,dc=lab,dc=example,dc=com
changetype: modify
replace: lastLoginTime
lastLoginTime: 20260101000000Z
EOFI apply the aged timestamp so the inactivity policy should deny the next bind:
ldapmodify -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -f /tmp/old-last-login.ldifAttempt a bind over LDAPS:
ldapwhoami -x -H ldaps://127.0.0.1:1636 -D "uid=inactive-test,ou=people,dc=lab,dc=example,dc=com" -w 'InactiveTest!2026'Sample output:
ldap_bind: Constraint violation (19)
additional info: Account inactivity limit exceeded. Contact system administrator to reset.Confirm that another recently active user within the same CoS scope can still authenticate. This proves that policy assignment alone does not block an account whose timestamp is within the configured limit.
Reactivate an automatically inactivated account
Try dsidm account unlock first:
dsidm -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 account unlock "uid=inactive-test,ou=people,dc=lab,dc=example,dc=com"On the tested 389 DS 3.2.0 build, that command returned Error: float() argument must be a string or a real number, not 'NoneType' and did not restore access while lastLoginTime still exceeded the policy limit. For lastLoginTime inactivity, set the timestamp to the current time:
I capture the current UTC timestamp for the recovery LDIF:
CURRENT_LOGIN_TIME=$(date -u +%Y%m%d%H%M%SZ)I write an LDIF that replaces lastLoginTime with that value:
cat > /tmp/reactivate-last-login.ldif <<EOF
dn: uid=inactive-test,ou=people,dc=lab,dc=example,dc=com
changetype: modify
replace: lastLoginTime
lastLoginTime: ${CURRENT_LOGIN_TIME}
EOFI apply the timestamp reset with ldapmodify:
ldapmodify -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -f /tmp/reactivate-last-login.ldifVerify a successful bind:
ldapwhoami -x -H ldaps://127.0.0.1:1636 -D "uid=inactive-test,ou=people,dc=lab,dc=example,dc=com" -w 'InactiveTest!2026'Sample output:
dn: uid=inactive-test,ou=people,dc=lab,dc=example,dc=comRecovery depends on which state denied the bind:
| Policy state | Correct recovery |
|---|---|
lastLoginTime |
Set it to the current time or use a working dsidm account unlock |
createTimestamp |
Do not rewrite the creation timestamp; remove or change the policy assignment, increase the limit, or recreate the disposable account |
passwordExpirationTime |
Reset the password or correct the password-expiration policy so Directory Server recalculates password state |
Manual nsAccountLock |
Run dsidm account unlock or remove or set nsAccountLock to false |
| Locked role | Unlock the role |
Simply clearing nsAccountLock does not correct an expired lastLoginTime.
Automatically inactivate accounts based on creation time
Account-age expiration uses createTimestamp as the state attribute:
state attribute: createTimestamp
limit attribute: accountInactivityLimitTypical use case:
Disable contractor or temporary accounts 60 days after creation.The Account Policy plug-in has one active state-attribute configuration per Directory Server instance. Changing stateAttrName from lastLoginTime to createTimestamp changes how all assigned Account Policy entries on that instance are evaluated. Do not present creation-age expiration as an additional independent policy running beside the existing inactivity configuration. Use a separate test instance, or deliberately replace the current plug-in configuration after assessing every assigned account.
Automatically inactivate accounts after password expiration
Password-expiry inactivation evaluates passwordExpirationTime through the Account Policy plug-in. This replaces the lastLoginTime-based evaluation configuration rather than applying only to one policy entry:
dsconf -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 plugin account-policy config-entry set "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config" --always-record-login yes --always-record-login-attr lastLoginTime --state-attr non_existent_attribute --alt-state-attr passwordExpirationTime --spec-attr acctPolicySubentry --limit-attr accountInactivityLimitRestart the instance after changing plug-in configuration:
dsctl ldap2 restart| Behaviour | Ordinary password-policy expiration | Account Policy password-expiry mode |
|---|---|---|
| Expired password | May allow bind and password change within grace rules | Can fully deny authentication after the configured interval |
| Recovery | User self-service or administrator password reset | Reset the password or correct password-expiration policy so Directory Server recalculates passwordExpirationTime |
Configure password expiration in global, subtree, and user password policy. Use Account Policy enforcement when you need to block authentication completely after the post-expiration interval.
Combine login inactivity and password-expiration checks
checkAllStateAttrs evaluates both lastLoginTime and passwordExpirationTime when enabled, but the alternate attribute must actually be passwordExpirationTime. Do not leave the --alt-state-attr 1.1 inactivity configuration in place and assume that enabling checkAllStateAttrs alone adds password-expiration checks.
dsconf -D "cn=Directory Manager" -y /root/ldap2-dm.pw ldap2 plugin account-policy config-entry set "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config" --always-record-login yes --state-attr lastLoginTime --alt-state-attr passwordExpirationTime --spec-attr acctPolicySubentry --limit-attr accountInactivityLimit --check-all-state-attrs yesRestart the instance so the plug-in loads the updated configuration:
dsctl ldap2 restartcheckAllStateAttrs with createTimestamp; Red Hat explicitly warns that this can unexpectedly lock accounts.
Audit disabled and inactive accounts
Use separate checks for different mechanisms.
Manually disabled accounts:
ldapsearch -LLL -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "dc=lab,dc=example,dc=com" "(nsAccountLock=true)" dn uidAccounts with a recorded login time:
ldapsearch -LLL -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "dc=lab,dc=example,dc=com" "(lastLoginTime=*)" dn uid lastLoginTimeAccounts assigned an inactivity policy:
ldapsearch -LLL -x -H ldaps://127.0.0.1:1636 -D "cn=Directory Manager" -y /root/ldap2-dm.pw -b "dc=lab,dc=example,dc=com" "(acctPolicySubentry=*)" dn uid acctPolicySubentry lastLoginTimeFor CoS-generated operational values, request operational attributes explicitly where required. Determining whether an account is currently past its inactivity limit may require comparing the timestamp with the policy's accountInactivityLimit.
Troubleshoot account disabling and inactivity
| Symptom | Likely cause | Fix |
|---|---|---|
dsidm account lock succeeds but the user can still bind |
Wrong user DN, wrong server, replication lag, or authentication passed to another provider | Confirm the DN, target instance, nsAccountLock, and bind destination |
| Account is active but authentication still fails | Failed-password lockout, password expiration, Account Policy plug-in, invalid password, disabled role, or pass-through authentication | Inspect operational attributes and the effective policy for each mechanism |
| Existing users are disabled immediately after enabling inactivity | Users lacked lastLoginTime and the plug-in fell back to an old createTimestamp |
Use --alt-state-attr 1.1 until users record lastLoginTime, or introduce tracking before enforcement |
lastLoginTime is not updated |
Plug-in disabled, alwaysRecordLogin off, wrong configuration entry, or missing restart |
Enable the plug-in, verify config-entry show, restart the instance, and bind again |
| Policy applies to the wrong users | Incorrect acctPolicySubentry, CoS template, or CoS definition |
Verify the policy DN, subtree placement, and operational CoS values |
account unlock does not restore access |
Account is past its inactivity limit rather than manually locked | Use the recovery table for the evaluated state attribute |
| User becomes inactive again immediately | lastLoginTime, createTimestamp, or passwordExpirationTime still exceeds the policy limit |
Set a current timestamp, adjust the policy, or change the plug-in configuration before retesting |
| User removes a locked role and regains access | nsRoleDN or role-determining attributes are writable by the user |
Restrict self-modification of nsRoleDN and filtered-role attributes with ACIs |
Security and operational recommendations
- Disable accounts instead of deleting them when access may need to be restored.
- Keep manual disabling and automatic inactivity clearly distinguishable in runbooks.
- Use
dsidmfor routine lock, unlock, and status operations when your instance supports it. - Protect
nsRoleDNand role-determining attributes before using roles for inactivation. - Use CoS for one policy applied to a large subtree; use direct
acctPolicySubentryvalues for small exceptions. - Introduce
lastLoginTimetracking before enforcing an inactivity limit. - Avoid using an old
createTimestampas an accidental fallback. - Test with a small pilot subtree before applying inactivity globally.
- Audit service accounts before applying interactive-user inactivity rules.
- Use short inactivity limits only in test environments.
- Document which timestamp caused every automatic account denial.
- Bind over LDAPS or STARTTLS for password authentication in production.
What's Next
- Account lockout — failed-password lockout is separate from manual disable
- Delegated administration — delegate unlock rights to helpdesk groups
- ACI examples — protect
nsRoleDNand policy attributes
References
- Red Hat Directory Server 13 — Managing access control (Account Policy sections 2.6–2.8)
- Red Hat Directory Server 13 — User management and authentication (manual inactivation)
- Red Hat Directory Server 13 — Configuration and schema reference (Account Policy plug-in)
- 389 Directory Server — Account Policy Plugin Software Design Specification
- 389 Directory Server — Account Policy Plugin Inactivity and Expiration Design
Summary
- Use
dsidm account lockto disable a user manually anddsidm account unlockto restore manual locks. - Use roles when several users should be disabled together, and protect
nsRoleDNwith ACIs. - Remember that Account Policy state attributes are configured once per instance.
- Enable the Account Policy plug-in to record
lastLoginTime, then applyaccountInactivityLimitthrough CoS or direct assignment. - Test automatic inactivity with a dedicated account and an old
lastLoginTime. - Reactivate accounts using the recovery path that matches the evaluated state attribute.
- Keep failed-password lockout in account lockout.

