Joining Linux to Active Directory

linux active directory microsoft rfc2307 shell script

Microsoft's Active Directory. No escape, love it or hate it its the industry standard. Let me pull out my cane and shake it at you while I tell you that joining a linux host to active directory used to be a real chore. You young whippersnappers have no idea. I remember taking a stab at it circa 2006, taking a whole three day weekend and pouring over 15+ guides trying to join my ubuntu 6.06 laptop to my homelab domain only to be unsuccessful. I seem to remember I eventually got it sketchily working with a product called Likewise Open.

Fast forward to today, there are many many "Linux Active Directory Bridge" software available out there on the free market. You can pay a lot for a Cadillac solution like Centrify and I can't say with a straight face its not worth every penny. Oh its super nice, it will allow near full integration of your linux machines in AD. Supports GPOs and OU structures, allows you to manage and push out .conf files based on AD OU. But what if you're poor like me or just run a homelab? Centrify used to offer a free version called Centrify Express but I guess it was eating into their profit because they seem to have retired it. That Likewise product I mentioned earlier was FOSS and became PBIS Open which is the free/OSS version of Beyond Trust's AD bridge. The problem I have with all these free versions of the third party solutions is they usually require a client installation package and the vendor does a crap job at keeping it up to date and supporting latest versions of your distro of choice, thats if they support your distro at all. Additionally they impose some annoying artificial limitations in order to push you into purchasing the enterprise edition to unlock all features. The most infuriating of these artificial limitations in my opinion is ignoring RFC2307 attributes configured in the directory. This can cause you a shit storm if you're not careful or if you use shared network file systems off something like a NAS.

RFC What?

RFC2307 is simply a standard for storing POSIX attributes in your directory. Past versions of Active Directory prior to 2012 used to offer an official plug in from Microsoft to extend your directory to support this and add easy to admin GUI editor inside of ADUC for maintaining these attributes. However, with Windows server 2012 and newer they dropped support. It doesnt mean you cant manually store RFC2307 attributes in AD though just that they'll no longer offer that easy to maintain GUI for interacting with them. There are already a few good guides out there for manually configuring these so I wont reinvent the wheel here. If you are starting from scratch with a brand new AD environment and want to join linux to that domain you should first follow the instructions on the previously linked guides to create the RFC2307 attributes in ADUC for your users and groups.

Enter SSD

So back on topic, what if you want a free, open, and supportable way to join your linux distro of choice? Enter sssd. A bit more work to setup but it works fabulous, it even caches correctly on a laptop that roams around and cant always reach the domain controller and of course you can set it to use RFC2307 attributes from AD which is a must for my network.

Below is a script I wrote recently to automate the chore of setting up RHEL/CentOS/Fedora/Pop machines to join AD to my tastes. My tastes involve of course using RFC2307 attributes, ensuring all Domain Admins have sudo rights, and setting the user login name and home directory to a simple $username as opposed to DOMAIN\$username or $username@domain. This means home directories will be /home/$username for AD users because there are some silly developers out there that expect that format (I am looking at you with firey hatred snapd!)

Fedora and Pop are my distro's of choice but I think one day I will expand this script to support more common distros. Its on my TODO list anyway, right up there with cleaning the gutters.

#!/bin/bash
#######################################################################
#   Originally written for tech.luc3nt.com by [email protected]                  #
#######################################################################

bold=$(tput bold)
normal=$(tput sgr0)

echo "executing $0"
echo ""

# Now lets build our functions
function display_help () {
    echo "${bold}######################################"
    echo "# LINUX ACTIVE DIRECTORY JOIN SCRIPT #"
    echo "######################################${normal}"
    echo ""
    echo "${bold}WARNING:${normal}This script joins a Linux machine to a Windows Active Directory Domain"
    echo "with realm (using RFC2307 attributes from AD); if successful it will update sudoers"
    echo "file and make joined domain default domain suffix. ${bold}Currently, this script only"
    echo "supports RHEL8/CentOS8/Fedora 3x.${normal}"
    echo ""
    echo "${bold}USAGE:${normal} $0 [OPTION]... | $0 [DomainName] [DomainUser]"
    echo ""
    echo "${bold}OPTIONS:${normal}"
    echo "  -h | --help            Prints this help message and exits"
    echo "  [DomainName]           The AD domain name that you wish to join"
    echo "  [DomainUser]           The administrator user with rights to join AD"
    echo ""
    echo "EXAMPLE1: $0 --help"
    echo "EXAMPLE2: $0 domain.example.com administrator"
    echo ""
    echo "${bold}######################################${normal}"
}

function detect_distro () {

arch=$(uname -m)
kernel=$(uname -r)
if [ -n "$(command -v lsb_release)" ]; then
    distroname=$(lsb_release -s -d)
elif [ -f "/etc/os-release" ]; then
    distroname=$(grep PRETTY_NAME /etc/os-release | sed 's/PRETTY_NAME=//g' | tr -d '="')
elif [ -f "/etc/redhat-release" ]; then
    distroname=$(cat /etc/redhat-release)
elif [ -f "/etc/SuSE-release" ] ; then
    distroname=$(cat /etc/SuSE-release)
elif [ -f "/etc/debian_version" ]; then
    distroname="Debian $(cat /etc/debian_version)"
else
    distroname="$(uname -s) $(uname -r)"
fi

echo "${bold}${distroname} distro detected${normal}"

}

function install_dependencies () {
# Simple array of dependency packages, add extra packages here.
if [[ ${distroname} = *"CentOS Linux 8"* ]]; then
    dependencies=(sssd realmd oddjob oddjob-mkhomedir adcli samba-common samba-common-tools krb5-workstation openldap-clients python3-policycoreutils bind-utils authselect-compat)
elif [[ ${distroname} = *"Red Hat Enterprise Linux 8"* ]]; then
    dependencies=(sssd realmd oddjob oddjob-mkhomedir adcli samba-common samba-common-tools krb5-workstation openldap-clients python3-policycoreutils bind-utils authselect-compat)
elif [[ ${distroname} = *"Fedora 3"* ]]; then
    dependencies=(sssd realmd oddjob oddjob-mkhomedir adcli samba-common samba-common-tools krb5-workstation openldap-clients python3-policycoreutils bind-utils authselect-compat)
elif [[ ${distroname} = *"Pop!_OS 20.10"* ]]; then
    dependencies=(realmd sssd sssd-tools libnss-sss libpam-sss adcli samba-common-bin oddjob oddjob-mkhomedir packagekit heimdal-clients msktutil)
elif [[ ${distroname} = *"Ubuntu 20.10"* ]]; then
    dependencies=(realmd sssd sssd-tools libnss-sss libpam-sss adcli samba-common-bin oddjob oddjob-mkhomedir packagekit heimdal-clients msktutil)
else
    echo "${bold}Sorry your linux distribution is not supported by this script.${normal}"
    exit
fi
    echo ""
    echo "${bold}Installing Dependencies for ${distroname}${normal}"
    echo ""

if [[ ${distroname} = *"Pop!_OS 20.10"* ]]; then
    for Dependency in "${dependencies[@]}"
            do
            apt install $Dependency -y 2>1 >/dev/null
            # Verify dependencies have installed successfully.
                if [ $? -ne 0 ]; then
                    echo "Failed to install dependency $Dependency. Exiting Script"
                    exit
                else
                    echo "Successfully installed (or discovered) dependency $Dependency"
                fi
            done    
else
    for Dependency in "${dependencies[@]}"
        do
            yum install $Dependency -y 2>1 >/dev/null
            # Verify dependencies have installed successfully.
                if [ $? -ne 0 ]; then
                    echo "Failed to install dependency $Dependency. Exiting Script"
                    exit
                else
                    echo "Successfully installed (or discovered) dependency $Dependency"
                fi
        done    
fi
} 

# Help will always be given at hogwarts for those who ask

if [ -z "$1" ] || [ "$1" = '-h' ] || [ "$1" = '--help' ]
then
    display_help
    exit 0

# Test domain is reachable
else
    # Assume variables and run basic ping 

    DomainName=$1
    DomainUser=$2

    ping -c 1 -W 1 $DomainName 2>1 >/dev/null
         if [ $? -ne 0 ]; then
            echo "ERROR: Unable to locate $DomainName. Please verify connectivity or DNS."
            exit
         fi
fi

# Verify root access
[ "$EUID" -eq 0 ] || {
  echo 'Please run with sudo or as root.'
  exit 1
}

# Oops one more function (requires $DomainName)
function verify_domain () {
    # Verify DNS entries for the domain
    which nslookup 2>1 >/dev/null
    if [ $? -ne 0 ]; then
      yum install bind-utils -y 2>1 >/dev/null
    fi
    nslookup $DomainName 2>1 >/dev/null
    if [ $? -ne 0 ]; then
    echo "ERROR: Unable to verify DNS entry for $DomainName."
    exit 1
    fi
}

# Determine OS platform
detect_distro
install_dependencies
verify_domain

# Join domain with realm (use RFC2307 attributes from AD); if successful update sudoers file and make joined domain default domain suffix.
echo ""
echo "${bold}Attempting join AD domain $DomainName, Please enter $DomainUser's password when prompted.${normal}"
read -sp "Enter $DomainUser's password: " password
echo

echo $password | realm join $DomainName --user=$DomainUser --automatic-id-mapping=no 

if [ $? -eq 0 ]; then
    echo "Successfully joined $DomainName"
    echo ""
    authselect select sssd 2>1 >/dev/null
    authselect select sssd with-mkhomedir 2>1 >/dev/null
    echo "Permitting local login for all members of $DomainName"
    realm permit -a
    echo ""
    echo "Granting sudo rights to all Domain Admins"; echo "%domain\ admins ALL=(ALL) ALL" > /etc/sudoers.d/domain-admins
    echo ""
    echo "Setting the default login domain to $DomainName. Configuring simple domain username login, no \ or @ required!"
    sed "/^services = nss, pam.*/a default_domain_suffix = $DomainName" /etc/sssd/sssd.conf 2>1 >/dev/null
    sed -i -e 's/use_fully_qualified_names = True/use_fully_qualified_names = False/g' /etc/sssd/sssd.conf 2>1 >/dev/null
    sed -i -e 's/%u@%d/%u/g' /etc/sssd/sssd.conf 2>1 >/dev/null
    if [[ ${distroname} = *"Pop!_OS 20.10"* ]]; then
    sed -i -e '/pam_unix.so/a\session required\tpam_mkhomedir.so skel=/etc/skel umask=0077' /etc/pam.d/common-session 2>1 >/dev/null
    fi
    # Restart SSSD
    if [ -n "/run/systemd/system" ]; then
        /usr/bin/systemctl restart sssd
        sss_cache -E
        /usr/bin/systemctl enable --now oddjobd.service

    else
        service sssd restart
        sss_cache -E
        chkconfig oddjobd on
    fi
        adduser administrator sudo 2>1 >/dev/null
        adduser $DomainUser sudo 2>1 >/dev/null
        adduser administrator wheel 2>1 >/dev/null
        adduser $DomainUser wheel 2>1 >/dev/null
    echo ""
    echo "${bold}Successfully joined domain $DomainName! Rebooting this system is recommended.${normal}"
    exit

else
    echo ""
    echo "${bold}Could not join domain $DomainName. Verify AD computer account and error output.${normal}"
    exit
fi

Previous Post Next Post