"Factored" create-user, and script to create shared service users
authorClinton Ebadi <clinton@unknownlamer.org>
Fri, 11 Jan 2013 08:06:18 +0000 (03:06 -0500)
committerClinton Ebadi <clinton@unknownlamer.org>
Fri, 11 Jan 2013 08:06:18 +0000 (03:06 -0500)
Converted create-user script into a set of procedures, grouped by
logical step in the user creation process. Things were regrouped only
as much as was needed to get `create-service-user' script for creating
principles for non-humans and `create-user` working with minimal
duplication. This still needs a lot of work (and destroy-user even
more).

create-service-user [new file with mode: 0755]
create-user-new [new file with mode: 0755]
lib/create-user-lib.sh [new file with mode: 0644]

diff --git a/create-service-user b/create-service-user
new file mode 100755 (executable)
index 0000000..a3144c4
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/bash -ex
+
+# create a shared service user, that is not able to use mod_waklog.
+
+# MUST be executed:
+#  - on fritz
+#  - as a user with an /etc/sudoers line
+#  - member of "wheel" unix group on deleuze (FIXME: TRUE?)
+#  - while holding tickets for a user who can 'ssh -K' to mire
+#     - and is a member of "wheel" on mire
+#  - while holding tokens for a user who is:
+#     - a member of system:administrator
+#     - listed in 'bos listusers fritz'
+#  - and who has been set up with Domtool admin privileges by:
+#     - running 'domtool-adduser $USER' while holding AFS admin tokens as
+#       someone who is already a Domtool admin
+#     - running 'domtool-admin grant $USER priv all' as someone who is already a
+#       Domtool admin
+#       (To bootstrap yourself into admindom:
+#         1. Run '/etc/init.d/domtool-server stop' on deleuze.
+#         2. Run '/etc/init.d/domtool-slave stop' on all Domtool slave machines
+#            (e.g., mire).
+#         3. Edit ~domtool/acl, following the example of adamc_admin to grant
+#             yourself 'priv all'.
+#         4. Run '/etc/init.d/domtool-server start' on deleuze.
+#         5. Run '/etc/init.d/domtool-slave start' on all Domtool slave
+#            machines.
+#         6. Run 'domtool-adduser' as above.)
+
+NEWUSER=$1
+
+if test -z "$NEWUSER"; then
+       echo "Invoke as create-user <USERNAME>"
+       exit 1
+fi
+
+source /afs/hcoop.net/common/etc/scripts/lib/create-user-lib.sh
+
+create_pts_user
+
+create_home_volume
+
+ensure_afs_servers_synced
\ No newline at end of file
diff --git a/create-user-new b/create-user-new
new file mode 100755 (executable)
index 0000000..b35f935
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/bash -ex
+
+# MUST be executed:
+#  - on fritz
+#  - as a user with an /etc/sudoers line
+#  - member of "wheel" unix group on deleuze (FIXME: TRUE?)
+#  - while holding tickets for a user who can 'ssh -K' to mire
+#     - and is a member of "wheel" on mire
+#  - while holding tokens for a user who is:
+#     - a member of system:administrator
+#     - listed in 'bos listusers fritz'
+#  - and who has been set up with Domtool admin privileges by:
+#     - running 'domtool-adduser $USER' while holding AFS admin tokens as
+#       someone who is already a Domtool admin
+#     - running 'domtool-admin grant $USER priv all' as someone who is already a
+#       Domtool admin
+#       (To bootstrap yourself into admindom:
+#         1. Run '/etc/init.d/domtool-server stop' on deleuze.
+#         2. Run '/etc/init.d/domtool-slave stop' on all Domtool slave machines
+#            (e.g., mire).
+#         3. Edit ~domtool/acl, following the example of adamc_admin to grant
+#             yourself 'priv all'.
+#         4. Run '/etc/init.d/domtool-server start' on deleuze.
+#         5. Run '/etc/init.d/domtool-slave start' on all Domtool slave
+#            machines.
+#         6. Run 'domtool-adduser' as above.)
+
+NEWUSER=$1
+
+if test -z "$NEWUSER"; then
+       echo "Invoke as create-user <USERNAME>"
+       exit 1
+fi
+
+source /afs/hcoop.net/common/etc/scripts/lib/create-user-lib.sh
+
+create_pts_user
+create_pts_user_daemon
+export_user_keytabs
+
+create_home_volume
+create_mail_volume
+
+ensure_afs_servers_synced
+
+seed_user_hcoop_directories
+setup_user_databases
+create_dav_locks
+
+enable_domtool
+
+subscribe_to_lists
\ No newline at end of file
diff --git a/lib/create-user-lib.sh b/lib/create-user-lib.sh
new file mode 100644 (file)
index 0000000..51834ef
--- /dev/null
@@ -0,0 +1,295 @@
+# -*- sh-mode -*-
+
+# Library functions for create-user scripts
+# Export the $NEWUSER variable before sourcing!
+
+# Functionality is split so that the scripts for creating real users,
+# service users, and web service users can share as much code as
+# possible.
+
+# This has probably grown to the point where it shouldn't be a shell
+# script any more.
+
+# ALWAYS REMEMBER: THIS MUST BE IDEMPOTENT! re creating a user is
+# something that should be perfectly permissible, and is something
+# that we do somewhat regularly (to bring old accounts up to date).
+
+export PATH=$PATH:/afs/hcoop.net/common/bin/
+
+if test -z "$NEWUSER"; then
+       echo "NEWUSER not set before sourcing create user library"
+       exit 1
+fi
+
+#
+# Construct various paths for later perusal.
+#
+
+# (If it's not clear, for user fred, PATHBITS = f/fr/fred)
+PATHBITS=`echo $NEWUSER | head -c 1`/`echo $NEWUSER | head -c 2`/$NEWUSER
+HOMEPATH=/afs/hcoop.net/user/$PATHBITS
+MAILPATH=/afs/hcoop.net/common/email/$PATHBITS
+
+#
+# Helper functions
+#
+
+function execute_on_web_nodes () {
+    ssh -K deleuze $*
+    ssh -K mire $*
+    ssh -K navajos $*
+}
+
+function execute_on_domtool_server () {
+    ssh -K deleuze.hcoop.net $*
+}
+
+
+function execute_on_all_machines () {
+    $*
+    ssh -K mire.hcoop.net $*
+    ssh -K hopper.hcoop.net $*
+    ssh -K deleuze.hcoop.net $*
+    ssh -K navajos.hcoop.net $*
+    ssh -K bog.hcoop.net $*
+}
+
+#
+# User credentials
+#
+
+function create_pts_user () {
+    # Create primary user kerberos principle and afs pts user
+
+    # We use -randkey for user's main principal as well, to make sure
+    # that the creation process does not continue without having a
+    # main principal. (But you who want to set password for a user,
+    # don't worry - we'll invoke cpw later, so that it has the same
+    # effect as setting password right now - while it is more error
+    # tolerant).
+
+    sudo kadmin.local -p root/admin -q "ank -policy user -randkey +requires_preauth $NEWUSER@HCOOP.NET"
+    sudo kadmin.local -p root/admin -q "modprinc -maxlife 1day $NEWUSER@HCOOP.NET"
+
+    pts cu $NEWUSER || true
+}
+
+function create_pts_user_daemon () {
+
+    # Create additional kerberos principles ($user.daemon for now, in
+    # theory also $user.mail, $user.cgi) and pts users for any used to
+    # gain afs access ($user.daemon only)
+    sudo kadmin.local -p root/admin -q "ank -policy daemon -randkey +requires_preauth $NEWUSER/daemon@HCOOP.NET"
+    pts cu $NEWUSER.daemon || true
+}
+
+function export_user_keytabs () {
+
+    # Export .mailfilter and .cgi keys to a keytab file
+
+    # This is suboptimal, we need to generate keytabs for
+    # cgi/mail/etc. separately, and only sync to the nodes that
+    # perform the services in question
+
+    # create a daemon keytab (used by /etc/exim4/get-token)
+    # *only* if it does not exist!
+    test -e /etc/keytabs/user.daemon/$NEWUSER || \
+       sudo kadmin.local -p root/admin -q "ktadd -k /etc/keytabs/user.daemon/$NEWUSER $NEWUSER/daemon@HCOOP.NET"
+
+    # Properly chown/mod keytab files (must be $NEWUSER:www-data)
+    sudo chown $NEWUSER:www-data /etc/keytabs/user.daemon/$NEWUSER
+    sudo chmod 440            /etc/keytabs/user.daemon/$NEWUSER
+
+    # rsync keytabs
+    (cd /etc/keytabs
+       sudo tar clpf - user.daemon/$NEWUSER | \
+           ssh mire.hcoop.net cd /etc/keytabs\; sudo tar xlpf -)
+    (cd /etc/keytabs
+       sudo tar clpf - user.daemon/$NEWUSER | \
+           ssh hopper.hcoop.net cd /etc/keytabs\; sudo tar xlpf -)
+    (cd /etc/keytabs
+       sudo tar clpf - user.daemon/$NEWUSER | \
+           ssh deleuze.hcoop.net cd /etc/keytabs\; sudo tar xlpf -)
+    (cd /etc/keytabs
+       sudo tar clpf - user.daemon/$NEWUSER | \
+           ssh navajos.hcoop.net cd /etc/keytabs\; sudo tar xlpf -)
+    (cd /etc/keytabs
+       sudo tar clpf - user.daemon/$NEWUSER | \
+           ssh bog.hcoop.net cd /etc/keytabs\; sudo tar xlpf -)
+}
+
+
+#
+# Create/mount/set-perms on user's volumes (home, mail, databases, logs)
+#
+
+# Each function that creates an afs volume should ensure that the
+# backup volume is created and mounted for users.
+
+function create_home_volume () {
+
+    if vos examine user.$NEWUSER.d 2>/dev/null; then
+       echo "Reactivating old volume (user.$NEWUSER.d)"
+       vos rename user.$NEWUSER.d user.$NEWUSER
+    fi
+    vos examine user.$NEWUSER 2>/dev/null || \
+       vos create fritz.hcoop.net /vicepa user.$NEWUSER -maxquota 400000
+
+    mkdir -p `dirname $HOMEPATH`
+    fs ls $HOMEPATH || test -L $HOMEPATH || fs mkm $HOMEPATH user.$NEWUSER
+    chown $NEWUSER:nogroup $HOMEPATH
+    fs sa $HOMEPATH $NEWUSER            all
+    fs sa $HOMEPATH system:anyuser   l
+    # cleanliness / needed to keep suphp happy
+    chown root:root $HOMEPATH/../../
+    chown root:root $HOMEPATH/../
+    
+    # backup volume
+    mkdir -p `dirname /afs/hcoop.net/.old/user/$PATHBITS`
+    fs ls /afs/hcoop.net/.old/user/$PATHBITS || \
+       fs mkm /afs/hcoop.net/.old/user/$PATHBITS user.$NEWUSER.backup
+}
+
+
+function create_mail_volume () {
+
+    if vos examine mail.$NEWUSER.d 2>/dev/null; then
+       echo "Reactivating old volume (mail.$NEWUSER.d)"
+       vos rename mail.$NEWUSER.d mail.$NEWUSER
+    fi
+    vos examine mail.$NEWUSER 2>/dev/null || \
+       vos create fritz.hcoop.net /vicepa mail.$NEWUSER -maxquota 400000
+    
+    mkdir -p `dirname $MAILPATH`
+    fs ls $MAILPATH || fs mkm $MAILPATH         mail.$NEWUSER
+    fs ls $HOMEPATH/Maildir || fs mkm $HOMEPATH/Maildir mail.$NEWUSER
+    chown $NEWUSER:nogroup $MAILPATH
+    chown $NEWUSER:nogroup $HOMEPATH/Maildir
+    fs sa $MAILPATH $NEWUSER        all
+    fs sa $MAILPATH $NEWUSER.daemon all
+
+    if test ! -e $MAILPATH/new; then
+       mkdir -p $MAILPATH/cur $MAILPATH/new $MAILPATH/tmp
+       echo -e "This email account is provided as a service for HCoop members." \
+            "\n\nTo learn how to use it, please visit the page" \
+            "\n<http://wiki.hcoop.net/MemberManual/Email> on our website."| \
+            mail -s "Welcome to your HCoop email store" \
+            -e -a "From: postmaster@hcoop.net" \
+            real-$NEWUSER
+    fi
+
+    chown $NEWUSER:nogroup $MAILPATH/cur $MAILPATH/new $MAILPATH/tmp
+
+    # Set up shared SpamAssassin folder
+    if test -f $HOMEPATH/Maildir/shared-maildirs; then
+        # Deal with case where user rsync'd their Maildir from fyodor
+        # Not an issue now, but harmless and can be adapted when we
+        # move the spamd dirs into afs where they belong later.
+       pattern='^SpamAssassin  /home/spamd'
+       file=$HOMEPATH/Maildir/shared-maildirs
+       if grep $pattern $file; then
+            sed -i -r -e \
+               's!^(SpamAssassin       )/home/spamd!\1/var/local/lib/spamd!1' \
+               $file
+       fi
+    else
+       maildirmake --add SpamAssassin=/var/local/lib/spamd/Maildir \
+            $HOMEPATH/Maildir
+    fi
+
+    mkdir -p `dirname /afs/hcoop.net/.old/mail/$PATHBITS`
+    fs ls /afs/hcoop.net/.old/mail/$PATHBITS || \
+       fs mkm /afs/hcoop.net/.old/mail/$PATHBITS mail.$NEWUSER.backup
+    vos release old
+}
+
+function seed_user_hcoop_directories () {
+    # Additional standard directories. Some of these should probably
+    # be on their own volumes, and access via a canonical path instead
+    # to give users more control over their home dir without risking
+    # breaking system services.
+
+    # Apache logs
+    mkdir -p $HOMEPATH/.logs
+    chown $NEWUSER:nogroup $HOMEPATH/.logs
+    mkdir -p $HOMEPATH/.logs/apache
+    chown $NEWUSER:nogroup $HOMEPATH/.logs/apache
+    fs sa $HOMEPATH/.logs/apache $NEWUSER.daemon rlwidk
+    mkdir -p $HOMEPATH/.logs/mail
+    fs sa $HOMEPATH/.logs/mail $NEWUSER.daemon rlwidk
+    chown $NEWUSER:nogroup $HOMEPATH/.logs/mail
+
+    # public_html
+    test -e $HOMEPATH/public_html || \
+       (mkdir -p $HOMEPATH/public_html; \
+       chown $NEWUSER:nogroup $HOMEPATH/public_html; \
+       fs sa $HOMEPATH/public_html system:anyuser none; \
+       fs sa $HOMEPATH/public_html $NEWUSER.daemon rl)
+
+    # .procmail.d
+    mkdir -p $HOMEPATH/.procmail.d
+    chown $NEWUSER:nogroup $HOMEPATH/.procmail.d
+    fs sa $HOMEPATH/.procmail.d system:anyuser rl
+
+    # .public
+    mkdir -p $HOMEPATH/.public/
+    chown $NEWUSER:nogroup $HOMEPATH/.public
+    fs sa $HOMEPATH/.public system:anyuser rl
+
+    # .domtool
+    mkdir -p $HOMEPATH/.public/.domtool
+    chown $NEWUSER:nogroup $HOMEPATH/.public/.domtool
+    test -e $HOMEPATH/.domtool || \
+       test -L $HOMEPATH/.domtool || \
+        execute_on_domtool_server sudo -u $NEWUSER ln -s $HOMEPATH/.public/.domtool $HOMEPATH/.domtool
+        # ^^ work around sudo env_reset crap without having to
+        # actually figure out how to make it work cleanly -- clinton,
+        # 2011-11-30
+
+    # Gitweb hosting
+    test -L /var/cache/git/$NEWUSER || \
+       sudo ln -s $HOMEPATH/.hcoop-git /var/cache/git/$NEWUSER
+
+}
+
+#
+# Non-AFS files and directories
+#
+
+function create_dav_locks () {
+    # Make per-user apache DAV lock directory -- the directory must be
+    # both user and group-writable, which is silly.
+    execute_on_web_nodes sudo mkdir -p /var/lock/apache2/dav/$NEWUSER
+    execute_on_web_nodes sudo chown $NEWUSER:www-data /var/lock/apache2/dav/$NEWUSER
+    execute_on_web_nodes sudo chmod ug=rwx,o= /var/lock/apache2/dav/$NEWUSER
+}
+
+function setup_user_databases () {
+    sudo /afs/hcoop.net/common/etc/scripts/create-user-database $NEWUSER
+}
+
+#
+# etc
+#
+
+function enable_domtool () {
+    execute_on_domtool_server domtool-adduser $NEWUSER
+}
+
+function subscribe_to_lists () {
+    # Subscribe user to our mailing lists.
+
+    echo $NEWUSER@hcoop.net | ssh -K deleuze sudo -u list \
+       /var/lib/mailman/bin/add_members -r - hcoop-announce
+}
+
+function ensure_afs_servers_synced () {
+    vos release old
+    
+    # technically this might not be necessary, but for good measure...
+    vos syncserv fritz
+    vos syncvldb fritz
+    
+    # refresh volume location cache (takes ~2hrs otherwise)
+    execute_on_all_machines fs checkvolumes
+}
\ No newline at end of file