Which hops does an Ignition login cross on its way to OpenLDAP?
Follow the packet. A client login lands on the Ignition Gateway. The Active Directory user source opens an LDAP connection to the directory server, here ldap://server:389. In a remote-site design that server is OpenLDAP, which either holds the users locally or proxies the request onward to the primary Active Directory domain controller. The request stops wherever the first hop rejects it, and the gateway log line tells you which hop that was.
The user source does not make one kind of LDAP call. It makes two, and each binds with a different identity:
| Phase | Bind identity | Search base (working example) | Filter (working example) | When it runs |
|---|---|---|---|---|
| User list | Gateway Username: cn=admin,dc=xxx,dc=yyy,dc=zzz
|
dc=xxx,dc=yyy,dc=zzz |
(objectClass=inetOrgPerson) |
Profile load, user and role browsing |
| Role list | Gateway Username | ou=groups,dc=xxx,dc=yyy,dc=zzz |
(objectClass=groupOfUniqueNames) |
Profile load, role mapping |
| User authentication | The logging-in user, e.g. cn=cgi2,dc=xxx,dc=yyy,dc=zzz
|
dc=xxx,dc=yyy,dc=zzz |
(&(objectClass=inetOrgPerson)(cn={0})) |
Every login |
The field description for Gateway Username reads: "The login name for the gateway to use when querying Active Directory. Used for retrieving the list of users and roles via LDAP." That is literal. The service account enumerates users and roles. It does not run the search during authentication. At login the gateway binds as the user and runs the user search on that same connection. Two failure modes follow from this. A bind name that OpenLDAP cannot parse fails with error 34. A user who can bind but cannot see the search base fails with error 32.
Layer one first. From the gateway host, confirm TCP 389 reaches the OpenLDAP server. On Windows use Test-NetConnection server -Port 389. On Linux use nc -vz server 389. If this fails, you have a firewall or routing problem, and no user source setting will fix it.
Why does the gateway report error code 34, invalid DN?
The full console message is Failed connecting to LDAP server. Caused by: javax.naming.InvalidNameException: [LDAP: error code 34 - invalid DN]. Result code 34 means the server could not parse the bind name as a distinguished name. It rejected the bind before checking any password.
The cause is the user source type. The Active Directory profile expects an Active Directory server, and a domain controller accepts bind names that are not DNs: a UPN such as admin@domain, or a down-level name such as DOMAIN\admin. OpenLDAP (slapd) accepts only a syntactically valid DN in a simple bind. It parses the bind name on arrival. A bare admin or a UPN therefore fails at the first hop with code 34. This happens even when OpenLDAP only proxies to AD, because the name never gets forwarded.
The fix is to enter the Gateway Username as a complete DN. The form that bound successfully against a TurnKey OpenLDAP appliance was cn=admin,dc=xxx,dc=yyy,dc=zzz. The gateway then logged:
Connecting to "ldap://server:389" as "cn=admin,dc=xxx,dc=yyy,dc=zzz"...
Read the exact DN of your service account from the directory browser (phpLDAPadmin on the TurnKey appliance). Copy it character for character. A comma, a trailing space, or a missing dc= component all reproduce code 34.
Check before moving on: run this from any host that can reach the server:
ldapwhoami -x -H ldap://server:389 -D "cn=admin,dc=xxx,dc=yyy,dc=zzz" -W
If it returns dn:cn=admin,..., the bind DN and password are good. Enter the identical string in the gateway.
What LDAP result codes appear, and what does each point to?
| Symptom in gateway log | Phase | Most likely cause | First check |
|---|---|---|---|
error code 34 - invalid DN on connect |
Service bind | Gateway Username entered as a bare name or UPN instead of a DN |
ldapwhoami with the exact string |
error code 34 only at user login |
User bind | The DN the gateway builds for the user is malformed | Read the Connecting ... as line for that login |
| Invalid credentials at user login | User bind | Constructed user DN does not match the entry's real DN (e.g. user lives under an OU), or wrong password | Compare constructed DN with the DN in phpLDAPadmin |
error code 32 - No Such Object after Connecting ... as "cn=<user>,..."
|
User search | User lacks read/disclose rights on the search base, or the base is not present in the namespace the server exposes |
ldapsearch bound as the user against the same base |
| Users load ("Found N user") but roles list is empty | Role search | Role base or filter does not match the directory's group object class |
ldapsearch as service account on the role base |
How do the user and role search properties map to OpenLDAP?
The AD user source ships with an Active Directory schema in mind: AD attributes such as sAMAccountName, and AD object classes. A stock OpenLDAP tree uses inetOrgPerson for people and groupOfUniqueNames (or groupOfNames) for groups. Replace the search properties with values that match what the server actually holds. The following set loaded users and roles against an OpenLDAP appliance:
| Property | Value that worked | Derived from |
|---|---|---|
| Gateway Username | cn=admin,dc=xxx,dc=yyy,dc=zzz |
DN of the directory admin/service entry |
| User search base | dc=xxx,dc=yyy,dc=zzz |
Directory suffix, or the OU that contains people |
| User list filter | (objectClass=inetOrgPerson) |
Object class of person entries |
| User login filter | (&(objectClass=inetOrgPerson)(cn={0})) |
{0} is replaced by the typed username |
| Role search base |
ods=groups branch: ou=groups,dc=xxx,dc=yyy,dc=zzz
|
OU that contains group entries |
| Role filter | (objectClass=groupOfUniqueNames) |
Object class of group entries |
If your people entries use uid as the login name rather than cn, change the attribute in the login filter to match. The attribute in the filter must match the name users type.
Check before moving on: save the profile and read the gateway log. The service-account phase should show lines equivalent to:
Searching for user in [dc=xxx,dc=yyy,dc=zzz] with filter "(objectClass=inetOrgPerson)"...
Successfully loaded user: "{username=cgi}"
Found 3 user in 1 ms
Searching for roles in [ou=groups,dc=xxx,dc=yyy,dc=zzz] with filter "(objectClass=groupOfUniqueNames)"...
Found 9 roles in 1 ms
Compare the counts against the same query run outside Ignition:
ldapsearch -x -H ldap://server:389 -D "cn=admin,dc=xxx,dc=yyy,dc=zzz" -W \
-b "dc=xxx,dc=yyy,dc=zzz" "(objectClass=inetOrgPerson)" dn
What DN does the gateway build for a logging-in user?
A successful user list proves only the service account path. Logins take the other path. On a login attempt the gateway logged Connecting to "ldap://server:389" as "cn=cgi2,dc=xxx,dc=yyy,dc=zzz". The profile built a DN from the typed username and the directory base. That constructed DN has to be the entry's real DN.
Decision path:
- Attempt a login and copy the DN from the
Connecting ... asline. - Look up the same user in phpLDAPadmin, or with
ldapsearchas admin using(cn=cgi2), and read itsdn:. - If the two DNs are identical, the bind path is correct. Continue to the error 32 section.
- If the real entry sits deeper, for example under a people OU, the constructed DN points at nothing. Either restructure the gateway settings so the base the profile appends matches the users' parent container, or flatten the users to the container the profile expects.
Check before moving on: bind as the user outside Ignition with the exact constructed DN:
ldapwhoami -x -H ldap://server:389 -D "cn=cgi2,dc=xxx,dc=yyy,dc=zzz" -W
Why does login fail with error code 32 after the user binds?
Here the user bind succeeded. The next operation then failed:
Connecting to "ldap://server:389" as "cn=cgi2,dc=xxx,dc=yyy,dc=zzz"...
Searching for user in [dc=xxx,dc=yyy,dc=zzz] with filter "(&(objectClass=inetOrgPerson)(cn={0}))"...
Authentication as user "cgi2" failed with NamingException: [LDAP: error code 32 - No Such Object]
The search runs on the user's own connection, so the user's rights govern it, not the admin's. OpenLDAP deliberately hides entries from identities that lack disclose rights. Instead of "insufficient access", it returns noSuchObject (32) for a base entry that the bound identity cannot see. A restricted user bound as cn=cgi2 therefore gets 32 against a suffix that plainly exists for the admin.
Grant authenticated users read access to the user subtree. If role membership is resolved during login, grant the same on the group subtree. ACLs are first-match. Keep the password-protection rule above the broad rule so that userPassword stays unreadable. An illustrative slapd access block for the example tree:
access to attrs=userPassword
by self write
by anonymous auth
by * none
access to dn.subtree="dc=xxx,dc=yyy,dc=zzz"
by dn.exact="cn=admin,dc=xxx,dc=yyy,dc=zzz" write
by users read
by * none
On appliances configured through the online configuration backend (cn=config), apply the same directives as access values on the database entry with ldapmodify, not by editing a flat file. Tighten by users read to the specific subtrees and attributes your security policy allows. The minimum is the user's own entry, the attributes in the login filter, and the group entries used for roles.
The second cause of 32 is a search base that does not exist in the namespace this server presents. That happens with a typo in the base, or with a proxy whose suffix differs from the base configured in the gateway. The admin-bound test separates the two causes. If admin also gets 32 on that base, the base is wrong, not the ACL.
Check before moving on: reproduce the gateway's login search as the user:
ldapsearch -x -H ldap://server:389 -D "cn=cgi2,dc=xxx,dc=yyy,dc=zzz" -W \
-b "dc=xxx,dc=yyy,dc=zzz" "(&(objectClass=inetOrgPerson)(cn=cgi2))" dn
ldapsearch -x -H ldap://server:389 -D "cn=cgi2,dc=xxx,dc=yyy,dc=zzz" -W \
-b "ou=groups,dc=xxx,dc=yyy,dc=zzz" "(objectClass=groupOfUniqueNames)" dn
Both queries must return entries before the gateway login can succeed.
What changes when OpenLDAP only proxies the Active Directory controller?
When OpenLDAP at the remote site is a proxy to the primary AD domain controller, there are two directory hops in the path: gateway to proxy, then proxy to AD. The rules above apply at each hop:
- Bind names: the proxy parses the bind name as a DN, so the Gateway Username and each user's bind must be DNs even though AD itself would accept a UPN. Use the DN form of the AD accounts as the proxy presents them.
- Namespace: the search bases in the gateway must be the suffix the proxy exposes. If the proxy rewrites the AD suffix, use the rewritten form. If it passes the AD suffix through, use the AD DNs.
-
Schema: the objects behind the proxy are AD objects. The
inetOrgPerson/groupOfUniqueNamesfilters that work on a native OpenLDAP tree will not match unless the proxy maps them. Build filters from the object classes and attributes the proxy actually returns. - Rights: the user-bound login search is evaluated first by the proxy ACLs and then by AD permissions on the forwarded identity. An error 32 can originate at either hop.
Check before moving on: run every ldapsearch above against the proxy address, not the domain controller. Inspect one returned user and one group with all attributes. Then write the filters from what the proxy returns.
What are the options if the directory cannot be reshaped?
The AD profile needs three things from the directory. The typed username must map to a bindable DN. The user must be able to search for their own entry. The group entries must be readable for role lookup. When directory policy blocks any of these, options are limited:
| Option | What it changes | Constraint |
|---|---|---|
| Adjust OpenLDAP ACLs and DN layout | Makes the directory fit the AD profile's bind-then-search behaviour | Requires admin rights on the remote OpenLDAP |
| Point the gateway at the domain controller directly | Removes the proxy hop; AD accepts its native bind forms | Needs WAN reachability to the DC on the LDAP port |
| AD hybrid user source (directory authenticates, roles held in Ignition) | Removes the dependency on group-entry read rights for roles | User entry search at login still applies |
| Custom authentication module via the Ignition SDK | Plain-LDAP semantics, e.g. search as service account then bind as user | Development and maintenance effort |
How do you verify the profile end to end?
- From the gateway host, confirm TCP 389 to the OpenLDAP server is open.
- Run
ldapwhoamiwith the Gateway Username DN. It must echo that DN back. - Run
ldapsearchas the service account on the user and role bases with the configured filters. Record the counts. - Save the user source. The gateway log must show a connect as the full DN,
Found N user, andFound N rolesmatching the counts from step 3, with no code 34. - Log in with a restricted account. Confirm the
Connecting ... asDN equals the entry's real DN. - Repeat that account's login search with
ldapsearchbound as the user. It must return the user entry and the group entries, not code 32. - Log into an Ignition client or designer with the same account. Confirm authentication passes and the expected roles are assigned. Then confirm that a wrong password is rejected.
FAQ
Why does Ignition show LDAP error code 34 invalid DN with OpenLDAP?
OpenLDAP accepts only a full distinguished name in a simple bind, while the AD user source is often set up with a bare name or UPN that only Active Directory accepts. Enter the Gateway Username as a DN such as cn=admin,dc=xxx,dc=yyy,dc=zzz and test it with ldapwhoami.
Why does the Ignition AD user source list users but fail at login with error 32 No Such Object?
User listing runs as the Gateway Username, but the login search runs on the logging-in user's own connection. OpenLDAP returns noSuchObject when that user lacks read/disclose rights on the search base. Grant authenticated users read access to the user and group subtrees and confirm with ldapsearch bound as the user.
Why doesn't Ignition use the Gateway Username to authenticate LDAP users?
The Gateway Username only retrieves the user and role lists. Authentication binds as the user and searches as that user, so every user needs search rights on the configured bases, as shown by the Connecting ... as "cn=<user>,..." log line at login.