Wednesday, May 12, 2010

Solaris : automatic creation of home dirs

When users in Solaris are externally authenticated (e.g. via LDAP, Active Directory, NIS, etc) you often find a need for users home directories to be created automatically when they login for the first time. In Linux people typically resort to pam_mkhomedir but in Solaris there is really no need as you've got something better: the automounter.

In this example I'll assume that users home directories are in /export/home/ldapusers/. What you want is that when user johndoe logs in you want to make sure he has a home directory, if not create one for him. Here is the trick: using the automounter you can do this.

First a few notes on what the automounter does. Essentially it automatically mounts 'something' when you access a certain mount point. By the latter a mean a local directory path, e.g. /export/home/ldapusers/johndoe. What you mount can be a network drive, a CD-ROM or simply another directory on the local file system. We'll do the latter in this example.

The Solaris automounter has a feature called 'executable maps'. This means that whenever a directory on the moint point is accessed a script will be executed. The script must return the destination on stdout but as it is a shell script it allows us any kind of customization before that such as creating directories, copying skell files, or whatever.

As said lets assume that /export/home/ldapusers/ is our mount point. As this is our mount point we cannot physically have the user's home directories in the same location; they must reside somewhere else, in our case in /export/home/.externalusers/.

Here we go:

1. Add the following lines to /etc/auto_master file :

# Automount (and create if not exist) home dirs for external users
/export/home/ldapusers /etc/auto_homedir


This essentially tells the automounter to execute script /etc/auto_homedir whenever someone accesses directories on /export/home/ldapusers. You can of course use any location and name for the script that you like.

2. Next create the /etc/auto_homedir script:

#!/bin/bash
#
# Automounter executable script.
# This script must be referenced in file /etc/auto_master in order
# to have any effect. Furthermore it must have the sticky bit set.
#
# This script receives as $1 the name of the object (file or directory)
# that is being accessed under the moint point. For example if the
# moint point is /home and someone is accessing /home/johndoe then
# $1 will be "johndoe".
# The script is expected to write the path of the physical location
# on stdout.


# ------------------------------------------------------------------------------
# Customizations


# Path of our mount point. This must be the same as the name
# in the first column in the file /etc/auto_master.
HOMEDIRPATH=/export/home/ldapusers

# Where the physical user home directories are
PHYSICALDIRPATH=/export/home/.extusers

# The group owner to give to a user's home directory
HOMEDIRGROUP="external users"


# ------------------------------------------------------------------------------

hdir=~$1


# Sanity check

if [[ $hdir != $HOMEDIRPATH/* ]]; then
# Someone is trying to access a directory under $HOMEDIRPATH
# which is not the name of a user. This we simply ignore, i.e. we exit
# without writing anything on stdout. This will make the automounter
# ignore the action and thus the user will get an error.
exit
fi


# Now we know that $1 is a valid user with a home directory
# under $HOMEDIRPATH

# Next see if the user's physical home dir exist. If not create it.

phdir="$PHYSICALDIRPATH/$1"

if [ -d "$phdir" ]; then
# Yes the user's physical home dir already exist.
# Return the path of the physical home dir to the automounter
# and exit.
echo "localhost:$phdir"
exit
fi


# Create the directory
mkdir -p $phdir

# Set owner and group
chown "$1":"$HOMEDIRGROUP" $phdir


# At this point we can copy setup files into the user's home directory
# e.g. files from /etc/skel. This step is optional.

# Example for Bash logins :
#cp /etc/skel/local.profile $phdir/.profile
#chown "$1":"$HOMEDIRGROUP" $phdir/.profile


# This was all we needed to do.
# Return the path of the physical home dir to the automounter
# and exit.

echo "localhost:$phdir"

exit


3. Set sticky bit and execute on the script

chmod +t+x /etc/auto_homedir

4. Restart the automounter

svcadm restart autofs


That's it.


Debugging and more info.

For more info see man automount. Also if you find that your script is not working as intended you can always put in a few debug lines such as

echo "Variable hdir is : $hdir " >> /tmp/mydebug

and then you can examine the contents of /tmp/mydebug file.

Why not on /home ?

Why don't I use /home in the above example? No good reason, really. Feel free to change. If you are from Linux and wonder about the difference between /home and /export/home then I recommend this blog posting. It also explains about the Solaris automounter although not the 'executable maps' feature we use above. Well worth the read.

6 comments:

  1. Using clean solaris 10 u9 install but auto_homedir is not even executed. Sorry but there must be some more steps?

    autofs is running and i have restarted it after changes.
    online 16:44:51 svc:/system/filesystem/autofs:default

    auto master looks like this:
    +auto_master
    /net -hosts -nosuid,nobrowse
    /home auto_home -nobrowse
    /home /etc/auto_homedir

    ReplyDelete
  2. use hdir=$HOMEDIRPATH/$1 instead of ~$1. ~$1 will give you back the / because automounter is running as root.

    another problem:
    if [[ $hdir != $HOMEDIRPATH/* ]]; then
    # Someone is trying to access a directory under $HOMEDIRPATH
    # which is not the name of a user. This we simply ignore, i.e. we exit
    # without writing anything on stdout. This will make the automounter
    # ignore the action and thus the user will get an error.
    exit
    fi

    i don't understand this.
    because of that "if statement" the automounter won't mount anything. $HOMEDIRPATH/* is empty...

    use this:
    getent passwd $1 > /dev/null
    if [ $? -ne 0 ]; then
    exit
    fi

    this will check if the user who is logging in is exists in the passwd file, or in ldap. if yes, automounter will create the physical directory and mount or just mount the user's homedirectory.

    ReplyDelete
  3. As the "aaa" user above me said, this does not appear to work on Solaris 10 (works fine on Solaris 11). Autofs was restarted, sticky and exec bit added to /etc/auto_homedir, and the script does spit out necessary info if I run it manually (sh -x /etc/auto_homedir username).

    In automounter logs I get this:

    LOOKUP REQUEST: Wed May 23 13:54:10 2012
    t1 name=username[] map=/etc/auto_homedir opts= path=/home direct=0
    t1 PUSH /etc/auto_homedir
    t1 Executable map: map=/etc/auto_homedir key=username
    t1 call_read_execout /etc/auto_homedir username
    t1 POP /etc/auto_homedir
    t1 do_lookup1: action=2 wildcard=FALSE error=2
    t1 LOOKUP REPLY : status=2

    As such, no home directory gets created.

    ReplyDelete
  4. This requires installation of patch 147775-01, which fixes executable automount maps:
    Topic: SunOS 5.10_x86: automountd patch
    7085850 automounter fails to execute executable automounter maps

    Strangely it does not appear to be part of a recommended patch cluster.

    I was experiencing the same problem as "aaa" and "robot", but after installing this patch, everything now works properly. Before installing the patch, the map was never even run (I put syslog calls into it, which never happened), and monitoring the automountd process with truss showed its door_call() in call_read_execout failing with EBADF -- thus it never forked and executed the map.

    ReplyDelete
  5. Greetings.

    Automatic creation of home dirs does work on the NFS server where the share /export/home is exported so the executable map script can indeed create the user_homedir (mkdir command)

    But how can we create the homedir from an NFS client where the mkdir command fails since there is no /export/home there.

    Thank you very much for your reply. Best Regards. kamel (from France).




    ReplyDelete