OpenLDAP pass-through auth to Active Directory

Yes, this is hysterically historical.  I’m keeping it here for safe keeping.

Control Tier Authentication and Authorization: Files, OpenLDAP, and Pass Through to Active Directory

in brief
You want to enable flexible authentication and authorization schemes for your Control Tier server.
*# Control Tier first checks the “fileRealm” files for usernames, passwords and roles.
*# On failure, Control Tier then checks against an OpenLDAP server which is setup to acts as a proxy for the corporate Active Directory, while also providing it’s own branches for Control Tier roles (and any other apps.)

 

Control Tier --> files -> OpenLDAP --> Active Directory
(users (roles) (users)
and
roles)
 ou=roles,dc=corp,dc=example,dc=net <-- OpenLDAP
ou=people,dc=corp,dc=example,dc=net <-- Active Directory

;in long
Most corporations have Active Directory user directories. Oftentimes, operations does not have the ability to add and remove groups and group membership from the Active Directory LDAP server. Or perhaps the business process of requesting these changes are onerous, and cannot be relied upon in an emergency situation.

Operations might be tempted to create their own directory services for their infrastructure to reference. This is generally not recommended, because that would likely lead to duplication of user identities, and not increase the number of credentials that users are required to keep safe.

Instead of creating their own user population, operations can honor the user authentication systems of the corporate directory via an LDAP proxy, and still manage their own groups on their own directory server. Operations would configure an OpenLDAP server to proxy some branches of the Active Directory to manage authentication. The OpenLDAP would also host other suffixes locally, which might include groups and roles branches, hosts branches, and application specific branches. This would allow authentication and authorization in one LDAP call, across the Active Directory and OpenLDAP servers.

In a fashion similiar to PAM and NSSWITCH, Control Tier can query more than one source of user and role information. In the configuration I’m proposing here, the files based configuration takes precedence. Only after files based authentication and authorization fails does Control Tier query the LDAP/AD based authentication and authorization services.

Assumptions

  • Existing Microsoft Active Directory LDAP server available.
    • AD server has a root of <code>dc=corp,dc=example,dc=com</code>
    • AD Server has two user populations:
      • Normal People Accounts for operator access to ctier <code>ou=people,dc=corp,dc=example,dc=com</code>
      • Secondary accounts (service accounts) stored in <code>ou=Secondary Accounts,dc=corp,dc=example,dc=com</code>
      • RHEL 5.4 or CentOS 5.4 with root access

      • Control Tier 3.6 server setup and running in default configuration.

== How to set it all up ==

We’ll be using an OpenLDAP directory server. It will use the slap-ldap backend to proxy authentication to the Active Directory server. Some objectclass and attribute rewrites are needed, because OpenLDAP does not have the same schema as Active Directory, and it is inadvisable to add those objectclasses and attributes to the OpenLDAP directory.

=== OS and tools ===

* Install latest OpenLDAP you can find on Centos5.4 or RHEL5.4
wget http://tools.ltb-project.org/attachments/download/161/berkeleydb-ltb-4.6.21.NC-4.patch4.x86_64.rpm
wget http://tools.ltb-project.org/attachments/download/163/openldap-ltb-2.4.23-1.el5.x86_64.rpm
rpm -i berkeleydb-ltb-4.6.21.NC-4.patch4.x86_64.rpm
rpm -i openldap-ltb-2.4.23-1.el5.x86_64.rpm
yum install libtool-ltdl.x86_64

=== SLAPD Configuration ===

* Add some more schema – provided in the distro

;<code>/usr/local/openldap/etc/slapd.conf</code>

include /usr/local/openldap/etc/openldap/schema/cosine.schema

* Create the backend to proxy the branches that stored on the AD. Note also that the order of the backends is important, vis-a-vis their suffixes. The rule is, the longer the suffix, the firster it has to appear in slapd.conf

;<code>/usr/local/openldap/etc/slapd.conf</code>

database ldap
suffix "OU=Secondary Accounts,DC=corp,DC=example,DC=net"
suffix "ou=people,DC=corp,DC=example,DC=net"
rootdn "cn=slapd-ldap"
uri ldap://activedirectory.corp.example.net/
# This controls what attribs can be accessed by the LDAP proxy.
# The last rwm-map line maps all other attributes to nothing.
overlay rwm
rwm-map objectclass account user
rwm-map attribute uid sAMAccountname
rwm-map attribute cn name
rwm-map attribute sn sn
rwm-map attribute mail mail
rwm-map attribute company company
rwm-map attribute entry entry
rwm-map attribute userPassword unicodePassword
rwm-map attribute *

* Create the backend to hold all the data local to the OpenLDAP server

;<code>/usr/local/openldap/etc/slapd.conf</code>

database bdb
suffix "dc=corp,dc=example,dc=net"
rootdn "cn=manager,dc=corp,dc=example,dc=net"
rootpw secret1
# Cleartext passwords, especially for the rootdn, should
# be avoid. See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
directory /usr/local/openldap/var/openldap-data
# Indices to maintain
index objectClass eq

* Restart the OpenLDAP server to make sure the config works.

/etc/init.d/slapd restart

* Shut down the OpenLDAP server to enable loading the following sample LDIF file. Edit to taste.

; Important Note: The DN naming of the role can be prefixed, as shown here and accounted for below in <code>rolePrefix</code> setting in the <code>/opt/ctier/pkgs/jetty-6.1.21/etc/file-loginModule.conf</code> ”’however you must include an UNPREFIXED version of your role name in the attribute.”’

;<code>/tmp/devops.ldif</code>

<pre>
dn: dc=corp,dc=example,dc=net
objectClass: dcObject
objectClass: organization
o: Example, Inc.
dc: corp

dn: ou=devops,dc=corp,dc=example,dc=net
objectClass: organizationalUnit
ou: devops

# Define an entry to contain roles:
dn: ou=users, ou=devops,dc=corp,dc=example,dc=net
objectClass: organizationalUnit
ou: users

# Define some users and their membership:
dn: cn=default,ou=users,ou=devops,dc=corp,dc=example,dc=net
objectClass: organizationalPerson
cn: default
sn: default

dn: cn=deploy,ou=users,ou=devops,dc=corp,dc=example,dc=net
objectClass: organizationalPerson
cn: deploy
sn: deploy

dn: cn=build,ou=users,ou=devops,dc=corp,dc=example,dc=net
objectClass: organizationalPerson
cn: build
sn: build

# Define an entry to contain roles:
dn: ou=roles, ou=devops,dc=corp,dc=example,dc=net
objectClass: organizationalUnit
ou: roles

# Define some roles and their membership:
dn: cn=ctier.architect, ou=roles,ou=devops,dc=corp,dc=example,dc=net
objectClass: groupOfUniqueNames
uniqueMember: cn=Maltin\2C Judd,ou=People,dc=corp,dc=example,dc=net
cn: ctier.architect
cn: architect

dn: cn=ctier.admin, ou=roles,ou=devops,dc=corp,dc=example,dc=net
objectClass: groupOfUniqueNames
uniqueMember: cn=default,ou=users,ou=devops,dc=corp,dc=example,dc=net
uniqueMember: cn=Maltin\2C Judd,ou=People,dc=corp,dc=example,dc=net
cn: ctier.admin
cn: admin

dn: cn=ctier.user, ou=roles,ou=devops,dc=corp,dc=example,dc=net
objectClass: groupOfUniqueNames
uniqueMember: cn=default,ou=users,ou=devops,dc=corp,dc=example,dc=net
uniqueMember: cn=deploy,ou=users,ou=devops,dc=corp,dc=example,dc=net
uniqueMember: cn=build,ou=users,ou=devops,dc=corp,dc=example,dc=net
cn: ctier.user
cn: user

dn: cn=ctier.build, ou=roles,ou=devops,dc=corp,dc=example,dc=net
objectClass: groupOfUniqueNames
uniqueMember: cn=default,ou=users,ou=group,dc=corp,dc=example,dc=net
uniqueMember: cn=build,ou=users,ou=group,dc=corp,dc=example,dc=net
cn: build
cn: ctier.build

dn: cn=ctier.deploy, ou=roles,ou=devops,dc=corp,dc=example,dc=net
objectClass: groupOfUniqueNames
uniqueMember: cn=default,ou=users,ou=group,dc=corp,dc=example,dc=net
uniqueMember: cn=deploy,ou=users,ou=group,dc=corp,dc=example,dc=net
cn: ctier.deploy
cn: deploy

</pre>

* Load LDIF file into OpenLDAP

/usr/local/openldap/sbin/slapadd -l /tmp/devops.ldif

* Test the pass-through authentication with an LDAP Search on the corporate database

<pre>
ldapsearch -x -v -D “CN=DevOpsService,OU=Secondary Accounts,DC=corp,DC=example,DC=net” -W \
-b “ou=people,dc=corp,dc=example,dc=net” \
-H ldap://openldap.corp.example.net'(uid=*)’ uid
</pre>

== Setup Multiple Authentication Points (PAM for ControlTier) ==

In the controltier startup scripts there are a series of configuration settings.

;<code>/etc/default/ctier</code>

<pre>

CONFIG_PROPS=”-Dctlcenter.config.location=/opt/ctier/ctlcenter/ctlcenter-config.properties -Djava.security.auth.login.config=/opt/ctier/pkgs/jetty-6.1.21/etc/file-loginModule.conf “

</pre>

That file, <code> /opt/ctier/pkgs/jetty-6.1.21/etc/file-loginModule.conf </code> should have the LDAP setup ADDED to it… as follows:

; IMPORTANT: as noted above in the LDIF: if you use the <code>rolePrefix</code> your RDN must show the naming attribute (example “<code>dn: cn=ctier.admin,ou=roles,ou=devops,dc=corp,dc=example,dc=net</code>”) prefixed, while there ”’MUST”’ also be an attribute value for the unprefixed role name (example “<code>cn: admin</code>”)

;<code>/opt/ctier/pkgs/jetty-6.1.21/etc/file-loginModule.conf</code>

; note the userIdAttribute value has a ~ to make the search insensitive.

<pre>
ctrealm {
org.mortbay.jetty.plus.jaas.spi.PropertyFileLoginModule sufficient
debug=”true”
file=”${jetty.home}/etc/realm.properties”;

com.controltier.ctl.webad.jaas.JettyCachingLdapLoginModule sufficient

debug=”true”
contextFactory=”com.sun.jndi.ldap.LdapCtxFactory”
hostname=”localhost”
port=”389″
bindDn=”CN=DevOpsService,OU=Secondary Accounts,DC=corp,DC=example,DC=net”
bindPassword=”SECRETSECRETSECRET”
authenticationMethod=”simple”
forceBindingLogin=”true”
userBaseDn=”ou=people, DC=corp, DC=example, DC=net”
userRdnAttribute=”cn”
userIdAttribute=”uid~”
userPasswordAttribute=”unicodePassword”
userObjectClass=”organizationalperson”
roleBaseDn=”ou=roles,ou=devops,DC=corp, DC=example, DC=net”
roleNameAttribute=”cn”
roleMemberAttribute=”uniqueMember”
roleObjectClass=”groupOfUniqueNames”
rolePrefix=”ctier.” ;
};
</pre>

Note that in the above, the <code>PropertyFileLoginModule</code> comes first, with a condition of <code>sufficient</code> which means that if the credentials and roles are found there, then use them and quit looking for more options. Otherwise, try the next one, which is <code>JettyCachingLdapLoginModule</code>. That one has our LDAP servers settings, and some Active Directory LDAP attributes listed, especially “unicodePassword”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s