Guide to Sync Configuration

What is LDAP Sync?

LDAP (Lightweight Directory Access Protocol) is the standard way to read and synchronise information from Active Directory (AD).

In Zluri’s setup, the LDAP Agent connects to your AD and fetches details like users, groups, and organizational units (OUs) — all based on the YAML configuration.


How the Configuration Works

Your configuration file controls what data to sync, how often to sync, and what access permissions to apply.

It’s structured in three main sections:

Configuration
├── General Settings (connection, timing, logging)
├── Global Settings (default sync rules)
└── Search Bases (specific OU rules and exceptions) (Optional) 

Section 1: General Settings

These settings control timing, retries, and LDAP connection behaviour.

general:
  polling_frequency_minutes: 5  # How often the agent checks for tasks in Zluri for AD
  sync_frequency_minutes: 60    # How often to perform a full sync
  max_page_size: 1000           # Number of records fetched per request
	daily_sync_hour: 23           # Local hour (0–23) when a full sync will automatically run daily

  

Understanding Key Terms

  • polling_frequency_minutes : How often the agent checks for tasks in Zluri for AD
  • sync_frequency_minutes: How often to perform a full sync
  • daily_sync_hour: It’s a local hour (0–23) when a full sync will automatically run daily. Follows 24 hour format. 23 in the above example = 11 PM.
  • max_page_size: Number of records fetched per request

Section 2: Global Settings

These are the default rules that apply to all AD objects unless overridden by a specific search base rule(Section 3).

globalSettings:
  defaultSearchBase:
    enabled: true
    baseDN: "DC=ZLURI,DC=DEV"   # Root of your domain
    scope: sub                  # Search the full directory tree
    accessModeDefault: R        # Default: read-only access

  entities:
    users:
      attributes:
        - dn
        - cn
        - mail
        - memberOf
      objectClasses:
        search:
          - user
        create:
          - user
      filters:
        dn:
          include: []
          exclude:
            - ^CN=Guest.*
        ldapCustomFilter: "(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
  # If your AD stores built‑in Users container and you want those users included
  includeUsersContainer: true

Understanding Key Terms

  • enabled: true or false - Enable/disable synchronisation for this entity

  • baseDN: The root location in AD where synchronisation starts.

    Example: DC=company,DC=com means your entire domain.

  • scope:

    • base - Indicates that only the entry specified as the search base should be considered. None of its subordinates will be considered.
    • one - Indicates that only the immediate children of the entry specified as the search base should be considered. The base entry itself should not be considered, nor any descendants of the immediate children of the base entry.
    • sub - Indicates that the entry specified as the search base, and all of its subordinates to any depth, should be considered.
    • children - Indicates that the entry specified by the search base should not be considered, but all of its subordinates to any depth should be considered.
  • accessModeDefault:

    • R : Data is visible and synced, but not changed
    • RW : Full sync and update permissions
  • includeUsersContainer: If your AD stores built‑in Users container and you want those users included, use true

    • true : If your AD stores built‑in Users container and you want those users included, value = true (DEFAULT)
    • false : If your AD stores built‑in Users container and you want those users included, value = false
  • attributes: List of data fields to pull (name, username, email, etc.).

Understanding Filters

  • ldapCustomFilter: Define which records to include/exclude.

    Example:

    ldapCustomFilter: "(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"

    The ldapCustomFilter above ensures disabled user accounts are skipped.

  • Understanding include and exclude filters:

    include and exclude filters use regex (regular expressions) to decide which directory objects are synced based on their Distinguished Name (DN) — the object’s full AD path (e.g., CN=John Doe,OU=Corporate,DC=company,DC=com).

    • include → Defines which objects to sync.

      Empty list [] means include all.

      Example:

      include: 
      		- "^CN=ADM_.*"

      → Syncs only objects whose names start with ADM_.

    • exclude → Defines which objects not to sync. Empty list [] means no exclusions.

      Example:

      exclude: 
      		- "^CN=Guest,.*"

      → Skips all users whose names start with “Guest”.

    Exclude takes precedence — if something matches both include and exclude, it’s excluded.

    You can use regex symbols like:

    • ^ = starts with
    • $ = ends with
    • .* = any characters
    • | = OR condition

Section 3: Search Bases (Specific Rules)

Each “search base” represents a folder (OU) in your AD where specific sync rules apply.

This allows fine-grained control. For example, you can give full access to IT, but read-only access to HR.

Example:

searchBases:
  - baseDN: "OU=Corporate,DC=ZLURI,DC=DEV"
    ruleId: "corporate-rule"
    enabled: true
    scope: sub
    traversal:
      users:
        accessModeDefault: RW     # Can read and modify user data
        filters:
          dn:
            exclude: 
                - ".*,OU=HR,OU=Corporate,.*"  # Skip HR users

Understanding Key Terms

  • baseDN: Where in AD this rule starts applying

  • ruleId: Each search base must include a short, unique ruleId. This ID helps Zluri identify which rule governs a specific LDAP object or action.

    • When Zluri performs an operation (e.g., sync, modify, or access validation) and it fails due to permission restrictions, the error message will clearly reference the ruleId — allowing admins to quickly identify which rule caused the restriction.

    Example: “Action denied: privileged-access rule restricts modification of this user.”

    • In short, ruleId provides traceability between LDAP sync behavior, configuration logic, and any permission-based enforcement within Zluri.
  • scope: Whether to include sub-OUs (folders) under this one

  • accessModeDefault: Default permission for the entity type (R, RW)

  • filters: Define what to include/exclude by name or condition(explained here)


Example Configuration File with all three sections

  • Basic / Quick Start Configuration:

    This version contains only General and Global Settings — no searchBases.

    It’s perfect for first-time setups or small domains that just need full-directory sync with minimal filtering.

# ===============================
# 1) GENERAL SETTINGS
# ===============================
general:
	  # Timers (minutes)
    polling_frequency_minutes: 5     # How often the agent checks for AD changes
    sync_frequency_minutes: 600      # How often to run a full sync
    daily_sync_hour: 23              # Local hour (0–23) when a full sync will automatically run daily
    # Reliability & performance
    max_page_size: 500
  # Page size for LDAP queries (tune for your AD)

# ===============================
# 2) GLOBAL DEFAULTS
# ===============================
globalSettings:
  # Where the agent starts if no specific rule matches
  defaultSearchBase:
    enabled: true
    baseDN: "DC=COMPANY,DC=LOCAL"  # Root of your AD domain
    scope: sub                     # sub = include all child OUs, explained above in section 2
    accessModeDefault: R           # R=read, RW=read/write

  # Entity‑level defaults that apply everywhere unless a searchBase overrides them
  entities:
    users:
      # Optional: add attributes includes the immutable defaults
      attributes:
        - mail
        - memberOf
        - whenChanged
      objectClasses:
        search:
            - user
        create: 
            - user
      filters:
        dn:
          include: []
          exclude: 
            - ^CN=Guest,.* # Skip Guest accounts globally
        # Only enabled users (standard AD disabled bit check)
        ldapCustomFilter: "(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"

    groups:
      attributes: 
        - member
        - groupType
        - description
      objectClasses:
        search: 
            - group
        create: 
            - group
      filters:
        dn:
          include: []
          exclude: []
        ldapCustomFilter: "(objectClass=group)"

    ous:
      attributes: 
        - description     # `dn`,`ou`,`objectClass` are auto‑added
      objectClasses:
        search: 
            - organizationalUnit
        create: 
            - organizationalUnit
      filters:
        dn:
          include: []
          exclude: []
        ldapCustomFilter: "(objectClass=organizationalUnit)"

  # If your AD stores built‑in Users container and you want those users included
  includeUsersContainer: true
  • Advanced / Granular Configuration (with Search Bases)

    This version includes searchBases with per-OU controls, filters, overrides, and ruleIds. It’s meant for large or complex environments where different departments or OUs need different sync or access rules.

# ===============================
# 1) GENERAL SETTINGS
# ===============================
general:
  # Timers (minutes)
    polling_frequency_minutes: 5     # How often the agent checks for AD changes
    sync_frequency_minutes: 600      # How often to run a full sync
    daily_sync_hour: 23              # Local Timezone in hour when the full sync will run
    # Reliability & performance
    max_page_size: 500
  # Page size for LDAP queries (tune for your AD)

# ===============================
# 2) GLOBAL DEFAULTS
# ===============================
globalSettings:
  # Where the agent starts if no specific rule matches
  defaultSearchBase:
    enabled: true
    baseDN: "DC=COMPANY,DC=LOCAL"  # Root of your AD domain
    scope: sub                     # sub = include all child OUs, explained above in section 2
    accessModeDefault: R           # R=read, RW=read/write

  # Entity‑level defaults that apply everywhere unless a searchBase overrides them
  entities:
    users:
      # Optional: add attributes includes the immutable defaults mentioned in the Defaults Section
      attributes:
        - mail
        - memberOf
        - whenChanged
      objectClasses:
        search:
            - user
        create: 
            - user
      filters:
        dn:
          include: []
          exclude: 
            - ^CN=Guest,.* # Skip Guest accounts globally
        # Only enabled users (standard AD disabled bit check)
        ldapCustomFilter: "(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"

    groups:
      attributes: 
        - member
        - groupType
        - description
      objectClasses:
        search: 
            - group
        create: 
            - group
      filters:
        dn:
          include: []
          exclude: []
        ldapCustomFilter: "(objectClass=group)"

    ous:
      attributes: 
        - description     # `dn`,`ou`,`objectClass` are auto‑added
      objectClasses:
        search: 
            - organizationalUnit
        create: 
            - organizationalUnit
      filters:
        dn:
          include: []
          exclude: []
        ldapCustomFilter: "(objectClass=organizationalUnit)"

  # If your AD stores built‑in Users container and you want those users included
  includeUsersContainer: true

# ===============================
# 3) SEARCH BASES (specific OU rules)
# ===============================
searchBases:
  # --- Corporate OU: Standard RW access ---
  - baseDN: "OU=Corporate,DC=COMPANY,DC=LOCAL"
    ruleId: "corporate-rule"
    enabled: true
    scope: sub
    traversal:
      users:
        accessModeDefault: RW
        filters:
          dn:
            include: []
            exclude: 
                - .*,OU=HR,OU=Corporate,.* # Exclude HR users from Corporate
          ldapCustomFilter: ""                     # No extra filter beyond globals
      groups:
        accessModeDefault: RW
        filters:
          dn:
            include: 
                - ^CN=(TEAM|APP)_.*       # Only TEAM_* or APP_* groups
            exclude: []
          ldapCustomFilter: ""
      ous:
        accessModeDefault: RW
        filters:
          dn:
            include: []
            exclude: 
                - ^OU=BreakGlass,.*        # Don’t traverse BreakGlass OU
          ldapCustomFilter: ""

  # --- HR OU: Read‑only & scoped ---
  - baseDN: "OU=HR,OU=Corporate,DC=COMPANY,DC=LOCAL"
    ruleId: "hr-restrictions"
    enabled: true
    scope: sub
    traversal:
      users:
        accessModeDefault: R
        filters:
          dn:
            include: []
            exclude: []
          ldapCustomFilter: "(department=Human Resources)"  # Only HR users
      groups:
        accessModeDefault: R                              # Hide HR groups
        filters:
          dn:
            include: []
            exclude: []
          ldapCustomFilter: ""
      ous:
        accessModeDefault: R
        filters:
          dn:
            include: []
            exclude: []
          ldapCustomFilter: ""

  # --- Privileged Accounts: High security ---
  - baseDN: "OU=Privileged Accounts,DC=COMPANY,DC=LOCAL"
    ruleId: "privileged-access"
    enabled: true
    scope: sub
    traversal:
      users:
        accessModeDefault: R
        filters:
          dn:
            include: 
                - ^CN=ADM_.*  # Admin‑prefixed accounts only
            exclude: []
          ldapCustomFilter: ""
      ous:
        accessModeDefault: R
        filters:
          dn:
            include: []
            exclude: 
                - ^OU=Disabled,.*
          ldapCustomFilter: ""

  # --- Service Accounts: Audit‑only ---
  - baseDN: "OU=Service Accounts,DC=COMPANY,DC=LOCAL"
    ruleId: "service-audit"
    enabled: true
    scope: sub
    traversal:
      users:
        accessModeDefault: R
        filters:
          dn:
            include: []
            exclude: []
          # Only actual service principals
          ldapCustomFilter: "(&(objectClass=user)(servicePrincipalName=*))"
        # Minimal audit fields; agent auto‑adds immutable defaults
        attributes:
            - sAMAccountName
            - servicePrincipalName
            - pwdLastSet
            - lastLogonTimestamp

Defaults (always fetched)

General Settings:

  • Sync frequency minutes - sync_frequency_minutes
    • min you can set is - 120(2 hours)
    • max you can set it - 1440(24 hours)
    • default - 360(6 hours)
  • Polling frequency minutes - polling_frequency_minutes
    • min you can set is - 5
    • max you can set it - 30
    • default - 5
  • Maximum Page Size - max_page_size
    • min you can set is - 500
    • max you can set it - 2500
    • default - 500

Attributes:

Even if you remove attributes in your config, the agent always fetches a minimal, non‑removable set so objects can be identified and joined correctly. These are enforced by the agent at runtime (your YAML doesn’t need to include them).

Immutable default attributes for entities:

  • Users: dn, cn, userPrincipalName, mail, userAccountControl ,sAMAccountName , objectClass
  • Groups: dn, member , groupType , objectClass,cn
  • OUs: dn , cn

If you list attributes, the agent uses your list + these defaults (no duplicates). You cannot opt out of defaults.

Scope:

Scope is defaulted to sub which searches the full directory tree for the specified baseDN.

User Container:

If your AD stores built‑in Users container by default, the value = true.

🧾 Example: Privileged Accounts Rule

- baseDN: "OU=Privileged Accounts,DC=ZLURI,DC=DEV"
  ruleId: "privileged-access"
  enabled: true
  traversal:
    users:
      accessModeDefault: R   # Read-only for all privileged accounts
      filters:
        dn:
          include: ["^CN=ADM_.*"] # Include only accounts starting with ADM_

Explanation:

This means:

“Fetch all admin (ADM_) accounts under the Privileged Accounts OU, but never sync the BreakGlass account.”


🧮 Filter Logic (How Inclusion & Exclusion Works)

Zluri combines global and searchbases rule-specific filters like this:

Effective Filter = (Global Filter) AND (Rule Filter)

If both define rules, only users matching both conditions will sync.

Evaluation order:

  1. Include patterns → Objects must match these (if defined)
  2. Exclude patterns → Objects matching are skipped
  3. LDAP custom filter → Additional logic (AD-side filtering)

🧩 Real-World Scenarios

✅ Scenario 1: Exclude Disabled Users

ldapCustomFilter: "(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"

Means: Only syncs users who are enabled in AD.


✅ Scenario 2: HR Read-Only Access

- baseDN: "OU=HR,OU=Corporate,DC=company,DC=com"
  traversal:
    users:
      accessModeDefault: "R"
      ldapCustomFilter: "(department=Human Resources)"

Means: HR users can be seen but not modified.


⚙️ Best Practices

TipWhy It Matters
Start with read-only modeAvoid accidental changes
Use clear rule namesEasier debugging
Test regex filtersPrevent unwanted exclusions
Limit sync scopeImproves performance
Document overridesKeeps your setup auditable

🚨 Troubleshooting

Troubleshooting Doc