A unique problem recently presented itself- how to allowing multiple users to access a Linux VM in a secure fashion. Generic user accounts seemed like a poor idea to me, as they would likely end up being mismanaged. And shared accounts are, of course, a terrible idea. So, what to do? Well, I am, of course, already maintaining Active Directory – could I just use that?
The answer is YES. I was able to configure a Debian Linux virtual machine to authenticate against Active Directory. This helps maintain a healthy security posture without incurring additional administrative load. Also, to assist with securing this box, since many users would be using it, I modified the SSH config file /etc/ssh/sshd_config to contain ‘PermitRootLogin no’. This keeps anyone from being able to authenticate as the root user via SSH. However, this can potentially be bypassed by using the SU command. To counteract this, one can modify the file /etc/pam.d/su to contain ‘auth required pam_wheel.so’ this forces users to be a member of the wheel group before they can use SU.
To configure AD authentication, I used a page from the Debian wiki – https://wiki.debian.org/AuthenticatingLinuxWithActiveDirectorySssd. I found some small flaws with the documentation, so I am going to borrow very heavily from it, while making some small adjustments. But, it should be noted that I did not pioneer this method.
Firstly, the host should be named using an FQDN corresponding with the domain you will be joining. The wiki documentation states that you should configure DNS to resolve against the Domain Controller. However, I did not do this. You should make the determination if this makes sense for your use case.
Secondly, you need to synchronize time with your primary domain controller. This is because Kerberos requires time to be in sync. Install NTP and configure your primary DC as your NTP server.
apt-get install ntp
nano /etc/ntp.conf
# remove default NTP pool servers, such as
# 0.debian.pool.ntp.org, and replace w/ your DC or DNS server
# example: server dc01.ad.example.com
NSCD duplicates some of the functionality of sssd, and must be disabled:
systemctl stop unscd
systemctl disable unscd
rm /var/run/nscd/socket
Kerberos is a network authentication protocol designed to provide strong authentication for client/server applications by using secret-key cryptography. It was developed as part of Project Athena at MIT in the 1980s. In Active Directory, Kerberos is used for providing mutual authentication between users and services. It is a critical component for this process to function. Install Kerberos and edit its config file accordingly. Remember that this is case-sensitive.
[libdefaults]
# This relation identifies the default realm to be used in a client host's
# Kerberos activity.
default_realm = AD.EXAMPLE.COM
# Indicate whether DNS TXT records should be used to determine the Kerberos
# realm of a host. The default is not to use these records.
dns_lookup_realm = false
# Indicate whether DNS SRV records should be used to locate the KDCs
# and other servers for a realm, if they are not listed in the information
# for the realm. The default is to use these records.
# We set this explicitly since we're setting the admin_server anyway.
dns_lookup_kdc = false
# The value of this tag is the default lifetime for initial tickets. The
# default value for the tag is 1 day (1d).
#ticket_lifetime = 24h
# The value of this tag is the default renewable lifetime for initial
# tickets. The default value for the tag is 0.
renew_lifetime = 7d
# If this flag is set, initial tickets by default will be forwardable.
# The default value for this flag is false.
# See https://web.mit.edu/kerberos/krb5-devel/doc/user/tkt_mgmt.html for details.
#forwardable = true
# If this flag is true, reverse name lookup will be used in addition to
# forward name lookup to canonicalizing hostnames for use in service principal names.
# If dns_canonicalize_hostname is set to false, this flag has no effect.
# The default value is true.
rdns = false
[realms]
AD.EXAMPLE.COM = {
# This relation identifies the host where the administration server
# is running. Typically this is the Master Kerberos server.
# Required setting - cannot be looked up via DNS.
admin_server = DC01.AD.EXAMPLE.COM
# The name or address of a host running a KDC for that realm.
# This could be looked up via DNS (dns_lookup_kdc) but we must
# set the admin_server anyway, and this has the same value.
kdc = DC01.AD.EXAMPLE.COM
}
[domain_realm]
# The [domain_realm] section provides a translation from a hostname to
# the Kerberos realm name for the services provided by that host.
#
# The tag name can be a hostname, or a domain name, where domain names
# are indicated by a prefix of a period ('.') character. The value of
# the relation is the Kerberos realm name for that particular host or
# domain. Host names and domain names should be in lower case.
#
# If no translation entry applies, the host's realm is considered to
# be the hostname's domain portion converted to upper case.
.ad.example.com = AD.EXAMPLE.COM
[logging]
# Log everything to syslog. Default is severity of ERR and facility of AUTH.
default = SYSLOG
Install Samba SMBD, stopping unused daemons. Then, configure its config file accordingly.
apt-get install samba samba-common
systemctl stop nmbd
systemctl disable nmbd
systemctl disable samba
systemctl disable samba-ad-dc
nano /etc/samba/smb.conf
# Configures Samba suite for AD
# These parameters seem to work on the devtest domain.
[global]
# Netbios name for the AD domain in upper-case
workgroup=ADEX
# This controls whether the client is allowed or required to use SMB
# signing. Possible values are auto, mandatory and disabled.
#
# When set to auto, SMB signing is offered, but not enforced. When
# set to mandatory, SMB signing is required and if set to disabled,
# SMB signing is not offered either.
#
# Default: client signing = auto
client signing = auto
# This variable controls whether Samba clients will try to use Simple
# and Protected NEGOciation (as specified by rfc2478) with supporting
# servers (including WindowsXP, Windows2000 and Samba 3.0) to agree
# upon an authentication mechanism. This enables Kerberos authentication
# in particular.
#
# Default: client use spnego = yes
client use spnego = yes
# This option specifies the kerberos realm to use. The realm is used as the
# ADS equivalent of the NT4 domain. It is usually set to the DNS name of the
# kerberos server. Since it is kerberos it is in capital letters.
realm=AD.EXAMPLE.COM
# In this mode, Samba will act as a domain member in an ADS realm. To operate
# in this mode, the machine running Samba will need to have Kerberos
# installed and configured and Samba will need to be joined to the ADS realm
# using the net utility.
security=ads
# Use the keytab to store secrets for authenticating against kerberos
# and to identify the kerberos server.
kerberos method = secrets and keytab
# Logging settings
# This option allows you to override the name of the Samba log file (also
# known as the debug file).
#
# This option takes the standard substitutions, allowing you to have separate
# log files for each user or machine.
#
# No default
#
# Example: log file = /usr/local/samba/var/log.%m
log file = /var/log/samba/smbd.log
# The value of the parameter (a astring) allows the debug level (logging
# level) to be specified in the smb.conf file.
# Values seem to be 0 to 10.
#
# Default: log level = 0
log level = 10
# This option (an integer in kilobytes) specifies the max size the log file
# should grow to. Samba periodically checks the size and if it is exceeded it
# will rename the file, adding a .old extension.
#
# A size of 0 means no limit.
#
# Default: max log size = 5000
max log size = 500
# Turn off printing to avoid log spam
load printers = no
printing = bsd
printcap name = /dev/null
disable spoolss = yes
# Default ID mapping configuration for local BUILTIN accounts
# and groups on a domain member. The default (*) domain:
# - must not overlap with any domain ID mapping configuration!
# - must use a read-write-enabled back end, such as tdb.
idmap config * : backend = tdb
idmap config * : range = 3000-7999
# - You must set a DOMAIN backend configuration
# idmap config for the SAMDOM domain
idmap config ADEX:backend = ad
idmap config ADEX:schema_mode = rfc2307
idmap config ADEX:range = 10000-50000
idmap config ADEX:unix_nss_info = yes
When you’re done, run testparm and investigate any errors. Then, restart smbd and enable it on startup.
testparm
systemctl restart smbd
systemctl enable smbd
Install SSSD, which allows you to access remote directories and authentication mechanisms. It won’t start correctly because its config file doesn’t currently exist, and it hasn’t been joined to the domain. I have modified the order of events slightly. First, create the config file /etc/sssd/sssd.conf.
apt-get install sssd
nano /etc/sssd/sssd.conf
# Configuration for the System Security Services Daemon (SSSD)
[sssd]
# Syntax of the config file; always 2
config_file_version = 2
# Services that are started when sssd starts
services = nss, pam
# List of domains in the order they will be queried
domains = AD.EXAMPLE.COM
# Configuration for the AD domain
[domain/AD.EXAMPLE.COM]
# Use the Active Directory Provider
id_provider = ad
# Use Active Directory for access control
access_provider = ad
# Turn off sudo support in sssd - we're doing it directly in /etc/sudoers.d/
# and leaving this enabled results in spurious emails being sent to root
sudo_provider = none
# UNIX and Windows use different mechanisms to identify groups and users.
# UNIX uses integers for both; the challenge is to generate these consistently
# across all machines from the objectSID.
#
# Active Directory provides an objectSID for every user and group object in
# the directory. This objectSID can be broken up into components that represent
# the Active Directory domain identity and the relative identifier (RID) of the
# user or group object.
#
# The SSSD ID-mapping algorithm takes a range of available UIDs and divides it into
# equally-sized component sections - called "slices"-. Each slice represents
# the space available to an Active Directory domain.
#
# The default configuration results in configuring 10,000 slices, each capable
# of holding up to 200,000 IDs, starting from 10,001 and going up to
# 2,000,100,000. This should be sufficient for most deployments.
ldap_id_mapping = true
# Define some defaults for accounts that are not already on this box.
# We appear to need these settings as well as the PAM configuration.
fallback_homedir = /home/%d/%u
default_shell = /bin/bash
skel_dir = /etc/skel
# When running in an unprivileged Linux container these settings
# should be uncommented.
#ldap_idmap_range_min = 10000
#ldap_idmap_range_max = 50000
#ldap_idmap_range_size = 1000
Then, modify the permissions of your config file. Then, modify /etc/nsswitch.conf and ensure the below are contained within.
chmod 0600 /etc/sssd/sssd.conf
passwd: compat sss
group: compat sss
netgroup: nis sss
sudoers: files
PAM provides a central place for managing authentication. Create the file /usr/share/pam-configs/my-ad and ensure it contains the following:
Name: Guestline AD user home management
Default: yes
Priority: 127
Session-Type: Additional
Session-Interactive-Only: yes
Session:
required pam_mkhomedir.so skel=/etc/skel/ umask=0022
Update PAM to use this config file with the following:
/usr/sbin/pam-auth-update --package
For my use case, I did not want to grant any users sudo privileges, but that can be managed by creating the following file at /etc/sudoers.d/ad-linux-admins. This assumes there exists a Linux Admins security group in AD.
%Linux\ Admins ALL = (ALL) ALL
To join the domain, the Debian wiki provides a script. It must be executed with the SAMAccountName of a Domain Admin as an argument. Here is the script:
# Script to handle the process of joining the domain.
# Run with username where:
# username is the SAM name for the domain admins account - e.g. brownm for martin.brown
#
# The script produces lots of output to help with debugging any issues. See
# https://www.redhat.com/en/files/resources/en-rhel-intergrating-rhel-6-active-directory.pdf
# for a full description of the process (albeit RedHat rather than Debian).
#
# Note that it is very important that the hostname of the machine is set up correctly!
if [ "$#" != 1 ]
then
echo "Usage: join-ad.sh username"
exit 1
fi
username=$1
domain=$(hostname --domain)
echo "Using account ${username} to join domain ${domain}..."
echo ""
# Quit on error
set -e
# Uncomment to debug
#set -x
# Upper-case version of the domain name
upper_domain=$(echo ${domain} | tr [a-z] [A-Z])
# Find the short hostname and get an upper-case version of it
short_hostname=$(hostname --short)
upper_short_hostname=$(echo ${short_hostname} | tr [a-z] [A-Z])
# Log into the domain as the administrator, asking user for password
# The domain part must be in upper-case
echo "Logging into domain as the administrator"
/usr/bin/kinit "${username}@${upper_domain}"
echo ""
# List what kerberos sent back
echo "Listing kerberos tickets for the domain administrator:"
echo "------------------------------------------------------------------------"
klist
echo ""
# Join AD and put the machine credentials in the krb5.keytab
echo "Requesting domain join using administrator kerberos ticket"
net ads join -k
# List the machine credentials
echo "Listing kerberos tickets for the machine:"
echo "------------------------------------------------------------------------"
klist -k
echo ""
# Wait for 5s to allow everything to catch up - sometimes 5s isn't enough
echo "Waiting for everything to catch up..."
sleep 5
echo ""
# Sign in using the machine credentials
echo "Signing in using machine credentials ${upper_short_hostname}$"
kinit -k ${upper_short_hostname}$
echo ""
# Did it work?
joinedAd=$?
if [ $joinedAd -ne 0 ]
then
echo "Error: could not join the domain with machine credentials ${upper_short_hostname}$"
exit 1
else
echo "Joined the domain using machine credentials ${upper_short_hostname}$"
echo ""
echo "Listing kerberos machine ticket:"
echo "------------------------------------------------------------------------"
klist
echo ""
# Now restart SSSD and everything should be happy :-)
echo "Enabling and restarting sssd"
systemctl enable sssd
systemctl restart sssd
if [ $? -ne 0 ]
then
echo "Error: could not start the System Security Services Daemon (SSSD)"
exit 1
else
echo "System Security Services Daemon (SSSD) restarted and enabled."
echo "AD should now be working!"
fi
fi
exit 0
If you install applications using snap after this process, you will have a problem because the user home directory will (by default) be /home/AD.EXAMPLE.COM/<user>. To allow snap to locate users’ home directory correctly, make the following configuration change. Modify the file /etc/apparmor.d/tunables/home.d/ubuntu with the following, and be sure to include the trailing slash.
@{HOMEDIRS}+=/home/AD.EXAMPLE.COM/
Then, restart the following services:
systemctl restart apparmor.service snapd.apparmor.service snapd.service snapd.socket