Advanced 389 Directory Server ACI Targets and Bind Rules

Configure advanced 389 Directory Server ACIs with targetfilter, targattrfilters, target_from and target_to, userattr, groupdn filters, IP and DNS rules, SSF, time limits, and Boolean bind expressions.

Published

Updated

Read time 14 min read

Reviewed byDeepak Prasad

389 Directory Server advanced access control with targetfilter targetattrfilters userattr and IP bind rules on an LDAP directory tree

Advanced Access Control Instructions (ACIs) limit which entries, attributes, and values a bind identity may touch, and under which network, security, authentication, and time conditions. This guide builds on 389 Directory Server ACI examples. Read that article first for syntax, placement, rights, and basic userdn / groupdn rules.

Every ACI in this guide uses the same reference format:

text
(target rules)
(version 3.0;
 acl "Rule name";
 allow or deny (rights)
 bind rules;
)

Before you start:

IMPORTANT

This guide covers advanced target selection and conditional bind rules. It does not replace:

See the dedicated chapters linked from the body when you need those topics.

Tested on: Rocky Linux 10.2; 389 Directory Server 3.2.0.

Store lab bind passwords in a root-owned file instead of passing them on the command line, then set mode 600 with the chmod command:

bash
printf '%s\n' 'LabInfPassword123!' > /root/aci-lab-user.pw
chmod 600 /root/aci-lab-user.pw

Use -y /root/aci-lab-user.pw in the examples below. For uid=user2, use the password file from ACI examples (/root/user2.pw) when you already created it there.


Advanced ACI targets at a glance

Keyword Purpose
target Select entries or subtrees by DN
targetattr Select attributes affected by the rule
targetfilter Select entries matching an LDAP filter
targetattrfilters / targattrfilters Restrict attribute values during entry add, modify, replace, and delete operations
target_from Permitted source DN of a Modify DN operation
target_to Permitted destination DN of a Modify DN operation

Each target keyword may appear only once in a single ACI, but you can combine several different target keywords in one rule. Red Hat Directory Server documentation lists the keyword as targetattrfilters; the ACI string on the server uses targattrfilters in tested 389 DS 3.2 builds.

Do not use virtual attributes such as Class of Service values inside targetfilter, targattrfilters, or userattr rules. The server cannot evaluate them reliably for access control.


Target entries with DNs and wildcards

The target keyword selects entries by distinguished name. Place the ACI on the target entry or on an ancestor that contains it.

Exact entry:

text
(target="ldap:///uid=enguser1,ou=Engineering,ou=People,dc=example,dc=com")

Subtree under an organizational unit:

text
(target="ldap:///ou=Engineering,ou=People,dc=example,dc=com")

Wildcard in an RDN component:

text
(target="ldap:///uid=*,ou=Engineering,ou=People,dc=example,dc=com")

Target exclusion with !=:

text
(target!="ldap:///ou=Contractors,ou=People,dc=example,dc=com")

An exclusion matches every entry outside the named DN. That pattern grants access broadly and is difficult to audit. Red Hat recommends narrow allow rules instead of broad != targets. Wildcards are not supported inside the suffix portion of a target DN; (target="ldap:///dc=*.com") is invalid when your suffix is dc=example,dc=com.


Combine targetfilter and targetattr

Scenario: allow Engineering administrators to modify manager and jpegPhoto only on entries where departmentNumber=Engineering.

  • targetfilter selects entries.
  • targetattr limits affected attributes.
  • Both conditions must match.

I'll test the filter with ldapsearch before embedding it in an ACI. If directory data or structure changes, the filter may match a different entry set than you expect.

bash
ldapsearch -LLL -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -b "ou=people,dc=example,dc=com" "(departmentNumber=Engineering)" uid departmentNumber

Sample output:

output
dn: uid=enguser1,ou=Engineering,ou=People,dc=example,dc=com
uid: enguser1
departmentNumber: Engineering

The filter returns only Engineering entries. Use the same filter string in the ACI.

Add the rule on the People container:

ldif
dn: ou=People,dc=example,dc=com
changetype: modify
add: aci
aci: (targetfilter="(departmentNumber=Engineering)")(targetattr="manager || jpegPhoto")(version 3.0; acl "Engineering admins modify manager and jpegPhoto"; allow (read,search,compare,write) groupdn="ldap:///cn=engineering-admins,ou=Groups,dc=example,dc=com";)

Apply the LDIF as Directory Manager with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-targetfilter.ldif

Sample output:

output
modifying entry "ou=People,dc=example,dc=com"

Verify the grant as a member of cn=engineering-admins. The modify LDIF updates manager on the Engineering user:

ldif
dn: uid=enguser1,ou=Engineering,ou=People,dc=example,dc=com
changetype: modify
replace: manager
manager: uid=engadmin,ou=people,dc=example,dc=com

Apply the modify as engadmin to confirm the scoped write ACI works:

bash
ldapmodify -x -H ldap://localhost:389 -D "uid=engadmin,ou=people,dc=example,dc=com" -y /root/aci-lab-user.pw -f /tmp/engadmin-write-manager.ldif

Sample output:

output
modifying entry "uid=enguser1,ou=Engineering,ou=People,dc=example,dc=com"

engadmin can update manager on an Engineering entry. A write to an attribute outside targetattr is denied:

bash
ldapmodify -x -H ldap://localhost:389 -D "uid=engadmin,ou=people,dc=example,dc=com" -y /root/aci-lab-user.pw -f /tmp/engadmin-write-mail.ldif

Sample output:

output
ldap_modify: Insufficient access (50)
	additional info: Insufficient 'write' privilege to the 'mail' attribute of entry 'uid=enguser1,ou=engineering,ou=people,dc=example,dc=com'.

The same identity cannot change manager on a non-Engineering entry because the filter does not match:

bash
ldapmodify -x -H ldap://localhost:389 -D "uid=engadmin,ou=people,dc=example,dc=com" -y /root/aci-lab-user.pw -f /tmp/engadmin-write-user2.ldif

Sample output:

output
ldap_modify: Insufficient access (50)
	additional info: Insufficient 'write' privilege to the 'manager' attribute of entry 'uid=user2,ou=people,dc=example,dc=com'.

Restrict attribute values with targattrfilters

Value-based ACIs validate attribute values during entry add, modify, replace, and delete operations. They do not filter search results. Clients with read access still see stored values.

The example below uses userdn="ldap:///self" so each authenticated user may modify only their own entry. An ACI stored on ou=People,dc=example,dc=com with no target rule applies to that entry and all descendants; an explicit userdn identifies the caller but does not limit which entries under the subtree are affected.

ldif
dn: ou=People,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="telephoneNumber")(targattrfilters="add=telephoneNumber:(telephoneNumber=+1-555*)")(version 3.0; acl "Allow approved phone prefix only"; allow (write) userdn="ldap:///self";)

During entry creation, every applicable value must match the add= filter. During entry deletion, applicable values must match the del= filter. A replace operation must satisfy both filters because it deletes the old value and adds the new value.

Example: permit telephoneNumber values only when the number begins with +1-555.

ldif
dn: uid=user1,ou=people,dc=example,dc=com
changetype: modify
add: telephoneNumber
telephoneNumber: +1-555-0199

An approved value succeeds:

bash
ldapmodify -x -H ldap://localhost:389 -D "uid=user1,ou=people,dc=example,dc=com" -y /root/aci-lab-user.pw -f /tmp/user1-phone-good.ldif

Sample output:

output
modifying entry "uid=user1,ou=people,dc=example,dc=com"

A value outside the filter is rejected:

bash
ldapmodify -x -H ldap://localhost:389 -D "uid=user1,ou=people,dc=example,dc=com" -y /root/aci-lab-user.pw -f /tmp/user1-phone-bad.ldif

Sample output:

output
ldap_modify: Insufficient access (50)
	additional info: Insufficient 'write' privilege to the 'telephoneNumber' attribute of entry 'uid=user1,ou=people,dc=example,dc=com'.

Combine multiple attribute filters with && inside the add= or del= clause. For example, you can block a privileged role DN while allowing an approved phone prefix:

text
(targattrfilters="add=nsRoleDN:(!(nsRoleDN=cn=SuperAdmin,ou=People,dc=example,dc=com)) && telephoneNumber:(telephoneNumber=+1-555*)")

Use del= filters when you must control which values may be removed from multi-valued attributes. To restrict the rule to one entry instead of self-service across the subtree, add (target="ldap:///uid=user1,ou=people,dc=example,dc=com") and keep the explicit userdn for that account.


Control entry rename and move operations

Directory Server provides target_from and target_to to constrain Modify DN source and destination DNs. The actual changetype: modrdn LDIF records live in rename and move entries. This section focuses on the ACI shape.

Example intent: move accounts from ou=Contractors,ou=People,dc=example,dc=com into ou=People,dc=example,dc=com:

text
(target_from="ldap:///uid=*,ou=Contractors,ou=People,dc=example,dc=com")
(target_to="ldap:///ou=People,dc=example,dc=com")
(version 3.0; acl "Contractor move to People"; allow (moddn) userdn="ldap:///uid=contractor1,ou=Contractors,ou=People,dc=example,dc=com";)

target_from and target_to restrict the source and destination DNs of the Modify DN operation. The moddn right permits the rename or move within that scope, but it does not grant separate permission to add unrelated destination entries or delete unrelated source entries.

If target_from or target_to is omitted, the ACI matches any source or destination for that keyword. Narrow both sides when you delegate moves between specific organizational units.


Advanced user and group bind rules

Match one or more user DNs with userdn

List explicit DNs or use * wildcards in the RDN:

text
userdn="ldap:///uid=engadmin,ou=people,dc=example,dc=com"
userdn="ldap:///uid=*,ou=Engineering,ou=People,dc=example,dc=com"

Use userdn="ldap:///self" when the bind DN must equal the target entry. Use userdn="ldap:///all" for any authenticated bind (not anonymous).

Select users through an LDAP URL filter

Grant access when the bind user matches an LDAP URL filter. For example, you can target Human Resources staff identified by departmentNumber:

text
userdn="ldap:///ou=People,dc=example,dc=com??sub?(departmentNumber=HR)"

Test the filter with ldapsearch using the same base, scope, and filter before you deploy the ACI.

Match static or dynamic groups with groupdn

Match membership in a group entry:

text
groupdn="ldap:///cn=engineering-admins,ou=Groups,dc=example,dc=com"

Directory Server evaluates group membership using member, uniqueMember, memberUrl, and supported certificate attributes on the group entry. ACIs that reference groupdn must live on the same server instance as the group entry.

Filter form: the bind user must belong to at least one group the search returns.

text
groupdn="ldap:///ou=Groups,dc=example,dc=com??sub?(cn=*-admins)"

Configure relationship-based access with userattr

The userattr bind rule compares an attribute on the target entry with the bind identity.

Bind type Typical use
USERDN Attribute contains the permitted user's DN
GROUPDN Attribute names a group the bind user must belong to
ROLEDN Attribute or relationship identifies an authorized role
SELFDN User may manage a value containing their own DN
LDAPURL Attribute stores a filter that determines authorization

Manager-to-subordinate example: managers may update contact fields on entries whose manager attribute points to them.

ldif
dn: ou=Engineering,ou=People,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="description || mail || title")(version 3.0; acl "Managers update subordinate contact fields"; allow (read,search,compare,write) userattr="manager#USERDN";)

The manager bind succeeds on a subordinate entry:

bash
ldapmodify -x -H ldap://localhost:389 -D "uid=engadmin,ou=people,dc=example,dc=com" -y /root/aci-lab-user.pw -f /tmp/engadmin-title.ldif

Sample output:

output
modifying entry "uid=enguser1,ou=Engineering,ou=People,dc=example,dc=com"

A user who is not listed as manager on the target entry is denied for attributes outside their other ACIs:

bash
ldapmodify -x -H ldap://localhost:389 -D "uid=user2,ou=people,dc=example,dc=com" -y /root/user2.pw -f /tmp/user2-title.ldif

Sample output:

output
ldap_modify: Insufficient access (50)
	additional info: Insufficient 'write' privilege to the 'title' attribute of entry 'uid=enguser1,ou=engineering,ou=people,dc=example,dc=com'.

Inherited relationships use the parent keyword. For example, userattr="parent[0,1].manager#USERDN" applies the relationship rule to the targeted entry and its immediate children. Use parent[0,1,2] when the rule must also reach the second child level. Red Hat Directory Server supports levels 0 through 4; the listed numbers select exact inheritance levels, not a range. Keep inheritance levels minimal; deep inheritance is harder to audit than a dedicated group or role rule. Full self-service patterns belong in the self-service chapter, not here.


Restrict access by network location

IP address and range with ip

Grant or deny access based on the client IP address Directory Server observes. Red Hat documentation describes networks in CIDR notation, but the ACI expression uses an address prefix:

text
ip="127.0.0.1"
ip="192.0.2."
ip="2001:db8::"

The prefix 192.0.2. matches the 192.0.2.0/24 network; 2001:db8:: matches the 2001:db8::/64 network. Do not paste CIDR strings such as 192.0.2.0/24 into the ACI unless you have verified that form on your server version.

Combine ip with identity rules:

text
groupdn="ldap:///cn=ldap-readers,ou=Groups,dc=example,dc=com" and ip="127.0.0.1"

Load balancers, reverse proxies, NAT, and changing client addresses can alter the source IP the directory sees. Red Hat explicitly notes that load balancers can affect location-specific rules. Design ACIs using the address the server logs, not the address the client believes it uses.

Host or domain with dns

Match the client FQDN:

text
dns="admin1.example.com"
dns="*.example.com"

If reverse DNS fails, Directory Server skips dns ACIs for that client. Prefer ip when client addresses are not reliably resolvable.


Require secure connections and authentication methods

Security Strength Factor with ssf

Require a minimum key strength before an operation is allowed:

text
ssf>="128"

Example: an ACI that grants self-service userPassword changes only when the connection SSF is at least 128 (typically StartTLS or LDAPS):

ldif
dn: ou=People,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="userPassword")(version 3.0; acl "Self password change requires TLS"; allow (write) userdn="ldap:///self" and ssf>="128";)

This rule grants password-write access only when SSF is at least 128. Review inherited ACIs to ensure another rule does not independently grant the same write access over cleartext LDAP. Matching deny rules override allows, while permissions from multiple allow rules can combine.

In the lab, with no overlapping userPassword allow rule on cleartext LDAP, a plain bind fails:

bash
ldapmodify -x -H ldap://localhost:389 -D "uid=user1,ou=people,dc=example,dc=com" -y /root/aci-lab-user.pw -f /tmp/user1-pw-plain.ldif

Sample output:

output
ldap_modify: Insufficient access (50)
	additional info: Insufficient 'write' privilege to the 'userPassword' attribute of entry 'uid=user1,ou=people,dc=example,dc=com'.

StartTLS on port 389 succeeds when SSF meets the threshold. Use -ZZ so the command fails if StartTLS cannot be established. The lab uses a self-signed certificate, so use LDAPTLS_REQCERT=never only for local testing:

bash
LDAPTLS_REQCERT=never ldapmodify -ZZ -x -H ldap://localhost:389 -D "uid=user1,ou=people,dc=example,dc=com" -y /root/aci-lab-user.pw -f /tmp/user1-pw-plain.ldif

Sample output:

output
modifying entry "uid=user1,ou=people,dc=example,dc=com"

Authentication method with authmethod

Supported conditions include anonymous (none), password (simple), TLS client certificate (SSL), and SASL (SASL with mechanism such as EXTERNAL):

text
authmethod="simple"
authmethod="SSL"
authmethod="SASL EXTERNAL"

Use authmethod when an operation must run only over certificate or SASL binds. For example, you can deny simple binds for privileged subtrees while allowing certificate authentication from a known service account.


Restrict access by day and time

Day of week with dayofweek

text
dayofweek="Mon,Tue,Wed,Thu,Fri"
dayofweek="Sat,Sun"

Time of day with timeofday

Use 24-hour HHMM values on the server clock:

text
timeofday>="0900" and timeofday<="1700"

Business-hours example: grant Helpdesk read access only during weekday office hours.

text
(version 3.0; acl "Helpdesk business hours only"; allow (read,search,compare)
 groupdn="ldap:///cn=helpdesk,ou=Groups,dc=example,dc=com"
 and dayofweek="Mon,Tue,Wed,Thu,Fri"
 and timeofday>="0900" and timeofday<="1700";)

This ACI grants the rights during business hours. It does not deny access outside the window if another applicable ACI already grants the same rights. To make the schedule a strict requirement, ensure no broader ACI grants the same rights outside the window, or design a carefully scoped deny rule.

Directory Server evaluates dayofweek and timeofday using the server timezone and clock, not the client workstation. Keep time synchronized with NTP on every supplier and consumer.


Combine bind rules with Boolean operators

Use and, or, and not to group bind conditions:

text
groupdn="ldap:///cn=helpdesk,ou=Groups,dc=example,dc=com" and ssf>="128"
text
groupdn="ldap:///cn=Administrators,ou=Groups,dc=example,dc=com" or groupdn="ldap:///cn=Security Admins,ou=Groups,dc=example,dc=com"
text
userdn="ldap:///all" and not (ip="203.0.113.")

Parentheses control grouping when expressions mix and and or. Red Hat notes that one unreadable compound rule is harder to maintain than several small ACIs, but too many overlapping ACIs also increase evaluation cost. Split complex policies into named rules when operators struggle to predict the outcome.


Understand ACI inheritance and conflict resolution

Directory Server evaluates ACIs on the target entry and on every ancestor up to the top of the backend. Evaluation order among applicable rules is not "closest entry wins." Semantics of allow, deny, and bind match decide the result.

Behavior Effect
ACIs inherited from parent entries Rules on ou=People apply to users beneath it unless a narrower target excludes them
Entry-level versus subtree scope Default scope includes the entry storing the aci and its descendants
Multiple allow rules Permissions combine when no deny blocks them
Deny overrides allow A matching deny on an ancestor can void a child allow
Single instance only ACIs are not evaluated across chained remote backends

When an allow rule looks correct but access still fails, search aci on the target entry and every parent for a deny rule or a bind condition you forgot (IP, SSF, time, or filter mismatch).


Test advanced ACI scenarios

For each major rule, exercise:

Check Purpose
Matching identity Confirms the grant
Non-matching identity Confirms default deny still applies
Allowed attribute or value Confirms targetattr / targattrfilters scope
Excluded attribute or value Confirms narrowing works
Secure versus cleartext connection Confirms ssf and authmethod
Valid versus invalid network or time Confirms ip, dns, dayofweek, timeofday

Use real LDAP tools (ldapwhoami, ldapsearch, ldapmodify, and Modify DN from the rename chapter) rather than reviewing ACI text alone. For entry- and attribute-level permission matrices, use Get Effective Rights in the dedicated effective rights chapter instead of guessing from syntax.


Troubleshoot advanced ACIs

Symptom Likely cause Fix
Filter-based rule hits unexpected entries Stale or untested targetfilter Run the same filter with ldapsearch; tighten the filter or move the ACI
Valid modify rejected on one attribute value targattrfilters add/del mismatch Adjust the filter or use separate rules per value policy
userattr never matches Missing or malformed DN in the target attribute Confirm full DNs in manager, owner, or role attributes
groupdn filter misses the bind user User not in evaluated group attribute Verify member / uniqueMember values; place the ACI on the same instance as the group
IP rule works locally but fails remotely Proxy, NAT, or load balancer changes source IP Use the address Directory Server logs; prefer ip over dns when DNS is unreliable
Simple bind works but TLS rule fails SSF below threshold on cleartext LDAP Use StartTLS or LDAPS; retest with LDAPTLS_REQCERT only in lab
Time rule fails unexpectedly Server clock or timezone drift Sync with NTP; remember 24-hour timeofday format
Boolean expression behaves unexpectedly Incorrect and / or grouping Add parentheses; split into simpler ACIs
Allow on child, deny on parent Deny precedence Remove or narrow the ancestor deny; add a specific exception
Modify DN allowed from Contractors but not to Employees target_to mismatch or missing moddn right Align target_to with newSuperior; confirm target_from matches the source DN pattern

When result code 50 persists, see fix LDAP error 50.


What's Next


References


Summary

  1. Use targets to limit entries, attributes, values, and Modify DN source or destination.
  2. Use bind rules to limit identities, relationships, locations, security level, authentication method, and time.
  3. Prefer narrow, readable allow rules over broad != exclusions or catch-all deny rules.
  4. Test matching and non-matching identities, attributes, connections, and network conditions.
  5. When an allow rule appears ineffective, inspect inherited deny rules and bind clauses on ancestor entries.

Frequently Asked Questions

1. Can one ACI use targetfilter and targetattr together?

Yes. targetfilter selects entries and targetattr limits which attributes the rule affects. Both conditions must match before Directory Server applies the allow or deny rights.

2. Does targetattrfilters restrict LDAP search results by attribute value?

No. targattrfilters validates attribute values during entry add, modify, replace, and delete operations. It does not hide or filter attribute values returned by LDAP searches.

3. What is the difference between groupdn and userattr GROUPDN?

groupdn matches the bind user membership in a named group entry. userattr GROUPDN matches when the bind user DN is a member of a group named in an attribute on the target entry, such as owner.

4. Why does my allow ACI have no effect?

A deny rule on the same entry or an ancestor overrides allow. Directory Server also evaluates ACIs on the target entry and every parent up to the suffix, not only the entry where you added the rule.

5. Does Directory Server use the client clock for timeofday rules?

No. dayofweek and timeofday bind rules use the server clock and 24-hour time format. Keep NTP synchronized on every directory host.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …