Creating a user and group in OpenLDAP directory

When setting up an OpenLDAP server for a friend, I've come across the difficulty of automating user account creation. The problem was that the person (or program) creating the accounts would have to know quite a lot about the structure of LDAP directory, correct usage of ldap commands, as well as error handling and logging. I wanted to free the program from having to do any of this; instead, it could make a simple call to a Bash script and let it handle the details.

First, I assume that your server is set up correctly and you're storing account passwords in LDAP (or perhaps in Kerberos). In case of Kerberos, you'll see how to modify the way script sets user password. I also assume that LDAP commands are available on the host running the script, and that there's proper networking in place to reach the LDAP server.

Here's what the script will do:

  1. Set up constants (such as LDAP URI, domain name, etc)
  2. Determine new user UID and GID
  3. Generate LDIF file with user iformation
  4. Instruct LDAP server to process LDIF file, thus adding user to LDAP database
  5. Set user's password (not stored in LDIF file)
  6. Create user's home directory

Setting up constants

Let's create the following variables to ease future changes to the script.

DOMAIN="example.com"
LDAPURI="ldap://ldap.example.com/"
ADMINDN="cn=admin,dc=example,dc=com"
ADMINPW=""
USERSOU="ou=people,dc=example,dc=com"
GROUPSOU="ou=groups,dc=example,dc=com"
HOMEDIRBASE="/nethome"

This should be self-explanatory to anyone familiar with LDAP. Our domain is example.com, our LDAP server URI is ldap://ldap.example.com/, the administrative account that will create new users is cn=admin,dc=example,dc=com (in other words, admin). If you feel comfortable setting admin's password in this file, put it into ADMINPW variable (very risky). Otherwise, pass it as a command line argument (still risky), or ask for it during the script run:

# Get admin password
if [ -z $ADMINPW ] ; then
    read -p "LDAP password for "$ADMINDN": " ADMINPW
    if [ ! "$?" -eq "0" ] ; then
        exit $?
    fi
fi

Finally, new users will be created in people OU, new groups in groups OU, and home directories will be created under /nethome.

Note that as alternative to using hard-coded admin account, you may have sufficient privileges to create new users and groups as yourself. In that case, remove all instances of ADMINDN and ADMINPW from the script so that ldap commands use your user session instead.

Usage

It's always nice to let the user of the script know what options it expects, and to check for correct number of options passed.

if [ -z $5 ] ; then
    echo "Usage: $0 <username> <password> <first_name> <last_name> <alternate_emai> <phone>"
    exit 1
fi

So, the script can be called like this:

./ldap-create-user.sh sjones 123456 Smith Jones sjones@example.net 5551234

Username will be available to script user as $1, password as $2, etc.

Determining UID and GID

The following block of code will determine the current biggest UID and GID in LDAP database, increment it by 1, and use that as new UID and GID.

# Determine new UID and GID
echo "Determining new UID and GID..."
MAXGID=$(ldapsearch -LLL -H "$LDAPURI" -D "$ADMINDN" -w "$ADMINPW" "(objectClass=posixGroup)" gidNumber | grep gidNumber | sort | tail -n 1 | awk '{ print $2 }')
[ "$?" -eq "0" ] || exit $?
MAXUID=$(ldapsearch -LLL -H "$LDAPURI" -D "$ADMINDN" -w "$ADMINPW" "(objectClass=posixAccount)" uidNumber | grep uidNumber | sort | tail -n 1 | awk '{ print $2 }')
[ "$?" -eq "0" ] || exit $?
UID=""
GID=""
if [ $MAXGID -ge $MAXUID ] ; then
    UID=$(expr $MAXGID + 1)
    GID=$(expr $MAXGID + 1)
else
    UID=$(expr $MAXUID + 1)
    GID=$(expr $MAXUID + 1)
fi

Using ldapsearch command, we extract the current largest UID and GID value, and later use it to get the new value for UID and GID.

Note the [ "$?" -eq "0" ] || exit $? checks placed after almost every action. These will stop the script if command above fails (returns non-zero status).

Creating user account

Generating LDIF file

Now that we have all the information we need, it's time to create the LDIF file and instruct LDAP server to process the file as new user account. Here's how we create the LDIF file.

# Create temp ldapmodify file
echo "Generating LDIF file..."
cat >$0.ldif <<EOF
dn: uid=$1,$USERSOU
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
objectClass: top
uid: $1
cn: $3 $4
uidNumber: $UID
gidNumber: $GID
homeDirectory: $HOMEDIRBASE/$1
displayName: $1
givenName: $3
sn: $4
telephoneNumber: $5
mail: $1@$DOMAIN
loginShell: /bin/bash

dn: cn=$1,$GROUPSOU
objectClass: posixGroup
objectClass: top
cn: $1
gidNumber: $GID
EOF
[ "$?" -eq "0" ] || exit $?
cat $0.ldif

These two blocks of text define the required (and some optional) fields for user and group objects in LDAP directory. ldapadd and ldapmodify can take this input and pass it on to LDAP server.

In case of our sample run below, the following LDIF output is generated:

dn: uid=bjones,ou=people,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
objectClass: top
uid: bjones
cn: Bob Jones
uidNumber: 101
gidNumber: 101
homeDirectory: /home/bjones
gecos: Bob Jones,,,bjones@example.net
displayName: Bob Jones
givenName: Bob
sn: Jones
telephoneNumber: 5551234
mail: bjones@example.com
loginShell: /bin/bash

dn: cn=bjones,ou=groups,dc=example,dc=com
objectClass: posixGroup
objectClass: top
cn: bjones
gidNumber: 101

Running ldapadd and ldappasswd

Finally, we're ready to create the user and group account and set user's password.

YN="n"
read -p "Proceed with adding user? [y/n] " YN
if [ "x$YN" -eq "xy" ]
    # Run ldapmodify to create user
    echo "Running ldapadd..."
    ldapadd -H "$LDAPURI" -D "$ADMINDN" -w "$ADMINPW" -f $0.ldif
    [ "$?" -eq "0" ] || exit $?

    # Run ldappasswd to set password
    echo "Running ldappasswd..."
    ldappasswd -H "$LDAPURI" -D "$ADMINDN" -w "$ADMINPW" -s "$2" "uid=$1,$USERSOU"
    [ "$?" -eq "0" ] || exit $?

    # Create home directory
    mkdir $HOMEDIRBASE/$1
    [ "$?" -eq "0" ] || exit $?
    chown -R $1:$1 $HOMEDIRBASE/$1
    [ "$?" -eq "0" ] || exit $?
fi

We add a yes/no confirmation dialog before proceeding, for safety. Inside the if block, we pass ldapadd and ldappasswd all the information we've gathered, which is sufficient for creation of new account. Lastly, we create the home directory and sets its owner to the new user and group.

Cleaning up

Even though no critical information has been stored in our temporary LDIF file, it makes sense to delete it.

# Remove temp file
rm -f $0.ldif

In case of Kerberos

A friend of mine told me that adding -Y GSSAPI switch to ldap commands instructs them to use the (properly configured) Kerberos service on LDAP server. Hopefully this bit of information will help those with LDAP/Kerberos set up.

You can download complete script here.

10:36 PM | 0 Comments | Tags: ,

Comments

Notify me when others comment on this post