{"id":19,"date":"2007-05-16T19:01:43","date_gmt":"2007-05-17T01:01:43","guid":{"rendered":"http:\/\/blogs.cae.tntech.edu\/mwr\/2007\/05\/16\/authentication-servers\/"},"modified":"2024-10-27T14:26:20","modified_gmt":"2024-10-27T14:26:20","slug":"authentication-servers","status":"publish","type":"post","link":"https:\/\/sites.tntech.edu\/renfro\/2007\/05\/16\/authentication-servers\/","title":{"rendered":"Authentication Servers"},"content":{"rendered":"<blockquote><p><em>The whole point of an authentication service is that it allows the client to prove itself to be trustworthy, or at least to prove itself to be the same nefarious character it claims.<\/em><\/p>\n<p align=\"right\"> &#8212; <a href=\"http:\/\/www.infrastructures.org\/bootstrap\/auth.shtml\">Infrastructures.org<\/a><\/p>\n<\/blockquote>\n<p>I want to make our existing Active Directory the source for all the following:<\/p>\n<ul>\n<li>Lists of users allowed to log into the managed infrastructure<\/li>\n<li>UIDs and GIDs of those users<\/li>\n<li>Passwords of those users<\/li>\n<\/ul>\n<p>But I&#8217;m striking out on everything except the passwords themselves, and I&#8217;ve had that part working for years. So no new progress on that front, despite reading tons of LDAP and NSS howtos, bug reports, etc. So instead, let me lay out our planned implementation and workflow:<\/p>\n<ol>\n<li>Windows administrator makes a new user account in Active Directory.<\/li>\n<li>Windows administrator (or sometimes me) logs into the main file server, and runs an nearly-unmodified adduser script that creates the user&#8217;s Unix UID, home directory, and registers the user on either our student mailing list or our faculty\/staff mailing list. This script currently has a big &#8216;ssh in a for loop&#8217; part for adding accounts on the client systems, but this will be removed.<\/li>\n<li>A cron job on the file server runs every two minutes (even-numbered ones) and puts all the usernames, UIDs, and GIDs into a protected file accessible via NFS export. This takes only a fraction of a second to run.<\/li>\n<li>A cron job on the rest of the Linux systems runs every two minutes (odd-numbered ones &#8212; good thing I got time synchronization working earlier, right?), reads the protected file and the contents of our main home directory NFS area, and determines which users should be added and deleted on its local passwd and shadow files. Even on a freshly-reinstalled system with no domain users added, this takes seconds to run.<\/li>\n<li>Whenever a user gets deleted via our deluser script, the rest of the Linux systems will pick up on the deletion by the fact that their home directory will be owned by root and inaccessible to all other users.<\/li>\n<\/ol>\n<p>You may have noticed that there&#8217;s nothing in any of this about the user&#8217;s actual password. It&#8217;s only stored on the domain controllers, never on any of the Linux systems. We use Kerberos and PAM to directly authenticate the users against the Active Directory servers. Code and configurations for this setup after the jump.<!--more--><\/p>\n<p>The main adduser.cae script on the file server:<\/p>\n<pre>\nfor user in $*; do\n\/usr\/sbin\/adduser --debug\n  --home \/home\/CAE\/$user\n  --ingroup users\n  --disabled-password\n  --gecos \" \"\n  --shell \/usr\/bin\/scponly\n  $user<\/pre>\n<p>This script calls adduser.local:<\/p>\n<pre>\n#!\/bin\/bash\nNEW_USERNAME=$1\nNEW_UID=$2\nNEW_GID=$3\nNEW_HOME=$4\n# Figure out which CAE Lab mailing list to add them to: students or\n# faculty\nCLASSIFICATION=$(ldapclassification ${NEW_USERNAME})\ncase $CLASSIFICATION in\n  \"Undergraduate\"|\"Graduate\")\n    echo \"${NEW_USERNAME} is a student\"\n    LISTNAME=\"cae-students\";;\n  \"Faculty\"|\"Staff\")\n    echo \"${NEW_USERNAME} is faculty\/staff\"\n    LISTNAME=\"cae-faculty\";;\n  *)\n    echo \"Assuming ${NEW_USERNAME} is faculty\/staff\"\n    LISTNAME=\"cae-faculty\";;\nesac\necho \"${NEW_USERNAME}@tntech.edu\" | \/usr\/sbin\/add_members -n - -w n $LISTNAME\n# Give the user a real name in the gecos field\nREALNAME=$(ldapname ${NEW_USERNAME})\nchfn -f \"${REALNAME}\" $1\nchmod 711 ${NEW_HOME}\nchown -R ${NEW_USERNAME} ${NEW_HOME}\nchgrp -R ${NEW_GID} ${NEW_HOME}<\/pre>\n<p>where ldapname and ldapclassification are just wrappers around ldapsearch that query the campus Active Directory and determine the user&#8217;s real name and student\/faculty\/staff classification based off Windows groups they&#8217;re members of in that domain.<\/p>\n<p>ldapname:<\/p>\n<pre>\n#!\/bin\/bash\nLDAPACCT=REDACTED\nLDAPPASSWD=REDACTED_TOO\n\nPCLABSERVER=REDACTED_MORE\nPCLABBASEDN=\"dc=pclab,dc=tntech,dc=edu\"\nPCLABDN=\"CN=${LDAPACCT},CN=Users,${PCLABBASEDN}\"\nTTUSERVER=MOSTLY_REDACTED\nTTUBASEDN=\"dc=tntech,dc=edu\"\nTTUDN=\"CN=${LDAPACCT},CN=Users,${TTUBASEDN}\"\n\nUSERNAME=$1\n\nPCLABREALNAME=$(ldapsearch -x -LLL\n    -D ${PCLABDN}\n    -w ${LDAPPASSWD}\n    -h ${PCLABSERVER}\n    -b ${PCLABBASEDN}\n    \"(CN=$USERNAME)\"\n    displayname | grep displayName)\nPCLABREALNAME=$(echo $PCLABREALNAME | cut -d  -f2-)\n\nif [ -z \"$PCLABREALNAME\" ]; then\n    # Check TTU Domain if PCLAB domain fails\n    TTUREALNAME=$(ldapsearch -x -LLL\n        -D ${TTUDN}\n        -w ${LDAPPASSWD}\n        -h ${TTUSERVER}\n        -b ${TTUBASEDN}\n        \"(CN=$USERNAME)\"\n        displayname | grep displayName)\n    TTUREALNAME=$(echo $TTUREALNAME | cut -d  -f2-)\nfi\n\nif [ -z \"$PCLABREALNAME\" -a -z \"$TTUREALNAME\" ]; then\n    echo \"Can't find a PCLAB or TTU domain entry for $USERNAME.\"\n    exit 1\nelif [ ! -z \"$PCLABREALNAME\" ]; then\n    REALNAME=\"${PCLABREALNAME}\"\nelif [ ! -z \"$TTUREALNAME\" ]; then\n    REALNAME=\"${TTUREALNAME}\"\nfi\necho \"${REALNAME}\"<\/pre>\n<p>ldapclassification<\/p>\n<pre>\n#!\/bin\/bash\nLDAPACCT=REDACTED\nLDAPPASSWD=REDACTEDLY_CORRECT\n\nPCLABSERVER=EXTRA_REDACTION\nPCLABBASEDN=\"dc=pclab,dc=tntech,dc=edu\"\nPCLABDN=\"CN=${LDAPACCT},CN=Users,${PCLABBASEDN}\"\nTTUSERVER=DOUBLE_SECRET_REDACTION\nTTUBASEDN=\"dc=tntech,dc=edu\"\nTTUDN=\"CN=${LDAPACCT},CN=Users,${TTUBASEDN}\"\n\nUSERNAME=$1\n\nPCLABREALNAME=$(ldapsearch -x -LLL\n    -D ${PCLABDN}\n    -w ${LDAPPASSWD}\n    -h ${PCLABSERVER}\n    -b ${PCLABBASEDN}\n    \"(CN=$USERNAME)\"\n    displayname | grep displayName)\n\nPCLABREALNAME=$(echo $PCLABREALNAME | cut -d  -f2-)\n\nif [ -z \"$PCLABREALNAME\" ]; then\n    # Check TTU Domain if PCLAB domain fails\n    TTUREALNAME=$(ldapsearch -x -LLL\n        -D ${TTUDN}\n        -w ${LDAPPASSWD}\n        -h ${TTUSERVER}\n        -b ${TTUBASEDN}\n        \"(CN=$USERNAME)\"\n        displayname | grep displayName)\n\n    TTUREALNAME=$(echo $TTUREALNAME | cut -d  -f2-)\nfi\n\nif [ -z \"$PCLABREALNAME\" -a -z \"$TTUREALNAME\" ]; then\n    echo \"Can't find a PCLAB or TTU domain entry for $USERNAME.\"\n    exit 1\nelif [ ! -z \"$PCLABREALNAME\" ]; then\n    CLASSIFICATION=$(ldapsearch -x -LLL\n        -D ${PCLABDN}\n        -w ${LDAPPASSWD}\n        -h ${PCLABSERVER}\n        -b ${PCLABBASEDN}\n        \"(CN=$USERNAME)\"\n        memberOf |\n        grep memberOf | egrep -i 'graduate|faculty|staff' | tail -1 | cut -d, -f1 | cut -d= -f2)\n    REALNAME=\"${PCLABREALNAME}\"\nelif [ ! -z \"$TTUREALNAME\" ]; then\n    CLASSIFICATION=$(ldapsearch -x -LLL\n        -D ${TTUDN}\n        -w ${LDAPPASSWD}\n        -h ${TTUSERVER}\n        -b ${TTUBASEDN}\n        \"(CN=$USERNAME)\"\n        memberOf |\n        grep memberOf | egrep -i 'gradudate|faculty|staff' | tail -1 | cut -d, -\nf1 | cut -d= -f2)\n    REALNAME=\"${TTUREALNAME}\"\nfi\necho \"${CLASSIFICATION}\"<\/pre>\n<p>On the file server, the following command is run every two minutes to generate the list of UIDs: <code>grep \/home\/CAE \/etc\/passwd | cut -d: -f1,3,4 | sort &gt; \/home\/CAE\/root\/current-username-uid-gid.txt<\/code><\/p>\n<p>syncusers, run every two minutes on clients:<\/p>\n<pre>\n#!\/bin\/bash\n\n#DEBUG=echo\n\ncase `uname` in\n    Linux)\n        # Delete overdue expired users (so overdue that we've already killed\n        # their home directories)\n        for USERNAME in `getent passwd | grep \/home\/CAE | cut -d: -f1`; do\n            if [ ! -d \/home\/CAE\/$USERNAME ] ; then\n            # User has no home directory, which means they've been deleted\n            # (since NFS home has already been mounted to run this script).\n                $DEBUG \/usr\/sbin\/userdel $USERNAME\n            fi\n        done\n\n        # Delete recently-expired users\n        cd \/home\/CAE\n        for USERNAME in `ls -l | egrep '^drwx------.* root ' | awk '{print $NF}'`; do\n            grep \"^$USERNAME:\" \/etc\/passwd &gt; \/dev\/null\n            if [ $? -eq 0 ]; then\n            # User exists, delete them\n                $DEBUG \/usr\/sbin\/userdel $USERNAME\n            else\n                # User already deleted, do nothing\n                :\n            fi\n        done\n\n        # Add new users\n        if [ -f \/home\/CAE\/root\/current-username-uid-gid.txt ]; then\n            for USERNAME_UID_GID in `cat \/home\/CAE\/root\/current-username-uid-gid.txt`; do\n                NEW_USERNAME=`echo $USERNAME_UID_GID | cut -d: -f1`\n                NEW_UID=`echo $USERNAME_UID_GID | cut -d: -f2`\n                NEW_GID=`echo $USERNAME_UID_GID | cut -d: -f3`\n                NEW_HOME=\/home\/CAE\/$NEW_USERNAME\n                grep \"^$NEW_USERNAME:\" \/etc\/passwd &gt; \/dev\/null\n                if [ $? -eq 0 ]; then\n                # User exists, do nothing\n                    continue\n                fi\n                $DEBUG \/usr\/sbin\/useradd -d $NEW_HOME -g $NEW_GID\n                    -s \/bin\/bash -p '*NP*' -u $NEW_UID $NEW_USERNAME\n            done\n        fi\n        ;;\n    *)\n        echo \"Syncusers not yet supported on `uname`\"\n        exit 1\n        ;;\nesac<\/pre>\n<p>And deluser.cae:<\/p>\n<pre>\n#!\/bin\/bash\nfor USERNAME in $*; do\necho \"User: $USERNAME\"\n\n# Remove user from lab mailing lists\necho -n \"- Removing from mailing lists: \"\nfor LIST in cae-students cae-faculty\ndo\n  echo -n \"$LIST \"\n  \/usr\/sbin\/remove_members $LIST $USERNAME@tntech.edu\ndone\necho\n\n# Protect user files for end-of-semester backup purposes\n\/bin\/chown -R root.root \/home\/CAE\/$USERNAME\n\/bin\/chmod -R go-rwx \/home\/CAE\/$USERNAME\n\n\/usr\/sbin\/userdel $USERNAME\n\ndone<\/pre>\n<p>Kerberos stuff: installed libpam-krb5, krb5-config, and krb5-user from Debian packages. Entered names of Windows domain controllers when prompted for KDCs. Entered name of Active Directory master server when prompted for the administrative server. Edited \/etc\/pam.d\/common-auth as follows:<\/p>\n<pre>\nauth    sufficient      pam_unix.so nullok_secure\nauth    required        pam_krb5.so use_first_pass<\/pre>\n<p>As long as the clocks are synchronized between the Linux client and the Windows domain controllers, this is all it takes. Only users listed in the local passwd files are allowed access, and we authenticate them from the domain controllers. No changes to the Windows side required at all.<\/p>\n<p><strong>Update:<\/strong> oh, <a href=\"http:\/\/www.samba.org\/samba\/docs\/man\/Samba-HOWTO-Collection\/winbind.html\">winbind<\/a>, how I love you. Last time I looked at winbind (circa 2001), it either really sucked, or else I horribly misconfigured it. In particularly, I had a problem where each Unix system would generate its own UIDs dynamically, and none of them would match. This made NFS+winbind impossible. Now with winbind generating UIDs from the Active Directory RIDs, everything matches, and I can make it the centerpiece of my authentication setup.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The whole point of an authentication service is that it allows the client to prove itself to be trustworthy, or at least to prove itself to be the same nefarious character it claims. &#8212; Infrastructures.org I want to make our existing Active Directory the source for all the following: Lists of users allowed to log &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/sites.tntech.edu\/renfro\/2007\/05\/16\/authentication-servers\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Authentication Servers&#8221;<\/span><\/a><\/p>\n","protected":false},"author":87,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,7],"tags":[],"class_list":["post-19","post","type-post","status-publish","format-standard","hentry","category-debian","category-infrastructures","entry"],"_links":{"self":[{"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/posts\/19","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/comments?post=19"}],"version-history":[{"count":1,"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/posts\/19\/revisions"}],"predecessor-version":[{"id":502,"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/posts\/19\/revisions\/502"}],"wp:attachment":[{"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/media?parent=19"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/categories?post=19"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sites.tntech.edu\/renfro\/wp-json\/wp\/v2\/tags?post=19"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}