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.
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.
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