Tuesday, July 27, 2010

Configuring OpenLDAP as a replacement for NIS

Here's a step-by-step tutorial on installing OpenLDAP on a Red Hat Linux system and configuring it as a replacement for NIS. In a future blog post I intend to cover the python-ldap package.

Install OpenLDAP

# tar xvfz openldap-stable-20050429.tgz
# cd openldap-2.2.26
# ./configure
# make
# make install

Configure and run the OpenLDAP server process slapd
  • In what follows, the LDAP domain is 'myldap'
  • Change the slapd root password:
[root@myhost openldap]# slappasswd
New password:
Re-enter new password:
{SSHA}dYjrA1-JukrfESe/8b1HdZWfcToVE/cC
  • Edit /usr/local/etc/openldap/slapd.conf
    • Change my-domain to myldap
    • Point 'directory' entry to /usr/local/var/openldap-data/myldap
    • Point 'rootpw' entry to line obtained via slappasswd: 'rootpw {SSHA}dYjrA1-JukrfESe/8b1HdZWfcToVE/cC'
    • Add following lines after 'include /usr/local/etc/openldap/schema/core.schema' line:
include         /usr/local/etc/openldap/schema/cosine.schema
include         /usr/local/etc/openldap/schema/inetorgperson.schema
include         /usr/local/etc/openldap/schema/misc.schema
include         /usr/local/etc/openldap/schema/nis.schema
include         /usr/local/etc/openldap/schema/openldap.schema
  • Create data directory:
mkdir /usr/local/var/openldap-data/myldap
  • Start up slapd server:
/usr/local/libexec/slapd
  • Test slapd by running an ldap search:
# ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts
# extended LDIF
#
# LDAPv3
# base <> with scope base
# filter: (objectclass=*)
# requesting: namingContexts
#
#
dn:
namingContexts: dc=myldap,dc=com
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1

Populate the LDAP database
  • Create /usr/local/var/openldap-data/myldap/myldap.ldif LDIF file:
dn: dc=myldap,dc=com
objectclass: dcObject
objectclass: organization
o: My LDAP Domain
dc: myldap
dn: cn=Manager,dc=myldap,dc=com
objectclass: organizationalRole
cn: Manager
  • Add LDIF contents to the LDAP database via ldapadd:
# ldapadd -x -D "cn=Manager,dc=myldap,dc=com" -W -f myldap.ldif
Enter LDAP Password:
adding new entry "dc=myldap,dc=com"
adding new entry "cn=Manager,dc=myldap,dc=com"
  • Verify that the entries were added by doing an LDAP search:
[root@myhost myldap]# ldapsearch -x -b 'dc=myldap,dc=com' '(objectclass=*)'
# extended LDIF
#
# LDAPv3
# base  with scope sub
# filter: (objectclass=*)
# requesting: ALL
#
# myldap.com
dn: dc=myldap,dc=com
objectClass: dcObject
objectClass: organization
o: My LDAP Domain
dc: myldap
# Manager, myldap.com
dn: cn=Manager,dc=myldap,dc=com
objectClass: organizationalRole
cn: Manager
# search result
search: 2
result: 0 Success
# numResponses: 3
# numEntries: 2
  • Sample LDIF file with organizational unit info: /usr/local/var/openldap-data/myldap/myldap_ou.ldif
dn: ou=Sales,dc=myldap,dc=com
ou: Sales
objectClass: top
objectClass: organizationalUnit
description: Members of Sales
dn: ou=Engineering,dc=myldap,dc=com
ou: Engineering
objectClass: top
objectClass: organizationalUnit
description: Members of Engineering
  • Add contents of LDIF file to LDAP database via ldapadd:
[root@myhost myldap]# ldapadd -x -D "cn=Manager,dc=myldap,dc=com" -W -f myldap_ou.ldif
Enter LDAP Password:
adding new entry "ou=Sales,dc=myldap,dc=com"
adding new entry "ou=Engineering,dc=myldap,dc=com"
  • Sample LDIF file with user info: /usr/local/var/openldap-data/myldap/myldap_user.ldif
dn: cn=Larry Fine,ou=Sales,dc=myldap,dc=com
ou: Sales
o: myldap
cn: Larry Fine
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
mail: LFine@myldap.com
givenname: Larry
sn: Fine
uid: larry
homePostalAddress: 15 Cherry Ln.$Plano TX 78888
postalAddress: 215 Fitzhugh Ave.
l: Dallas
st: TX
postalcode: 75226
telephoneNumber: (800)555-1212
homePhone: 800-555-1313
facsimileTelephoneNumber: 800-555-1414
userPassword: larrysecret
title: Account Executive
destinationindicator: /bios/images/lfine.jpg
dn: cn=Moe Howard,ou=Sales,dc=myldap,dc=com
ou: Sales
o: myldap
cn: Moe Howard
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
mail: MHoward@myldap.com
givenname: Moe
sn: Howard
uid: moe
initials: Bob
homePostalAddress: 16 Cherry Ln.$Plano TX 78888
postalAddress: 216 South Fitzhugh Ave.
l: Dallas
st: TX
postalcode: 75226
pager: 800-555-1319
homePhone: 800-555-1313
telephoneNumber: (800)555-1213
mobile: 800-555-1318
title: Manager of Product Development
facsimileTelephoneNumber: 800-555-3318
manager: cn=Larry Howard,ou=Sales,dc=myldap,dc=com
userPassword: moesecret
destinationindicator: /bios/images/mhoward.jpg
dn: cn=Curley Howard,ou=Engineering,dc=myldap,dc=com
ou: Engineering
o: myldap
cn: Curley Howard
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
mail: CHoward@myldap.com
givenname: Curley
sn: Howard
uid: curley
initials: Joe
homePostalAddress: 14 Cherry Ln.$Plano TX 78888
postalAddress: 2908 Greenville Ave.
l: Dallas
st: TX
postalcode: 75206
pager: 800-555-1319
homePhone: 800-555-1313
telephoneNumber: (800)555-1214
mobile: 800-555-1318
title: Development Engineer
facsimileTelephoneNumber: 800-555-3318
userPassword: curleysecret
destinationindicator: /bios/images/choward.jpg
  • Add contents of LDIF file to LDAP database via ldapadd:
[root@myhost myldap]# ldapadd -x -D "cn=Manager,dc=myldap,dc=com" -W -f myldap_users.ldif
Enter LDAP Password:
adding new entry "cn=Larry Fine,ou=Sales,dc=myldap,dc=com"
adding new entry "cn=Moe Howard,ou=Sales,dc=myldap,dc=com"
adding new entry "cn=Curley Howard,ou=Engineering,dc=myldap,dc=com"
  • Verify entries were added by doing an LDAP search:
[root@myhost myldap]# ldapsearch -x -b 'dc=myldap,dc=com' '(objectclass=*)'
  • Search output should end with:
# search result
search: 2
result: 0 Success
# numResponses: 8
# numEntries: 7

Replace NIS with LDAP
Generate ldif files from /etc/passwd and /etc/group and add them to the LDAP database
  • Generate ldif file for creating 'people' and 'group' organizational units:
  • Edit /usr/local/var/openldap-data/myldap/myldap_people.ldif:
dn: ou=people,dc=myldap,dc=com
objectclass: organizationalUnit
ou: people
dn: ou=group,dc=myldap,dc=com
objectclass: organizationalUnit
ou: group
  • Insert contents of myldap_people.ldif in LDAP database:
# ldapadd -x -D "cn=Manager,dc=myldap,dc=com" -W -f myldap_people.ldif
# tar xvfz MigrationTools.tgz
# cd MigrationTools-46/
$DEFAULT_MAIL_DOMAIN = "myldap.com";
$DEFAULT_BASE = "dc=myldap,dc=com";
$DEFAULT_MAIL_HOST = "mail.myldap.com";
  • Generate passwd.ldif and group.ldif files:
[root@myhost MigrationTools-46]# ./migrate_passwd.pl /etc/passwd /usr/local/var/openldap-data/myldap/myldap_passwd.ldif
[root@myhost MigrationTools-46]# ./migrate_group.pl /etc/group /usr/local/var/openldap-data/myldap/myldap_group.ldif
  • Insert contents of myldap_passwd.ldif and myldap_group.ldif in LDAP database:
# ldapadd -x -D "cn=Manager,dc=myldap,dc=com" -W -f myldap_passwd.ldif
# ldapadd -x -D "cn=Manager,dc=myldap,dc=com" -W -f myldap_group.ldif
Install the pam_ldap and nss_ldap modules
# tar xvfz pam_ldap.tgz
# cd pam_ldap-180
# ./configure
# make
# make install
  • Install nss_ldap.tgz
# tar xvfz nss_ldap.tgz
# cd nss_ldap-243/
# ./configure --enable-rfc2307bis
# make
# make install
  • (See NOTE below before doing this) Edit /etc/ldap.conf (note that there's also /etc/openldap/ldap.conf; you need the one in /etc) and specify the following settings:
base dc=myldap,dc=com
scope sub
timelimit 30
pam_filter objectclass=posixAccount
nss_base_passwd ou=People,dc=myldap,dc=com?one
nss_base_shadow ou=People,dc=myldap,dc=com?one
nss_base_group  ou=Group,dc=myldap,dc=com?one
  • (See NOTE below before doing this) Edit /etc/nsswitch.conf and specify:
passwd:     files ldap
shadow:     files ldap
group:      files ldap
NOTE: Instead of manually modifying /etc/ldap/conf and /etc/nsswitch.conf, you should run the authconfig utility and specify the LDAP server IP and the LDAP base DN ('dc=myldap,dc=com' in our example). authconfig will automatically modify /etc/ldap.conf (minus the nss_base entries), /etc/nsswitch.conf and also /etc/pam.d/system-auth. This is how /etc/pam.d/system-auth looks on a RHEL 4 system after running authconfig:
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      /lib/security/$ISA/pam_env.so
auth        sufficient    /lib/security/$ISA/pam_unix.so likeauth nullok
auth        sufficient    /lib/security/$ISA/pam_ldap.so use_first_pass
auth        required      /lib/security/$ISA/pam_deny.so
account     required      /lib/security/$ISA/pam_unix.so broken_shadow
account     sufficient    /lib/security/$ISA/pam_succeed_if.so uid < default="bad" success="ok" user_unknown="ignore]" retry="3">
Test the LDAP installation with an LDAP-only user

  • Add new user in LDAP database which doesn't exist in /etc/passwd; create /usr/local/var/openldap-data/myldap/myldap_myuser.ldif file:
dn: uid=myuser,ou=People,dc=myldap,dc=com
uid: myuser
cn: myuser
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword: secret
shadowLastChange: 13063
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 500
gidNumber: 500
homeDirectory: /home/myuser
dn: cn=myuser,ou=Group,dc=myldap,dc=com
objectClass: posixGroup
objectClass: top
cn: myuser
userPassword: {crypt}x
gidNumber: 500
  • Add contents of the myldap_myuser.ldif file to LDAP database via ldapadd:
# ldapadd -x -D "cn=Manager,dc=myldap,dc=com" -W -f myldap_myuser.ldif
  • Create /home/myuser directory and change permissions:
# mkdir /home/myuser
# chown myuser.myuser myuser
  • Change the password for user 'myuser' via ldappasswd:
ldappasswd -x -D "cn=Manager,dc=myldap,dc=com" -W -S "uid=myuser,ou=People,dc=myldap,dc=com"
  • Log in from a remote system via ssh as user myuser; everything should work fine
Adding another host to the myldap LDAP domain
  • On any client machine that you want to join the myldap LDAP domain
    • Make sure the OpenLDAP client package is installed (from source or RPM)
    • Install the nss_ldap and pam_ldap packages
    • Run authconfig and indicate the LDAP server and the LDAP base DN
    • In a terminal console, try to su as user myuser (which doesn't exist locally); it should work
      • To avoid the "home directory not found" message, you'll also need to NFS-mount the home directory of user myuser from the LDAP server
    • Restart sshd and try to ssh from a remote machine as user myuser; it should work (it didn't work in my case until I restarted sshd)

Various notes
  • At this point, you can maintain a central repository of user accounts by adding/deleting/modifying them on the LDAP server machine via various LDAP client utilities such as ldapadd/ldapdelete/ldapmodify
    • For example, to delete user myuser and group myuser, you can run:
# ldapdelete -x -D "cn=Manager,dc=myldap,dc=com" -W 'uid=myuser,ou=People,dc=myldap,dc=com'
# ldapdelete -x -D "cn=Manager,dc=myldap,dc=com" -W 'cn=myuser,ou=Group,dc=myldap,dc=com'
  • I experimented with various ACL entries in slapd.conf in order to allow users to change their own passwords via 'passwd'; however, I was unable to find the proper ACL incantations for doing this (if anybody has a recipe for this, please leave a comment)
  • To properly secure the LDAP communication between clients and the LDAP server, you should enable SSL/TLS (see this HOWTO)
Here are some links I found very useful:

OpenLDAP Quick Start Guide
YoLinux LDAP Tutorial
Linux LDAP Authentication article at linux.com
LDAP for Rocket Scientists -- Open Source guide at zytrax.com
Paranoid Penguin - Authenticate with LDAP part III at linuxjournal.com
Turn your world LDAP-tastic -- blog entry by Ed Dumbill