Mailman and OpenDJ

For a project I started to add mailing list functionality to the development infrastructure, as requirement the mailing list should make use of the LDAP service.

In a previous article I explained that I chose OpenDJ to manage LDAP users and groups and for the management of mailing list I chose Mailman to be used with Postfix. As alternative I could have chosen Sympa but since Mailman is packaged already in many distribution and it doesn’t have many requirements like Sympa has, I preferred to go for Mailman. The drawback is that Mailman doesn’t come with LDAP support and I found a script which I adapted and uploaded on my github account.

To manage mailing list in OpenDJ you could opt for 3 ways:

  1. use the mailGroup object class
  2. use the groupOfUniqueNames object class together with the extensibleObject class
  3. customize your schema

OpenDJ has the schema definition of mailGroup coming from the Solaris system which allows  to enter the mail of the group and the mail of the users by using the attribute mgrpRFC822MailMember.

But…most of the time you will already have your users recorded in LDAP (using most probably the inetOrgPerson) therefore, instead of directly inserting users mail you would like to insert the DN of each user, in this way even PHPLdapAdmin allows you to insert easily each user without pasting email addresses.

Therefore I went for groupOfUniqueNames which allows me to define users with their DN and, by adding the extensibleObject class, I have the possibility to add the mail attribute.

In this way if I have already defined a group I can use it as well for mailing list without replicating information (e.g. mail address for each user with mailGroup).

After that I installed and configured Mailman and, as described above, I used a perl script which:

  1. extracts those groups that are groupOfUniqueNames and have a mail attribute set (&(objectClass=groupOfUniqueNames)(mail=*))
  2. extract all the users uniqueMember belonging to the group which have an mail address set and are not disabled (&(mail=*)(!(ds-pwp-account-disabled=*)))
  3. creates a mailing list which will have as name the CN group attribute (saved inside the variable $listname)

If you execute the script with the command:


you will get an output like:

Processing list: jenkins-users-mail
Found member: emidio
List jenkins-users-mail does not exist.
Creating new list jenkins-users-mail.

Syncing jenkins-users-mail...
Added :

enjoy creating mailing lists !


Export Crowd users and groups to ldap

I need to migrate users and groups created in Atlassian Crowd to OpenDJ, an open source LDAP server written in Java fork of OpenDS; in this way Crowd will act just as SSO and it will rely on OpenDJ to store users.

Crowd can rely on several LDAP servers but it doesn’t have tools to export users and groups to ldap so I created a couple of queries (my database is PostgreSQL) to export in ldif files:

COPY(select 'dn: cn=' || user_name || ',ou=users,dc=my,dc=website,dc=com' || chr(10) ||
'objectClass: inetOrgPerson' || chr(10) ||
'objectClass: organizationalPerson' || chr(10) ||
'objectClass: person' || chr(10) ||
'objectClass: top' || chr(10) ||
'mail: ' || lower_email_address || chr(10) ||
'uid: ' || user_name || chr(10) ||
'cn: ' || user_name || chr(10) ||
'givenName: ' || first_name || chr(10) ||
'sn: ' || last_name || chr(10) ||
'displayName: ' || display_name || chr(10) ||
'userPassword: ' || credential || chr(10) || chr(10)
from cwd_user) TO '/tmp/users.ldif';

Of course you can customize the query to adapt to your needs, have in mind that the userPassword most probably is stored in PKCS5S2 format (you can see that the passwords extracted start with {PKCS5S2} prefix, see Crowd hashes), which is the default for Crowd or called Atlassian-security. In this sense only ApacheDS currently (2.0.0-m16) supports the same format while for OpenDJ there is a change to be done to the pbkdf2 storage scheme. It is up to you if you prefer to reset passwords or try to keep them during the migration.

As small parenthesis I have been asking to those people of Crowd about their connector for ApacheDS, since their connector supports ApacheDS 1.5.x (not anymore maintained by ApacheDS developers) and they are not sure to spend time in updating such connector for version 2.0, see my question on Atlassian Answers. An interesting thing to know is that also WSO2 Identity Server has bundled ApacheDS 1.5.7, you can see it also in the source code but you can connect it to OpenDJ.

In case your Crowd was setup with SSHA1, you can also use Openldap, see FAQ.

For the groups the query would be something like:

COPY(select 'dn: cn=' || parent_name || 'BASE_GROUPS' || chr(10) ||
'cn: ' || parent_name || chr(10) ||
'objectClass: groupOfUniqueNames' || chr(10) ||
'objectClass: top' || chr(10) ||
'uniquemember:' array_to_string(array_agg(child_name),',')
from cwd_membership group by parent_name) TO '/tmp/groups.ldif';

As you can see I used the functions array_to_string and array_agg integrated in PostgreSQL to create the list of users. These users will not have their base (,ou=users,dc=my,dc=website,dc=com), so, after executing the query, you can apply some regular expression (I used Notepad++) to replace the regex “([,])” with “,ou=users,dc=my,dc=website,dc=com\nuniquemember: cn” which replaces all the “,” inserted with the query with their base. At the end, I replaced BASE_GROUPS with “,ou=groups,dc=my,dc=website,dc=com“.

You can import the ldif files with PHPLdapAdmin or directly in OpenDJ with import-ldif command, keep in mind that you need to allow pre encoded passwords before importing.

Enjoy the migration !




Nexus maven repository with Apache as proxy and OpenDJ for authentication

For a client I have recently setup a nexus (oss) maven repository with apache proxy/reverse proxy and OpenDJ to provide LDAP authentication.

Nexus, which competes with Artifactory as maven repository, by default is waiting on port 8081 with the context path /nexus, which means that you can connect to it to the address http://localhost:8081/nexus (admin/admin123)

Having defined the name of my machine as maven I first configured Apache as proxy pointing to Nexus in this way:

ProxyPass         /nexus http://maven:8081/nexus
ProxyPassReverse  /nexus http://maven:8081/nexus

Having configured Apache to accept only https connections to be forwarded to Nexus in http (so https–>Apache–>http–>Nexus), I need to add before the ProxyPass directive the following RequestHeader:

RequestHeader set X-Forwarded-Proto “https”

in this way, the Nexus zip version, which is bundled with the servlet container Jetty 8.1.11 (which from the version 7.2.0 understands x-forwarded-proto header, see also the commit related) it will reply with the connection option (forwarded=true), see below the default configuration (in /nexus/config/jetty.xml):

<Set name="host">${application-host}</Set>
<Set name="port>${application-port}</Set>
<Set name="forwarded">true</Set>

Now we add the ldap authentication, we go under Administration -> Server and add the OSS LDAP Authentication Realm to the Selected Realms.

I have OpenDJ listening on port 1389 (default), so we go under Security -> LDAP Configuration, we set the following parameters:

Protocol ldap
Hostname opendj
Port 1389
Search Base dc=my,dc=example,dc=com
Authentication method Simple Authentication
Username cn=admin
Password mypassword
User Element Mapping
Base DN ou=users
Object Class inetOrgPerson
User ID Attribute uid
Real Name Attribute cn
E-Mail Attribute mail
Group Element Mapping
Group type Dynamic groups
Member of Attribute isMemberOf

The last 2 lines are optional to Static groups, but since OpenDJ allows to use the isMemberOf attribute, I preferred to use Dynamic groups (see Nexus documentation). For static groups you can check out also this article based on OpenDS on which OpenDJ is based on.

Now if you click on the Check User Mapping button you should see the list of users and groups retrieved from OpenDJ.

The last step would be add roles to the user so we go under Security ->Roles -> Add…->External Role Mapping and we select the LDAP group which will act as nexus administrators and we add all the roles for the group. So we logout and then we login with the nexus ldap administrator.





Allow SSH users to change their passwords on OpenDJ

For a client I started to put in place a connection from ssh to OpenDJ, which I chose for previous reasons.

To setup ssh with PAM and LDAP you can follow several tutorials, the one I found more easy to follow is:

Depending on your security policy, you could need to allow ssh users to change their passwords with the passwd command, to do so the file /etc/pam.d/sshd has the following include:

# Standard Un*x password updating.
@include common-password

so a common configuration for the file common-password would be:

password        [success=2 default=ignore] obscure sha512
password        [success=1 user_unknown=ignore default=die] try_first_pass
password        requisite             
password        required              

As we can see when we change a password we check first the user in the system ( If the change succeed we skip the next 2 lines (success=2) and we allow the change ( If doesn’t succeed we check on ldap ( and if the operation succeed we skip the next line (success=1) and we allow password change (  If also the password change on ldap fails then we deny the password change (

Further, as we can see on PAM code, the pam-ldap package tries to update the attribute shadowLastChange, which

  • on unix system would correspond to the 3rd value in the /etc/shadow file  which is the number of days, (since January 1, 1970) since the password was last changed
  • on LDAP system, it is defined in the RFC 2307 as an attribute of the object shadowAccount, together with other values for password expiration, see brief description, similar to the /etc/shadow.

Therefore to allow PAM changing a password on OpenDJ we need to:

  • add the attribute shadowLastChange to the user, you can use PhpLdapAdmin to add the shadowAccount object class to your Posix users first.
  • add an ACI in OpenDJ to allow changing shadowLastChange to the binded user

You can learn how to create an ACI on OpenDJ here and also from the Oracle website.

Concerning the ACI for the password change, you could write in a ssh-shadow.ldif file something like:

dn: ou=People,dc=my,dc=domain,dc=com
changetype: modify
add: aci
aci: (targetattr = "shadowLastChange") (target = "ldap:///ou=People,dc=my,dc=domain,dc=com") (version 3.0; acl "change shadow"; allow (write) (userdn = "ldap:///self");)

where you allow the binded users (ldap:///self) to modify (write) the attribute shadowLastChange, which is similar to example reported on the OpendJ documentation to change own password.

Apply then the changes with the command:

./opendj/bin/ldapmodify -h myhost -p 1389 -D "cn=Directory Manager" -W -c -f ssh-shadow.ldif

Once the password is changed with passwd command, you can verify that the value is changed in OpenDJ with PhpLdapAdmin and also check online the correspondent date.

To delete the ACI just replace “add: aci” with “delete: aci” in the file and re-execute ldapmodify command.




16 reasons to choose OpenDJ

For a client I needed to put in place an LDAP system to be connected with Atlassian Crowd (SSO), after doing a comparison with OpenLDAP (C), Apache DS (Java) and OpenDJ (Java) I decided to go for OpenDJ for several reasons:

  1. It is open source released with CDDL 1.0 license; while Apache DS is released with Apache license and OpenLDAP with it is own license.
  2. it has worldwide commercial support, some here in Belgium; Apache DS has only one company and OpenLDAP has also it is worldwide support
  3. It is a fork of OpenDS like Oracle Unified Directory so you can still find common problems for  both products
  4. The project has a clear roadmap, also described in the wiki, also ApacheDS has it.
  5. The ForgeRock company provides also another SSO solution (OpenAM), so in case in a future I want to change SSO I can think to use it, while Apache DS no.
  6. It comes packaged as ZIP, RPM and DEB, while ApacheDS has a package for each Operating System and OpenLDAP is packaged in many Operating Systems.
  7. It has very good documentation with a lot of examples (released in EPUB, HTML, PDF and RTF), while I found the one from ApacheDS  simpler or in progress (HTML , PDF) and the one of OpenLDAP is good (released in HTML and PDF).
  8. It has a rich command line tool, while I found the command line of ApacheDS simpler (relying mostly on the Apache Directory Studio or on ldap client).
  9. It is shipped with a java client (not web) while Apache DS has a gui based on Eclipse (Directory Studio)
  10. it has REST API on top of the ldap protocol, while ApacheDS leaves to SCIM such job; therefore I hope there will be a web interface using the REST API.
  11. It supports replication out of the box, also ApacheDS has itOpenLDAP as well.
  12. It seems to have very good performance (almost at OpenLDAP level) at least in comparison to ApacheDS.
  13. It has a support for the virtual attribute IsMemberOf, useful to check which groups a user belongs to, while for ApacheDS the feature needs to be still implemented.
  14. It has an implementation of the PKCS5S2 (see PBKDF2) which is used by Crowd as default despite the implementation is  different from Crowd and ApacheDS, which OpenLDAP doesn’t have (and some other password schemes as well)
  15. The development team uses Continuous Integration practices (by testing against Ubuntu and Windows), also ApacheDS by using the common Apache Jenkins and testing on Ubuntu.
  16. The team has coding guidelines providing checkstyle rules, also ApacheDS has some guidelines and OpenLDAP has few

To test the performances, I did an LDAP jmeter script and published on Github, just to say that OpenDJ has it is own tool, and Tsung can also test LDAP server.

I have been working with my brother to change the PBKDF2 implementation so it is almost similar to Crowd and ApacheDS differing for the printing format of the iterations.

Enjoy using OpenDJ !