Wednesday, June 13, 2012

Setting up a network-based, centralized home directory as a service

As I mentioned in my last post, I just built out a dedicated server at home, and I'm migrating a bunch of services to it. One reason I build this server in the first place was so that I could set up roaming home directories for all my Linux PCs in the house.

I tried a few configurations before landing on the one described here. I had problems where Ubuntu 12.04 would fail to boot if the system failed to mount the network drive properly when the mount was in the fstab. Ubuntu 10.04 would error on boot, but give you the opportunity to skip the mount and continue booting. However, if the share was mounted to /home on the client machine, logging into a desktop environment would fail on account of none of the config files being available. Even when the mount worked properly, having it mounted at /home caused performance problems. Relying on the network for processes like booting and logging into a desktop ended up being a deal-breaker for me having automounted network home directories.

But I did settle on a solution that works well. In brief, I servicized the mounting and unmounting of the network drive, and the control script I wrote for it also places netdrive symbolic links in each non-system user's home directory if a directory on the share exists in their name. Here's how it's done.

There are two parts to this configuration - a server and some clients. Let's set up the server.

My server is running Debian Squeeze, but these instructions should translate well to other distributions. First, install the NFS server:

sudo apt-get install nfs-kernel-server

Then set the config to share the /home directory over the network. Add this to the end of /etc/exports:

/home   192.168.0.0/255.255.255.0(rw,sync,fsid=0,no_subtree_check)

You should modify that to suit your specific needs. /home is the directory on the server to share. 192.168.0.0/255.255.255.0 means that the only systems who are allowed to access it will be on the 192.168.0.x subnet, which, for me, means everything behind my router. This share won't be accessible to anyone on the public side of my router. Finally, the (rw,sync,fsid=0,no_subtree_check) part are options that say, respectively, "Mount it read/write so users can save files here," "Write files to disk synchronously so there aren't sudden unmounting problems," "If the NFS server is NFS4, treat this directory as the root of all shared directories," and "Speed things up by allowing all subdirectories of the share to be accessed."

Restart the NFS server:

sudo service nfs-kernel-server restart

If you encounter problems, check /var/log/daemon.log for something to Google with.

I had firewall problems here. Mounting an NFS share on a remote server uses lots of different ports, and I was unable to identify them all. I ended up adding a rule to my firewall to let anything behind my router (that 192.168.0.x subnet) access any ports on the server. For me, this isn't a security issue. You should consider your situation and make that decision for yourself. At any rate, here's my command to make that firewall rule:

sudo ufw allow from 192.168.0.0/24 to any

Now get onto a client machine and try to mount it manually just to be sure it works:

sudo mount -t nfs -o proto=tcp,port=2049 192.168.0.100:/home /mnt

If you get no errors, try...

ls /mnt

...and check if what you see is what you expect. If so, unmount it:

sudo umount /mnt

Assuming that everything works, it's time to set up a control script. The script below is hardcoded to mount the NFS share to /mnt/netdrive. You should probably read through it and understand it before running it to be sure it won't interfere with things on your system. If it's going to work for you, paste it into /etc/init.d/netdrive or wherever else you decide.

safe_start () {

    if [ ! -d /mnt/netdrive ]; then
        mkdir /mnt/netdrive
    fi

    if grep -qs '/mnt/netdrive' /proc/mounts; then
        echo "Something is already mounted at /mnt/netdrive [ FAIL ]"
        exit 1
    else
        mount -t nfs -o proto=tcp,port=2049 192.168.0.100:/home /mnt/netdrive
    fi

    for u in `grep '/home' /etc/passwd | cut -d: -f1`; do
        if [ -d /mnt/netdrive/$u ]; then
            HOME=`grep "$u" /etc/passwd | cut -d: -f6`
            if [ -e $HOME/netdrive ]; then
                rm $HOME/netdrive
            fi
            ln -s /mnt/netdrive/$u $HOME/netdrive
        fi
    done
}

safe_unmount () {
    FILES_IN_USE=`lsof | grep '/mnt/netdrive' | awk '{print $9}'`
    if [ ! "$FILES_IN_USE" = "" ]; then
        echo "The following files are located on the netdrive and are still in use:"
        echo $FILES_IN_USE
        exit 1
    fi

    unmount
}

unmount () {
    umount /mnt/netdrive
    for u in `ls /home`; do
        if [ -d /home/$u/netdrive ]; then
            rm $u/netdrive;
        fi
    done
}

show_status () {
    if grep -qs '/mnt/netdrive' /proc/mounts; then
        echo "Netdrive is mounted."
    else
        echo "Netdrive is not mounted."
    fi
}

show_help () {
    echo "This script accepts the following commands: start, stop, forcestop, status, help"
}

case $1 in
    "start" )
        safe_start ;;
    "stop" )
        safe_unmount ;;
    "forcestop" )
        unmount ;;
    "status" )
        show_status ;;
    * )
        show_help ;;
esac

exit 0

Apply permissions:

sudo chmod 755 /etc/init.d/netdrive

Now you can run...

sudo /etc/init.d/netdrive start

...to mount it, or give it stop instead to stop it safely. The script will refuse to unmount the drive if it has files open still. If you want to override that check, use forcestop. status will tell you if the network drive is mounted or not. help will give you a basic usage message. When mounting, this script will also place the netdrive symlink in users' home directories, so, for example, /home/ryan/netdrive is mapped to /mnt/netdrive/ryan.

Now, I still wanted my drive to mount on boot. So I threw a symlink in the startup:

sudo ln -s /etc/init.d/netdrive /etc/rc2.d/S10netdrive

That's my Ubuntu 12.04 machine. I can't remember at what point in the startup procedure I placed in on my Ubuntu 10.04 box, but it's somewhat discretionary anyway.

So reboot your client and make sure you boot with a working netdrive symlink in your home directory. And done!

6 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. i believe there's 2 typos in safe_unmount
    1) the back quote should come after, not before, the awk command
    2) in the test below that it should be = instead of ==

    also, in case anyone else has the same problem i did, make sure you apt-get nfs-common on the client

    thanks by the way, this helped out a lot

    ReplyDelete
  5. also
    sudo ln -s /etc/rc2.d/S10netdrive /etc/init.d/netdrive
    should be
    sudo ln -s /etc/init.d/netdrive /etc/rc2.d/S10netdrive

    ReplyDelete
  6. Good eye, Sam! I've updated the code above to reflect your suggestions. Thanks for the corrections.

    ReplyDelete