LDAP Deployment on CentOS, with AutoFS and Posix Schema

Introduction

I recently built an ESX lab and wanted central authentication. I’m a linux guy, so AD would be of minimal utility. Instead, I decided to dedicate one of my spare PCs as DNS, DHCP, LDAP, and SysLog. I decided to conquer LDAP first. It turns out to be kind of tricky.

  • The openldap packages will get the server up, but you wont’ have the necessary base to add users / groups.
  • Another goal is automounted NFS home directories – the Centos6 RPMs don’t have the autofs.schema file that makes this possible.
  • All of the docs you’ll read will talk about modifying slapd.conf; meanwhile, the latest versions of openldap have deprecated slapd.conf in favor of slapd.d.

At it’s core, I did the following:

  1. Install the openldap packages from centos repo
  2. Acquired the migrationtools to create the base.ldif
    1. The migrationtools are made by the same folks that make the nss-pam-ldap stuff, so I figured this was the best way to get a compliant directory structure.
  3. Acquired the autofs.schema file from the CentOS 5 repo, it has the necessary file.
    1. You’ll still need to build the necessary entries for autofs in the LDAP directory, I followed this guy’s guide.
  4. I used phpLDAPadmin to browse the directory and add entries manually.
    1. You’ll need to modify selinux to get this thing working right in apache.
    2. I’ve heard Apache Directory Studio is also pretty nice, but I haven’t checked it out.

Update 11/21: Trying to get a slave set up and I noticed that my config doesn’t allow you to interact with the cn=config DIT. Updated that section.

Let’s begin, shall we?


This document was converted from Confluence Markup. You may see residual formatting errors – I’m working through them as best I can.


Prep work: Generate a CA cert and a server cert.

Follow this guide, starting at step 1B to generate the CA cert. You’ll need to put this on the clients later.


Don’t put a passphrase on the server.key otherwise slapd will need it to encrypt things – the problem is that it won’t know how to ask.


If you did it right, you should have 5 files (although you don’t need the .csr after you have the .crt)

  • ldapdev.example.com.ca.crt
  • ldapdev.example.com.ca.key
  • ldapdev.example.com.server.crt
  • ldapdev.example.com.server.csr
  • ldapdev.example.com.server.key

The *.ca.crt and *.ca.key are the certificate authority files. You’ll need the .crt on all your clients. The .key will be needed if you want to certify another server, so put that somewhere safe.

The *.server.crt and *.server.key belong in /etc/openldap/cacerts and are used to provide the encryption.


The server’s private key is sensitive data and must only be readable by the user that slapd runs as, so the permissions are explicitly restricted on the key file above. The cert files should be publically readable.


Get the daemon running without anything fancy in there

I used the OpenLDAP.org Admin Guide and the Quick Start Guide, with a little deviation. The quick start guide has you (still) modifying and configuring everything via slapd.conf. So, we’ll do what they tell us, then we’ll port the slapd.conf over to slapd.d using slaptest (later).

  1. Install the necessary rpms
    yum install openldap-servers openldap-clients nss-pam-ldapd migrationtools 
    
    • openldap-servers contains the daemon and configs, almost everything you need to set up the service
    • openldap-clients are the CLI utilities for interacting with the server. You’ll need these later when we’re setting up the directory structure and schemas.
    • nss-pam-ldapd is the plugin that lets the local box call an LDAP box for auth. You don’t need this if you don’t want to use LDAP for client auth on the server.
    • The MigrationTools is a set of perl scripts that help you turn local authentication setups into ldap setups.
  2. Acquire some extra files that we’ll need.
    wget http://bay.uchicago.edu/centos/5/os/x86_64/CentOS/openldap-servers-2.3.43-25.el5.x86_64.rpm 
    
    1. The openldap-servers-2.3.43-25.el5.x86_64.rpm contains two files we need.
      • slapd.conf
      • autofs.schema
    2. You can use rpm2cpio ./openldap-servers-2.3.43*.rpm | cpio -idv to extract the rpm into a set of directories (without installing the rpm)
    3. See TK [the child page] for these files if you can’t find them.
  3. Toss autofs.schema with the others in /etc/openldap/schema/.
  4. Now we’re ready to modify slapd.conf and get this party started. Here are the main things to change:
    1. Up at the top, add the autofs.schema in there with the other includes:
      include /etc/openldap/schema/core.schema 
      include /etc/openldap/schema/cosine.schema 
      include /etc/openldap/schema/inetorgperson.schema 
      include /etc/openldap/schema/nis.schema 
      include /etc/openldap/schema/autofs.schema 
      
    2. I don’t like that “allow bind_v2″ option. Couldn’t tell you why. Comment that nasty out.
      # Allow LDAPv2 client connections. This is NOT the default. 
      #allow bind_v2 
      
    3. Lines 51-53 concern the TLS certs. I put the server key/cert in ldap’s home dir, /var/lib/ldap, and modified the permissions to root:ldap // 640. Then I put the ca-cert (world readable) in /etc/openldap/cacerts/.
      TLSCACertificateFile /etc/openldap/cacerts/ldapdev.example.com.ca.crt
      TLSCertificateFile /var/lib/ldap/ldapdev.example.com.server.crt 
      TLSCertificateKeyFile /var/lib/ldap/ldapdev.example.com.server.key 
      
    4. Lines 86-92 are the meat and potatoes of why we came in here.
      database bdb 
      suffix "dc=my-domain,dc=com"
      #suffix "dc=eng,dc=uni,dc=edu,dc=eu" 
      rootdn "cn=Manager,dc=my-domain,dc=com" 
      # Cleartext passwords, especially for the rootdn, should 
      # be avoided. See slappasswd(8) and slapd.conf(5) for details. 
      # Use of strong authentication encouraged. 
      # rootpw secret 
      # rootpw {crypt}sDIOPfJmmKGrt
      
      1. The database line is good – bdb is the preferred backend.
      2. The suffix will need to change – the example is for my-domain.com. I added in an example showing eng.uni.edu.eu for reference.
      3. You’ll also need to edit the rootdn – manager is typical, as is admin. Just remember what you pick here, you’ll need it when you need to interact with the directory for some activities.
      4. You’ll need to set the rootpw. Use the slappasswd utility to generate something, then copy the output into slapd.conf for the rootpw.
        [root@ldapdev ldaptest]# slappasswd 
        New password: 
        Re-enter new password: 
        {SSHA}SiopjfmNDDnfmRjU3msFjKLzNxWEer3t 
        
    5. If you want to be able to modify the config while the DB is online, which is the whole “feature” of the slapd.d config model, you need to add this above the first database definition – in our case, that’s line 85.
      database config
      rootdn "cn=Manager,cn=config"
      rootpw secret
      

      Set the rootpw here with slappasswd just as you did above.

  5. Slaptest will bark if there isn’t a DB_CONFIG file in /var/lib/ldap. Lets get that out of the way:
    cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG 
    chown ldap:ldap /var/lib/ldap/DB_CONFIG 
    chmod 600 /var/lib/ldap/DB_CONFIG 
    vi /var/lib/ldap/DB_CONFIG #Delete all the comments in this file. It makes the resulting output tidier/easier to read/maintain later. 
    
  6. Cool. The new config is ready, and our schemas are in place. Lets get the garbage that was installed by yum out of the way.
    cp -av /etc/openldap /etc/openldap.orig 
    cp -av /var/lib/ldap /var/lib/ldap.orig #This isn't necessary if you don't have an existing ldap configuration or if you've never started slapd. 
    rm -rf /etc/openldap/slapd.d 
    mkdir /etc/openldap/slapd.d && chown ldap:ldap /etc/openldap/slapd.d 
    
  7. Now the juke – we run slaptest to convert the slapd.conf into the new slapd.d format. This is what success looks like:
    [root@ldapdev ldapinstall]# slaptest -f ./slapd.conf -F /etc/openldap/slapd.d/ 
    bdb_db_open: database "dc=example,dc=com": db_open(/var/lib/ldap/id2entry.bdb) failed: No such file or directory (2). 
    backend_startup_one (type=bdb, suffix="dc=example,dc=com"): bi_db_open failed! (2) 
    slap_startup failed (test would succeed using the -u switch) 
    
  8. The new directory will be owned by root, and that will gum things up. Change the ownership to ldap:ldap
    chown -Rv ldap:ldap /var/lib/ldap /etc/openldap/slapd.d 
    
  9. You may now start the daemon.
    service slapd start 
    
  10. You can verify that the service is running with ldapsearch. We don’t have much to search through so far, so lets just look at the namingContexts. Note that it comes back with dc=example,dc=com. If there’s nothing under the “dn:” line, or you have other errors, go back and see what you missed above.
    [root@ldapdev2 ldapinstall]# ldapsearch -ZZ -x -b '' -s base '(objectclass=*)' namingContexts ## The -ZZ forces TLS. 
    # extended LDIF 
    # 
    # LDAPv3 
    # base <> with scope baseObject 
    # filter: (objectclass=*) 
    # requesting: namingContexts 
    # 
    
    # 
    dn: 
    namingContexts: dc=example,dc=com 
    
    # search result 
    search: 2 
    result: 0 Success 
    
    # numResponses: 2 
    # numEntries: 1 
    
  11. Next we need to modify the ACLs to increase security. It doesn’t appear that slaptest will pull the ACLs from slapd.conf, so we set these by hand.
    1. Open up the database config object in the cn=config directory.
      vi "/etc/openldap/slapd.d/cn=config/olcDatabase={1}bdb.ldif" 
      
    2. Add the following lines
      olcAccess: to attrs=userPassword by self write by anonymous auth by dn="uid=proxyuser,ou=People,dc=example,dc=com" read by * none 
      olcAccess: to attrs=shadowLastChange by self write by dn="uid=proxyuser,ou=People,dc=example,dc=com" read by * none 
      olcAccess: to * by dn="uid=proxyuser,ou=People,dc=example,dc=com" read by * none 
      
      • This sets us up for using a “proxy user” to handle the LDAP reads. You wouldn’t want a rogue client to be able to perform full reads. If you don’t want to use the proxy user, change the following:
        olcAccess: to attrs=userPassword by self write by anonymous auth by users read by * none 
        olcAccess: to attrs=shadowLastChange by self write by users read by * none 
        olcAccess: to * by * read 
        
  12. Enable ACL level logging:
    1. Open up /etc/openldap/slapd.d/cn=config.ldif and add the following line, right under the TLS definitions (lines 27-30):
      olcLoglevel: 128 
      
    2. This enables logging at the slapd level, but we need to tell syslog to actual commit these entries to file. Open up /etc/rsyslog.conf and add this above the block o’ comments at the bottom:
      # Save slapd messages 
      local4.* /var/log/slapd.log 
      
  13. Open up the local firewall for port 389:
    -A INPUT -m state --state NEW,ESTABLISHED -m tcp -p tcp -s 192.168.2.0/24 --dport 389 -j ACCEPT 
    
    • If you don’t want to restrict the source IP range, omit the -s 192.168.2.0/24 portion.

Next up: Creating a directory base that’s compliant with the nss-pam-ldapd / pam_ldap modules.

Creating the directory base

Technically, you can store all sorts of goofy stuff in an LDAP directory and configure access to different parts of it based on who is asking and how they’re authenticated. It’s meant to be flexible for use with many applications. However, most of us just want to use it to have centralized authentication of Linux users. A nice little bonus is being able to have your home directory on an nfs share, and have that directory automounted anywhere you login. Right now, lets focus on creating a directory structure that can be used for authentication. Remember those migrationtools you installed?

  1. First off, we have to modify migrate_common.ph.
    [root@ldapdev2 migrationtools]# vi /usr/share/migrationtools/migrate_common.ph 
    
    • Line 71 configures the mail domain. The comment says “dns domain”, which is a little different. In my environment, these are the same (hosts are hostname.domain.com, email is address@domain.com). If your email address was @mail.example.com, this might need tweeking. Otherwise, just make it example.com.
      # Default DNS domain 
      $DEFAULT_MAIL_DOMAIN = "padl.com"; 
      
    • Line 74 configures the default base. This is the part of the directory that the migration tools will be accessing. Had you created some sort of structure already, you could use this to only modify part of your directory. We’re using it to create the base, and we’re not creating any sort of referral system.
      # Default base 
      $DEFAULT_BASE = "dc=padl,dc=com"; 
      
  2. Next, we’ll use migrate_base.pl to create base.ldif, which we’ll then import.
    /usr/share/migrationtools/migrate_base.pl > ~/randotemp/base.ldif 
    
    • You can trim this output LDIF file down if you’d like, the only OUs i’ve used so far are People, Group and Mount.
  3. So now we import it:
    [root@ldapdev2]# ldapadd -h localhost -x -W -D "cn=Manager,dc=example,dc=com" -c -f ~/randotemp/base.ldif 
    
    Enter LDAP Password: 
    adding new entry "dc=example,dc=com" 
    
    adding new entry "ou=Hosts,dc=example,dc=com" 
    
    adding new entry "ou=Rpc,dc=example,dc=com" 
    
    adding new entry "ou=Services,dc=example,dc=com" 
    
    adding new entry "nisMapName=netgroup.byuser,dc=example,dc=com" 
    
    adding new entry "ou=Mounts,dc=example,dc=com" 
    
    adding new entry "ou=Networks,dc=example,dc=com" 
    
    adding new entry "ou=People,dc=example,dc=com" 
    
    adding new entry "ou=Group,dc=example,dc=com" 
    
    adding new entry "ou=Netgroup,dc=example,dc=com" 
    
    adding new entry "ou=Protocols,dc=example,dc=com" 
    
    adding new entry "ou=Aliases,dc=example,dc=com" 
    
    adding new entry "nisMapName=netgroup.byhost,dc=example,dc=com" 
    

That takes care of the minimal base, now lets get the necessary structure in place for autofs. This is pulled from Pat’s Daily Grind – AutoFS 5 and LDAP.

  1. We’re going to create three LDIF files and add them:
    1. auto.home.mounts.ldif
      dn: ou=auto.home,ou=Mounts,dc=example,dc=com 
      objectclass: automountMap 
      objectclass: top 
      ou: auto.home
      
    2. auto.master.mounts.ldif
      dn: ou=auto.master,ou=Mounts,dc=example,dc=com 
      objectclass: automountMap 
      objectclass: top 
      ou: auto.master 
      
    3. home.auto.master.mounts.ldif
      dn: cn=/home,ou=auto.master,ou=Mounts,dc=example,dc=com 
      automountinformation: auto.home 
      cn: /home 
      objectclass: automount 
      objectclass: top 
      
  2. Import the files to the directory with ldapadd.
    ldapadd -h localhost -x -W -D "cn=Manager,dc=example,dc=com" -c -f ./auto.home.mounts.ldif 
    ldapadd -h localhost -x -W -D "cn=Manager,dc=example,dc=com" -c -f ./auto.master.mounts.ldif
    ldapadd -h localhost -x -W -D "cn=Manager,dc=example,dc=com" -c -f ./home.auto.master.mounts.ldif
    
  3. I’ll show you how to add a user to this config when we actually start adding users.

Intermission

So far, we’ve done the following:

  • Installed the ldap server and clients
  • Configured the ACLs
  • Imported the minimal standard schema (plus AutoFS)
  • Created a directory base that will work with the standard pam_ldap, nslcd, and nss-pam-ldapd utilities.

The road ahead:

  • Our ACL won’t allow anonymous reads. We need to create the proxy user so that clients can access the directory
  • We don’t have any users or groups yet
  • I keep talking about AutoFS, so we’ll create the necessary directory entries for it and see an example of a working config as well.

Creating users and groups

This is pretty easy. We can either write up an ldif by hand and use ldapadd to add it, or we can use the migrationtools to create a template from an existing user. I don’t see any reason to put you through the migrationtools again, so I’ll paste an example of a working user and group.

  1. Create a new file called “<username>.ldif”, using the below as a template:
    dn: uid=user1000,ou=People,dc=example,dc=com 
    uid: user1000 
    cn: user1000 
    objectClass: account 
    objectClass: posixAccount 
    objectClass: shadowAccount 
    objectClass: top 
    userPassword: {SSHA}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
    loginShell: /bin/bash 
    uidNumber: 1000 
    gidNumber: 1000 
    homeDirectory: /home/user1000 
    gecos: user1000,,, 
    
    dn: cn=user1000,ou=Group,dc=example,dc=com 
    objectClass: posixGroup 
    objectClass: top 
    cn: user1000 
    userPassword: {crypt}x 
    gidNumber: 1000 
    
    • Customize to meet your needs. This LDIF has everything you need to create a working user and his username group (ala standard linux host account). You’ll need to do one of these for the proxyuser we configured during the ACL step, in addition to a regular user. For the proxyuser, I set the shell to /bin/false and made his uidNumber/gidNumber 999. All my other users will be 1000+.
    • Use the slappasswd command to generate an initial password, and paste it into the userPassword field of the user section. Do not set a password for the group, I don’t even know what that would do.
  2. If you want to configure an automount /home directory for this user, you can do so by adding this to the username.ldif:
    dn: cn=user1000,ou=auto.home,ou=Mounts,dc=example,dc=com 
    automountinformation: nfsfileserver01:/local/path/to/home/user1000 
    cn: user1000 
    objectclass: automount 
    objectclass: top 
    
    • You’ll need to create the home directory on the NFS file server, and you’ll have to have the LDAP and IDMAP clients working properly there. If you want, you could add this little bit of code to /etc/pam.d/system-auth on the NFS server to have the home directory auto-created when the user logs in the first time:
      session optional pam_mkhomedir.so skel=/etc/skel umask=077 
      

      Reference: http://www.server-world.info/en/note?os=CentOS\_6&p=ldap&f=2

    • I’m pretty sure you’d want to have your user log in to the NFS server via SSH first to have the home directory created, but it seems like it might work if you have them log in to any LDAP client server via SSH that has the appropriate automount hooks.
  3. Use the ldapadd command to add the user.
    ldapadd -h localhost -x -W -D "cn=Manager,dc=example,dc=com" -c -f ./user1000.ldif 
    
  4. That’s it for adding users and groups. You can see how you’d modify this file to add either just a user or just a group.

Configuring your clients

Now that we have a server, a directory, and some entries, we can use this thing. We’ll go over how to configure the client machines, and at the end we’ll talk about the automount considerations. If you were planning on SCPing files to the client machine, make sure you install openssh-clients.

  1. Install the necessary packages:
    openldap-clients nss-pam-ldapd autofs nfs-utils 
    
  2. Copy over the host CA cert and toss it in /etc/openldap/cacerts/.
  3. Use authconfig to get a jump on modifying the config files.
    authconfig --enableldap --enableldapauth --ldapserver=ldapdev.example.com --ldapbasedn="dc=example,dc=com" --enableldaptls --enablemd5 --update 
    
  4. Configure the proxy user.
    • First, edit /etc/nslcd.conf and add the proxy user information to the very bottom:
      binddn uid=proxyuser,ou=People,dc=example,dc=com 
      bindpw verysecret 
      
      • Do the same to /etc/pam_ldap.conf – the syntax is identical. If you skip this step, your users will not be able to change their own passwords!
      • Restart nslcd with service nslcd restart
    • You can log in with the user now. Do a test with getent passwd, and you should see your user and the proxy user (after all of the local users).
    • If it doesn’t work, check /var/log/messages. If it complains about not being able to talk to the ldap server,
      1. Log in to the ldap server
      2. Stop the slapd service
      3. Issue slapd -d acl
      4. In another window, restart nslcd on the client, and try getent passwd again
      5. The slapd debug output may show that the client doesn’t like the CA cert. I encountered this error on one of my boxes – it didn’t seem to be looking in /etc/openldap/cacerts for the cert. I had to add the following to /etc/nslcd.conf:
        tls_cacertfile /etc/openldap/cacerts/ldapdev.example.com.ca.crt 
        

Lets get his home directory squared away.

  1. Open up /etc/sysconfig/autofs and add this to the bottom:
    LDAP_URI="ldap://ldapdev.example.com" 
    LDAP_NETWORK_TIMEOUT=8 
    SEARCH_BASE="dc=example,dc=com" 
    MAP_OBJECT_CLASS="automountMap" 
    ENTRY_OBJECT_CLASS="automount" 
    MAP_ATTRIBUTE="ou" 
    ENTRY_ATTRIBUTE="cn" 
    VALUE_ATTRIBUTE="automountInformation" 
    
  2. Finally, we have to configure autofs to use the proxy user (I’m not sure why autofs has to have it’s own config and can’t reference the other 9 config files, but whatever)
    1. Crack open /etc/autofs_ldap_auth.conf and make it look like this:
      <?xml version="1.0" ?> 
      <!-- 
      This files contains a single entry with multiple attributes tied to it. 
      See autofs_ldap_auth.conf(5) for more information. 
      --> 
      
      <autofs_ldap_sasl_conf 
      usetls="yes" 
      tlsrequired="yes" 
      authrequired="simple" 
      user="uid=proxyuser,ou=People,dc=example,dc=com" 
      secret="plaintextpassword" 
      
      /> 
      

Bonus: Allowing only specific groups access to a host

In NIS, you can add users to a hostgroup, and be done with it. You can do similarly with LDAP, but it requires additional schema and filters and generally kind of looks nasty. I prefer to use the pam_listfile.so module to have pam look at a textfile full of users / groups to determine who can / can’t login. Check out this quick how-to for allowing groups, or this one for denying specific users..

  1. Create a new file called /etc/login.group.allowed. List your groups (local or LDAP) one per line.
    [root@webservices01 pam.d]# cat /etc/login.group.allowed 
    root 
    admin-web 
    
  2. Now we need to modify the pam stack to reference the new module and the flatfile. Add the following line to the top of==/etc/pam.d/system-auth= and /etc/pam.d/sshd
    auth required pam_listfile.so onerr=fail item=group sense=allow file=/etc/login.group.allowed 
    

Final thoughts

I need to add more instruction on which pre-reqs are needed for autofs on the clients, and I need to indicate when to bounce autofs. One more deployment to work out the kinks oughta do it.

SASL would be pretty great, since you could do passwordless SSH between hosts via the SASL-EXTERNAL mechanism. SASL is also necessary for Postfix and other applications, and even AutoFS is compatible with it.

Thanks for reading, and I hope you can learn from my experience.

Useful links

Link Description
http://www.computerglitch.net/bin/texts/CentOS6\_LDAP.php Interesting notes on the user account creation and setting a user’s password, as well as his own “recipe”. Useful comparison.
http://www.openldap.org/faq/data/cache/185.html The quick-n-simple TLS setup guide, although I hate the instructions on generating a CA cert.
http://www.tc.umn.edu/~brams006/selfsign.html A better reference on creating SSL certs
http://manpages.ubuntu.com/manpages/karmic/man5/slapd-config.5.html slapd-config man page
http://padraic2112.wordpress.com/2007/04/03/autofs-5-and-ldap/ Good for creating the autofs entries
http://www.server-world.info/en/note?os=CentOS\_6&p=ldap&f=1 Nice general ref for centos6 services, has un-explained configs that are worth thinking about / comparing to your setup
http://serverfault.com/questions/323497/how-do-i-configure-ldap-on-centos-6-for-user-authentication-in-the-most-secure-a Another useful recipe, although he has a half broken DB Monitor config.
http://eatingsecurity.blogspot.com/2008/11/openldap-security.html This is where I got turned on to the Proxy User. He only discusses it at a high level, and doesn’t discuss the ACL in detail.
http://www.cyberciti.biz/tips/howto-deny-allow-linux-user-group-login.html Configuring login access based on group membership

One comment

  1. Eric says:

    I am setting up a REDHAT 6 cluster at work using OpenLDAP. Thanks for the great article. It helped me alot with restricting who can login to what machine by group.