[GH-ISSUE #780] [FEATURE REQUEST] Add entrydn as an attribute for groups to permit Duo directory synchronisation #280

Closed
opened 2026-02-27 08:16:21 +03:00 by kerem · 10 comments
Owner

Originally created by @Alumni1506 on GitHub (Dec 30, 2023).
Original GitHub issue: https://github.com/lldap/lldap/issues/780

Motivation
In order to implement Duo's directory sync via their authentication proxy service, they require the following

Copied from https://duo.com/docs/ldapsync#prerequisites:

Synced groups must have the groupOfNames object class.
Synced groups must list their members by DN (directoryName) via themember attribute.
Synced groups must have a cn attribute, used as the Duo group name after import.
Synced users must have the organizationalPerson object class.

Users:

Synced users or admins must list group memberships by DN using the memberOf attribute (via the [memberOf overlay](http://www.openldap.org/software/man.cgi?query=slapo-memberof&sektion=5&apropos=0&manpath=OpenLDAP+2.4-Release)).
Synced users or admins must have the organizationalPerson object class.
Synced groups must also have the attributes entrydn (used as the distinguished name) and entryuuid (the user unique identifier).

Describe the solution you'd like
Implement entrydn as a group attribute plus any from above required to get Duo directory sync to function.

Describe alternatives you've considered
No alternative available as the requirements are specified by Duo

Additional context
Duo's OpenLDAP Synchronization
Duo imports users and administrators via LDAP from OpenLDAP directories. When configuring OpenLDAP sync, you'll need to install the Duo Authentication Proxy application on a server that can connect to your directory server.
image

The above could potentially allow implementation of 2fa for services like jellyfin without breaking access to the mobile/TV app (example workflow given below). While Authelia can still be used to control/add/delete entries in LLDAP.

image

Originally created by @Alumni1506 on GitHub (Dec 30, 2023). Original GitHub issue: https://github.com/lldap/lldap/issues/780 **Motivation** In order to implement Duo's directory sync via their authentication proxy service, they require the following > Copied from https://duo.com/docs/ldapsync#prerequisites: > > Synced groups must have the groupOfNames object class. > Synced groups must list their members by DN (directoryName) via themember attribute. > Synced groups must have a cn attribute, used as the Duo group name after import. > Synced users must have the organizationalPerson object class. > Users: > > Synced users or admins must list group memberships by DN using the memberOf attribute (via the [memberOf overlay](http://www.openldap.org/software/man.cgi?query=slapo-memberof&sektion=5&apropos=0&manpath=OpenLDAP+2.4-Release)). > Synced users or admins must have the organizationalPerson object class. > Synced groups must also have the attributes entrydn (used as the distinguished name) and entryuuid (the user unique identifier). **Describe the solution you'd like** Implement entrydn as a group attribute plus any from above required to get Duo directory sync to function. **Describe alternatives you've considered** No alternative available as the requirements are specified by Duo **Additional context** Duo's OpenLDAP Synchronization Duo imports users and administrators via LDAP from OpenLDAP directories. When configuring OpenLDAP sync, you'll need to install the Duo Authentication Proxy application on a server that can connect to your directory server. ![image](https://github.com/lldap/lldap/assets/112524737/c0c09399-3c78-4e6f-9941-d64ae4cc76e7) The above could potentially allow implementation of 2fa for services like jellyfin without breaking access to the mobile/TV app (example workflow given below). While Authelia can still be used to control/add/delete entries in LLDAP. ![image](https://github.com/lldap/lldap/assets/112524737/a87fee8f-c901-4a4e-bb03-8f6b5f624ef2)
kerem 2026-02-27 08:16:21 +03:00
Author
Owner

@Alumni1506 commented on GitHub (Dec 31, 2023):

Hi,

I've just tested the sync and it is not working. I am using LLDAP version 0.5.0.
The error I'm getting is as per below
image

Below is the log of the sync process.

2023-12-31T12:06:38.546615+0000 [duoauthproxy.lib.log#info] Summary: drpc_timing. Extra data: {'data_length': 617, 'parse_duration': 0, 'decompress_duration': None, 'call_id': '<REDACTED>'}
2023-12-31T12:06:38.547603+0000 [duoauthproxy.lib.log#info] Performing LDAP search for directory sync: call_id=<REDACTED> host=192.168.10.5 port=3890 base_dn=DC=EXAMPLE,DC=com auth_type=plain transport_type=clear ssl_verify_depth=9 ssl_verify_hostname=False ssl_ca_certs=False attributes=['entrydn', 'entryuuid', 'cn']
2023-12-31T12:06:38.547603+0000 [duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory#info] Starting factory <duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory object at 0x00000207E3DF3700>
2023-12-31T12:06:38.624619+0000 [Uninitialized] C->S LDAPMessage(id=9, value=LDAPBindRequest(version=3, dn='uid=ro_admin,ou=people,dc=EXAMPLE,dc=com', auth='****', sasl=False), controls=None)
2023-12-31T12:06:38.751650+0000 [LdapSyncClientProtocol, <REDACTED>,client] C<-S LDAPMessage(id=9, value=LDAPBindResponse(resultCode=0), controls=None)
2023-12-31T12:06:38.758650+0000 [LdapSyncClientProtocol, <REDACTED>,client] C->S LDAPMessage(id=10, value=LDAPSearchRequest(baseObject='DC=EXAMPLE,DC=com', scope=2, derefAliases=0, sizeLimit=0, timeLimit=0, typesOnly=0, filter=LDAPFilter_and(value=[LDAPFilter_equalityMatch(attributeDesc=LDAPAttributeDescription(value='objectclass'), assertionValue=LDAPAssertionValue(value='groupofnames')), LDAPFilter_or(value=[LDAPFilter_equalityMatch(attributeDesc=LDAPAttributeDescription(value='entryuuid'), assertionValue=LDAPAssertionValue(value='<REDACTED>'))])]), attributes=[b'entrydn', b'entryuuid', b'cn']), controls=[(b'1.2.840.113556.1.4.319', True, BERSequence(value=[BERInteger(value=5000), BEROctetString(value='')]))])
2023-12-31T12:06:38.849203+0000 [LdapSyncClientProtocol, <REDACTED>,client] C<-S LDAPMessage(id=10, value=LDAPSearchResultEntry(objectName=b'cn=jellyfin-users,ou=groups,dc=EXAMPLE,dc=com', attributes=[(b'entryuuid', [b'8e99cd33-3e4f-36e5-9067-e5c130c8b262']), (b'cn', [b'jellyfin-users'])]), controls=None)
2023-12-31T12:06:38.987234+0000 [LdapSyncClientProtocol, <REDACTED>,client] C<-S LDAPMessage(id=10, value=LDAPSearchResultDone(resultCode=0), controls=None)
2023-12-31T12:06:38.987234+0000 [duoauthproxy.lib.log#critical] Unexpected error handling message
	Traceback (most recent call last):
	  File "twisted\internet\tcp.pyc", line 248, in doRead
	    
	  File "twisted\internet\tcp.pyc", line 253, in _dataReceived
	    
	  File "ldaptor\protocols\ldap\ldapclient.pyc", line 67, in dataReceived
	    
	  File "ldaptor\protocols\ldap\ldapclient.pyc", line 217, in handle
	    
	--- <exception caught here> ---
	  File "duoauthproxy\modules\drpc_plugins\ldap_base.pyc", line 402, in handle_msg
	    
	  File "duoauthproxy\lib\util.pyc", line 708, in get_cookie
	    
	builtins.TypeError: 'NoneType' object is not iterable
	
2023-12-31T12:06:38.989233+0000 [duoauthproxy.lib.log#error] Paging cookie not found!
2023-12-31T12:06:38.989233+0000 [duoauthproxy.lib.log#info] Summary: drpc_jsonify_metrics. Extra data: {'json_parse_time': 0.0, 'length': 244}
2023-12-31T12:06:38.990234+0000 [duoauthproxy.lib.log#info] Summary: drpc_msg_metrics. Extra data: {'msg_time': 0.0010001659393310547, 'data_length': 245, 'msg_id': '2d6d77c0dda4314e7fbdf5a968ee928d'}
2023-12-31T12:06:38.990234+0000 [duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory#info] Stopping factory <duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory object at 0x00000207E3DF3700>
<!-- gh-comment-id:1872934539 --> @Alumni1506 commented on GitHub (Dec 31, 2023): Hi, I've just tested the sync and it is not working. I am using LLDAP version 0.5.0. The error I'm getting is as per below ![image](https://github.com/lldap/lldap/assets/112524737/1f9f9b7e-da10-4b38-ad76-32ee9e32e4dd) Below is the log of the sync process. ``` 2023-12-31T12:06:38.546615+0000 [duoauthproxy.lib.log#info] Summary: drpc_timing. Extra data: {'data_length': 617, 'parse_duration': 0, 'decompress_duration': None, 'call_id': '<REDACTED>'} 2023-12-31T12:06:38.547603+0000 [duoauthproxy.lib.log#info] Performing LDAP search for directory sync: call_id=<REDACTED> host=192.168.10.5 port=3890 base_dn=DC=EXAMPLE,DC=com auth_type=plain transport_type=clear ssl_verify_depth=9 ssl_verify_hostname=False ssl_ca_certs=False attributes=['entrydn', 'entryuuid', 'cn'] 2023-12-31T12:06:38.547603+0000 [duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory#info] Starting factory <duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory object at 0x00000207E3DF3700> 2023-12-31T12:06:38.624619+0000 [Uninitialized] C->S LDAPMessage(id=9, value=LDAPBindRequest(version=3, dn='uid=ro_admin,ou=people,dc=EXAMPLE,dc=com', auth='****', sasl=False), controls=None) 2023-12-31T12:06:38.751650+0000 [LdapSyncClientProtocol, <REDACTED>,client] C<-S LDAPMessage(id=9, value=LDAPBindResponse(resultCode=0), controls=None) 2023-12-31T12:06:38.758650+0000 [LdapSyncClientProtocol, <REDACTED>,client] C->S LDAPMessage(id=10, value=LDAPSearchRequest(baseObject='DC=EXAMPLE,DC=com', scope=2, derefAliases=0, sizeLimit=0, timeLimit=0, typesOnly=0, filter=LDAPFilter_and(value=[LDAPFilter_equalityMatch(attributeDesc=LDAPAttributeDescription(value='objectclass'), assertionValue=LDAPAssertionValue(value='groupofnames')), LDAPFilter_or(value=[LDAPFilter_equalityMatch(attributeDesc=LDAPAttributeDescription(value='entryuuid'), assertionValue=LDAPAssertionValue(value='<REDACTED>'))])]), attributes=[b'entrydn', b'entryuuid', b'cn']), controls=[(b'1.2.840.113556.1.4.319', True, BERSequence(value=[BERInteger(value=5000), BEROctetString(value='')]))]) 2023-12-31T12:06:38.849203+0000 [LdapSyncClientProtocol, <REDACTED>,client] C<-S LDAPMessage(id=10, value=LDAPSearchResultEntry(objectName=b'cn=jellyfin-users,ou=groups,dc=EXAMPLE,dc=com', attributes=[(b'entryuuid', [b'8e99cd33-3e4f-36e5-9067-e5c130c8b262']), (b'cn', [b'jellyfin-users'])]), controls=None) 2023-12-31T12:06:38.987234+0000 [LdapSyncClientProtocol, <REDACTED>,client] C<-S LDAPMessage(id=10, value=LDAPSearchResultDone(resultCode=0), controls=None) 2023-12-31T12:06:38.987234+0000 [duoauthproxy.lib.log#critical] Unexpected error handling message Traceback (most recent call last): File "twisted\internet\tcp.pyc", line 248, in doRead File "twisted\internet\tcp.pyc", line 253, in _dataReceived File "ldaptor\protocols\ldap\ldapclient.pyc", line 67, in dataReceived File "ldaptor\protocols\ldap\ldapclient.pyc", line 217, in handle --- <exception caught here> --- File "duoauthproxy\modules\drpc_plugins\ldap_base.pyc", line 402, in handle_msg File "duoauthproxy\lib\util.pyc", line 708, in get_cookie builtins.TypeError: 'NoneType' object is not iterable 2023-12-31T12:06:38.989233+0000 [duoauthproxy.lib.log#error] Paging cookie not found! 2023-12-31T12:06:38.989233+0000 [duoauthproxy.lib.log#info] Summary: drpc_jsonify_metrics. Extra data: {'json_parse_time': 0.0, 'length': 244} 2023-12-31T12:06:38.990234+0000 [duoauthproxy.lib.log#info] Summary: drpc_msg_metrics. Extra data: {'msg_time': 0.0010001659393310547, 'data_length': 245, 'msg_id': '2d6d77c0dda4314e7fbdf5a968ee928d'} 2023-12-31T12:06:38.990234+0000 [duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory#info] Stopping factory <duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory object at 0x00000207E3DF3700> ```
Author
Owner

@nitnelave commented on GitHub (Dec 31, 2023):

Did you make sure to pull the latest image? Given the request they sent, the response should contain the attribute. You need to be using a "latest" tag (latest-alpine, latest-debian, ...)
The v0.5 tag will not work (no new release containing the change)

<!-- gh-comment-id:1872943384 --> @nitnelave commented on GitHub (Dec 31, 2023): Did you make sure to pull the latest image? Given the request they sent, the response should contain the attribute. You need to be using a "latest" tag (latest-alpine, latest-debian, ...) The v0.5 tag will not work (no new release containing the change)
Author
Owner

@Alumni1506 commented on GitHub (Dec 31, 2023):

Did you make sure to pull the latest image? Given the request they sent, the response should contain the attribute. You need to be using a "latest" tag (latest-alpine, latest-debian, ...) The v0.5 tag will not work (no new release containing the change)

Made a mistake, I using the the stable tag. The LDAP is now connected but I am now having another issue with syncing. I've attached a log to see if you may have an idea as to what is wrong

2023-12-31T20:11:42.978123+0000 [duoauthproxy.lib.log#info] Performing LDAP search for directory sync: call_id=8c19f4c38fc287dca937a6d4b4adf40c_54fd7c2c5b707ebd6882c4839d11c0a9 host=192.168.10.5 port=3890 base_dn=dc=EXAMPLE,dc=com auth_type=plain transport_type=clear ssl_verify_depth=9 ssl_verify_hostname=False ssl_ca_certs=False attributes=['entrydn', 'entryuuid', 'cn', 'objectclass', 'member']
2023-12-31T20:11:42.978123+0000 [duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory#info] Starting factory <duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory object at 0x00000207E3DF78B0>
2023-12-31T20:11:43.051409+0000 [Uninitialized] C->S LDAPMessage(id=263, value=LDAPBindRequest(version=3, dn='uid=ro_admin,ou=people,dc=EXAMPLE,dc=com', auth='****', sasl=False), controls=None)
2023-12-31T20:11:43.188168+0000 [LdapSyncClientProtocol,,client] C<-S LDAPMessage(id=263, value=LDAPBindResponse(resultCode=0), controls=None)
2023-12-31T20:11:43.192061+0000 [LdapSyncClientProtocol, ,client] C->S LDAPMessage(id=264, value=LDAPSearchRequest(baseObject='dc=EXAMPLE,dc=com', scope=2, derefAliases=0, sizeLimit=0, timeLimit=0, typesOnly=0, filter=LDAPFilter_and(value=[LDAPFilter_equalityMatch(attributeDesc=LDAPAttributeDescription(value='objectclass'), assertionValue=LDAPAssertionValue(value='groupofnames')), LDAPFilter_equalityMatch(attributeDesc=LDAPAttributeDescription(value='entryuuid'), assertionValue=LDAPAssertionValue(value='8e99cd33-3e4f-36e5-9067-e5c130c8b262'))]), attributes=[b'cn', b'member', b'objectclass', b'entryuuid', b'entrydn']), controls=[(b'1.2.840.113556.1.4.319', True, BERSequence(value=[BERInteger(value=5000), BEROctetString(value='')]))])
2023-12-31T20:11:43.290824+0000 [LdapSyncClientProtocol, ,client] C<-S LDAPMessage(id=264, value=LDAPSearchResultEntry(objectName=b'cn=jellyfin-users,ou=groups,dc=EXAMPLE,dc=com', attributes=[(b'cn', [b'jellyfin-users']), (b'member', [b'uid=duotest,ou=people,dc=EXAMPLE,dc=com', b'uid=duotest2,ou=people,dc=EXAMPLE,dc=com']), (b'objectclass', [b'groupOfUniqueNames']), (b'entryuuid', [b'8e99cd33-3e4f-36e5-9067-e5c130c8b262']), (b'entrydn', [b'uid=jellyfin-users,ou=groups,dc=EXAMPLE,dc=com'])]), controls=None)
2023-12-31T20:11:43.461483+0000 [LdapSyncClientProtocol, ,client] C<-S LDAPMessage(id=264, value=LDAPSearchResultDone(resultCode=0), controls=[(b'1.2.840.113556.1.4.319', None, b'0\x05\x02\x01\x01\x04\x00')])

<!-- gh-comment-id:1873038977 --> @Alumni1506 commented on GitHub (Dec 31, 2023): > Did you make sure to pull the latest image? Given the request they sent, the response should contain the attribute. You need to be using a "latest" tag (latest-alpine, latest-debian, ...) The v0.5 tag will not work (no new release containing the change) Made a mistake, I using the the stable tag. The LDAP is now connected but I am now having another issue with syncing. I've attached a log to see if you may have an idea as to what is wrong > **2023-12-31T20:11:42.978123+0000 [duoauthproxy.lib.log#info] Performing LDAP search for directory sync: call_id=8c19f4c38fc287dca937a6d4b4adf40c_54fd7c2c5b707ebd6882c4839d11c0a9 host=192.168.10.5 port=3890 base_dn=dc=EXAMPLE,dc=com auth_type=plain transport_type=clear ssl_verify_depth=9 ssl_verify_hostname=False ssl_ca_certs=False attributes=['entrydn', 'entryuuid', 'cn', 'objectclass', 'member']** > 2023-12-31T20:11:42.978123+0000 [duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory#info] Starting factory <duoauthproxy.modules.drpc_plugins.ldap_directory_sync.LdapSyncClientFactory object at 0x00000207E3DF78B0> > 2023-12-31T20:11:43.051409+0000 [Uninitialized] C->S LDAPMessage(id=263, value=LDAPBindRequest(version=3, dn='uid=ro_admin,ou=people,dc=EXAMPLE,dc=com', auth='****', sasl=False), controls=None) > 2023-12-31T20:11:43.188168+0000 [LdapSyncClientProtocol,<REDACTED>,client] C<-S LDAPMessage(id=263, value=LDAPBindResponse(resultCode=0), controls=None) > 2023-12-31T20:11:43.192061+0000 [LdapSyncClientProtocol, <REDACTED>,client] C->S LDAPMessage(id=264, value=LDAPSearchRequest(baseObject='dc=EXAMPLE,dc=com', scope=2, derefAliases=0, sizeLimit=0, timeLimit=0, typesOnly=0, filter=LDAPFilter_and(value=[LDAPFilter_equalityMatch(attributeDesc=LDAPAttributeDescription(value='objectclass'), assertionValue=LDAPAssertionValue(value='groupofnames')), LDAPFilter_equalityMatch(attributeDesc=LDAPAttributeDescription(value='entryuuid'), assertionValue=LDAPAssertionValue(value='8e99cd33-3e4f-36e5-9067-e5c130c8b262'))]), attributes=[b'cn', b'member', b'objectclass', b'entryuuid', b'entrydn']), controls=[(b'1.2.840.113556.1.4.319', True, BERSequence(value=[BERInteger(value=5000), BEROctetString(value='')]))]) > 2**023-12-31T20:11:43.290824+0000 [LdapSyncClientProtocol, <REDACTED>,client] C<-S LDAPMessage(id=264, value=LDAPSearchResultEntry(objectName=b'cn=jellyfin-users,ou=groups,dc=EXAMPLE,dc=com', attributes=[(b'cn', [b'jellyfin-users']), (b'member', [b'uid=duotest,ou=people,dc=EXAMPLE,dc=com', b'uid=duotest2,ou=people,dc=EXAMPLE,dc=com']), (b'objectclass', [b'groupOfUniqueNames']), (b'entryuuid', [b'8e99cd33-3e4f-36e5-9067-e5c130c8b262']), (b'entrydn', [b'uid=jellyfin-users,ou=groups,dc=EXAMPLE,dc=com'])]), controls=None)** > 2023-12-31T20:11:43.461483+0000 [LdapSyncClientProtocol, <REDACTED>,client] C<-S LDAPMessage(id=264, value=LDAPSearchResultDone(resultCode=0), controls=[(b'1.2.840.113556.1.4.319', None, b'0\x05\x02\x01\x01\x04\x00')])
Author
Owner

@nitnelave commented on GitHub (Dec 31, 2023):

What is not working? What error are you getting? I see only a correct search query with a correct response, so it's a bit hard to help :/

<!-- gh-comment-id:1873053840 --> @nitnelave commented on GitHub (Dec 31, 2023): What is not working? What error are you getting? I see only a correct search query with a correct response, so it's a bit hard to help :/
Author
Owner

@Alumni1506 commented on GitHub (Jan 1, 2024):

Sorry, i thought i posted the snippets

image

The admin page of duo is not helpful

image

<!-- gh-comment-id:1873363350 --> @Alumni1506 commented on GitHub (Jan 1, 2024): Sorry, i thought i posted the snippets ![image](https://github.com/lldap/lldap/assets/112524737/a6cbcc0e-197c-40a4-a5c0-105a4c8db6cf) The admin page of duo is not helpful ![image](https://github.com/lldap/lldap/assets/112524737/2bdf50bf-d622-46f1-88b4-ee319819a8e6)
Author
Owner

@nitnelave commented on GitHub (Jan 1, 2024):

Hmm, hard to say from the errors. Can you ask some Duo support for help with this? AFAICT, LLDAP is behaving well.

<!-- gh-comment-id:1873364187 --> @nitnelave commented on GitHub (Jan 1, 2024): Hmm, hard to say from the errors. Can you ask some Duo support for help with this? AFAICT, LLDAP is behaving well.
Author
Owner

@Alumni1506 commented on GitHub (Jan 1, 2024):

will do, hopefully they come back with something useful

<!-- gh-comment-id:1873403821 --> @Alumni1506 commented on GitHub (Jan 1, 2024): will do, hopefully they come back with something useful
Author
Owner

@Alumni1506 commented on GitHub (Jan 3, 2024):

Just an update, I did not manage to Duo's LDAP working with push notification. Tried several mods with no luck

<!-- gh-comment-id:1876108780 --> @Alumni1506 commented on GitHub (Jan 3, 2024): Just an update, I did not manage to Duo's LDAP working with push notification. Tried several mods with no luck
Author
Owner

@nitnelave commented on GitHub (Jan 4, 2024):

I'd be curious to see if openLdap replies differently. I can try to match their response.

<!-- gh-comment-id:1876476905 --> @nitnelave commented on GitHub (Jan 4, 2024): I'd be curious to see if openLdap replies differently. I can try to match their response.
Author
Owner

@Alumni1506 commented on GitHub (Jan 9, 2024):

I'll try to make some time to find a simple openldap docker image to test

<!-- gh-comment-id:1882164449 --> @Alumni1506 commented on GitHub (Jan 9, 2024): I'll try to make some time to find a simple openldap docker image to test
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/lldap-lldap#280
No description provided.