What’s this about?
In this post, we’ll go through the steps of getting a computer, running GNU/Linux Debian 12 “bookworm”, be a member of an Active Directory domain.
Specifically, this computer will not be a server. What we’re aiming for is a workstation behaving like a Windows domain member: the end user will be able to log in using his credentials and access corporate network shares and internal web applications seamlessly.
These steps are designed to be generic enough so they can be deployed to an entire fleet of GNU/Linux computers through a master image that could potentially be cloned and restored over a network with CloneZilla.
Let’s Do It
The Basics
Required Packages
We will use SSSD
and pam_mount
to provide domain joining, authentication, authorization and remote resources to the system.
SSSD
(standing for System Security Services Daemon), a Red Hat project, is a set of services and tools that manage the domain connectivity part. SSSD
has different, configurable providers like sssd-ldap
or sssd-ad
and provides interfaces to PAM
and KRB5
, allowing common GNU/Linux programs to be backed by distant identity, authentication and authorization mechanisms without them having to be linked to another set of libraries or support such protocols internally.
pam_mount
is a PAM
module that can, among other things, automatically mount partitions, shares, virtually anything that has a mountable filesystem. It can be configured globally or per-user. We will use pam_mount
to automatically mount network shares at user login. This will be configured at the system level, meaning that the user cannot opt out of this automatic mounting nor modify it without root
privileges.
# apt install realmd sssd samba-common krb5-user adcli libsss-sudo sssd-tools libsasl2-modules-ldap packagekit libpam-mount
Joining the Domain
Joining the domain is just a matter of configuring the basics of KRB5
and using realm join
(you will need Domain Administrator credentials if you’ve restricted join operations to administrators):
# nano /etc/krb5.conf
[libdefaults] default_realm = MYDOMAIN.LOCAL
# realm join --user=[ADMIN USERNAME] MYDOMAIN.LOCAL -v
# realm list
mydomain.local type: kerberos realm-name: MYDOMAIN.LOCAL domain-name: mydomain.local configured: kerberos-member server-software: active-directory client-software: sssd required-package: sssd-tools required-package: sssd required-package: libnss-sss required-package: libpam-sss required-package: adcli required-package: samba-common-bin login-formats: %U@mydomain.local login-policy: allow-realm-logins
realm list
outputs the configured domains and through login-policy
indicates that any account existing in the Active Directory domain can be used for logging in.
Tuning our Membership
We will now configure SSSD
‘s global and per-domain settings to streamline its operations.
Since we have only one domain, we set default_domain_suffix
to our domain name so that it’s possible to log in using short user names.
To better integrate SSSD
with Debian 12, we set implicit_pac_responder
to false
and remove the services
parameter.
SSSD
‘s services correspond to socket
units dynamically handled by systemd
, having them statically declared in sssd.conf
messes with that. Also, having implicit_pac_responder
set to true
periodically crashes some SSSD
services, and because it is not essential we disable it.
# nano /etc/sssd/sssd.conf
[sssd] domains = mydomain.local default_domain_suffix = mydomain.local config_file_version = 2 implicit_pac_responder = false [domain/mydomain.local] access_provider = ad id_provider = ad krb5_realm = MYDOMAIN.LOCAL krb5_store_password_if_offline = True krb5_ccachedir = /tmp krb5_ccname_template = FILE:%d/.krb5cc_%U ad_domain = mydomain.local full_name_format = %1$s default_shell = /bin/bash fallback_homedir = /home/%u@%d override_homedir = /home/%u@%d cache_credentials = True use_fully_qualified_names = False ldap_id_mapping = True realmd_tags = manages-system joined-with-adcli dydns_update = False
In our domain section, we define a predictable path to the KRB5
ticket cache file in the /tmp
directory, it’ll be useful later. We also configure our users’ home directory (/home/user@mydomain.local
) and allow credential caching in the event of a network loss while the user is logged in.
Now it’s time to use pam-auth-update
and enable the following PAM modules:
# pam-auth-update
[*] SSS authentication [*] Mount volumes for user [*] Create home directory on login
Enabling pam_sss
, pam_mkhomedir
and pam_mount
enables the system to:
- Validate the user’s credentials against the domain configured in SSSD;
- Create the user’s home directory upon first login;
- Mount the user’s network shares.
pam_mkhomedir
can further be configured to use a more restrictive umask
:
# nano /etc/pam.d/common-session
session required pam_unix.so session optional pam_sss.so session optional pam_systemd.so session optional pam_mkhomedir.so umask=0077 session optional pam_mount.so
We can also configure sudoers
based on a domain group of privileged users:
# nano /etc/sudoers.d/mydomain
%privilegedusers ALL=(ALL:ALL) ALL
Lastly, we configure the Samba basics to point to our domain:
# nano /etc/samba/smb.conf
[global] workgroup = MYDOMAIN realm = MYDOMAIN.LOCAL encrypt passwords = yes client protection = encrypt
Fix GVFS
GVFS
is a userspace virtual filesystem that enables unprivileged users to securely and dynamically mount local or remote storage without requiring root
. GVFS
can use the user’s Kerberos ticket to mount an SMB share without asking for credentials (SSO). Unfortunately, the KRB5CCNAME
variable isn’t set in gvfsd
‘s environment, meaning that, by default, it cannot use such tickets.
What we do is edit the service
unit file and explicitly set the KRB5CCANME
environment variable with a value that can predictably match the one we defined in sssd.conf
:
# nano /usr/lib/systemd/user/gvfs-daemon.service
[Unit] Description=Virtual filesystem service PartOf=graphical-session.target [Service] Environment="KRB5CCNAME=FILE:/tmp/.krb5cc_%U" ExecStart=/usr/libexec/gvfsd Type=dbus BusName=org.gtk.vfs.Daemon Slice=session.slice
Automatically mount corporate shares
pam_mount
gets its configuration from user-defined and system-global XML files.
The following configuration file tells pam_mount
to:
- disable its verbose debug output;
- disregard user-defined configuration files;
- deny security-unwise
mount
options; - unmount everything when the user’s last session exits;
- mount a
CIFS
volume at the remote location on a mountpoint in the user’s home directory with the given options (duplicate thevolume
element for additional mounts).
# nano /etc/security/pam_mount.conf.xml
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE pam_mount SYSTEM "pam_mount.conf.xml.dtd"> <pam_mount> <debug enable="0" /> <!-- <luserconf name=".pam_mount.conf.xml" /> --> <mntoptions deny="suid,dev,exec" /> <mntoptions allow="*" /> <mntoptions require="nosuid,nodev,noexec" /> <logout wait="0" hup="no" term="no" kill="no" /> <volume fstype="cifs" sgrp="domain users" server="myserver.mydomain.local" path="share" mountpoint="~/Network Drives/Share" options="vers=3.0,sec=krb5i,cruid=%(USERUID),nodev,nosuid,noexec,rw" /> <mkmountpoint enable="1" remove="true" /> </pam_mount>
the sgrp
attribute is mandatory and should always be set to the by-default, domain users
group.
The mount options explicitly require SMB3+
and a Kerberos authentication with signing
enforced. To enforce encryption
instead (can induce a bigger load), use krb5p
in lieu of krb5i
. The cruid
option is also mandatory and should always be set to %(USERUID)
.
Is Everything Alright?
Test #1: System Status
After a reboot, the first test should always be to check if something’s not right at the system level:
# systemctl status
● debian12 State: running Units: 365 loaded (incl. loaded aliases) Jobs: 0 queued Failed: 0 units Since: Thu 2023-12-28 11:45:42 CET; 2h 15min ago systemd: 252.19-1~deb12u1 CGroup: / ├─init.scope │ └─1 /sbin/init ├─system.slice │ ├─sssd-ifp.service │ │ └─4293 /usr/libexec/sssd/sssd_ifp --uid 0 --gid 0 --dbus-activated --logger=files │ ├─sssd-nss.service │ │ └─4092 /usr/libexec/sssd/sssd_nss --logger=files --socket-activated │ ├─sssd.service │ │ ├─649 /usr/sbin/sssd -i --logger=files │ │ └─857 /usr/libexec/sssd/sssd_be --domain mydomain.local --uid 0 --gid 0 --logger=files
Observation: the global state isn’t degraded and SSSD
‘s services are running
Result: PASS
Test #2: Domain Status
We know that Debian is properly running, let’s see if SSSD
is doing well too:
# sssctl domain-status mydomain.local
Online status: Online Active servers: AD Global Catalog: dc01.mydomain.local AD Domain Controller: dc01.mydomain.local Discovered AD Global Catalog servers: - dc01.mydomain.local - dc02.mydomain.local Discovered AD Domain Controller servers: - dc01.mydomain.local - dc02.mydomain.local
Observation: domain is online, servers are properly discovered
Result: PASS
Test #3: Hello, I’m logging in
It is now time to log in for the first time:
root@debian12:~# login myuser
Password: ************ Linux debian12 6.1.0-16-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.67-1 (2023-12-12) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Creating directory "/home/myuser@mydomain.local".
Hooray! The user was able to log in using his existing credentials. pam_mkhomedir
successfully created their home directory.
myuser@debian12:~$ echo $KRB5CCNAME
FILE:/tmp/.krb5cc_[UID]
myuser@debian12:~$ ls -al /tmp/.krb5cc*
-rw------- 1 myuser domain users 1470 28 déc. 13:39 /tmp/.krb5cc_[UID] -rw------- 1 anotheruser domain users 1625 28 déc. 13:37 /tmp/.krb5cc_[ANOTHER UID]
myuser@debian12:~$ klist
Ticket cache: FILE:/tmp/.krb5cc_[UID] Default principal: myuser@MYDOMAIN.LOCAL Valid starting Expires Service principal 28/12/2023 13:39:29 28/12/2023 23:39:29 krbtgt/MYDOMAIN.LOCAL@MYDOMAIN.LOCAL renew until 29/12/2023 13:39:29
SSSD
successfully created the user’s Kerberos ticket cache file. The environment variable is properly populated. You can even see that another user is also logged in and has their ticket cache file in /tmp
, only accessible to them.
myuser@debian12:~$ ls -al /home/myuser@mydomain.local
total 40 drwx------ 1 myuser domain users 84 28 déc. 13:39 . drwxr-xr-x 1 root root 144 28 déc. 13:39 .. -rw------- 1 myuser domain users 220 28 déc. 13:39 .bash_logout -rw------- 1 myuser domain users 3526 28 déc. 13:39 .bashrc -rw------- 1 myuser domain users 807 28 déc. 13:39 .profile
The user’s home directory also has the required restrictive permissions.
Observation: the system successfully handled the user’s first login
Result: PASS
Test #4: GVFS can do SSO
$ cat /proc/$(pidof gvfsd)/environ | xargs --null -n1
HOME=/home/myuser@mydomain.local LANG=fr_FR.UTF-8 LOGNAME=myuser PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin SHELL=/bin/bash SYSTEMD_EXEC_PID=1033 USER=myuser XDG_RUNTIME_DIR=/run/user/[UID] GTK_MODULES=gail:atk-bridge QT_ACCESSIBILITY=1 QTWEBENGINE_DICTIONARIES_PATH=/usr/share/hunspell-bdic/ DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/[UID]/bus DISPLAY=:0 XAUTHORITY=/home/myuser/.Xauthority XDG_CURRENT_DESKTOP=LXQt MANAGERPID=950 INVOCATION_ID=cdaffc87ee5c4540a61fabfd797369f5 JOURNAL_STREAM=8:8517 KRB5CCNAME=FILE:/tmp/.krb5cc_[UID]
Seems like gvfsd
has the path to the Kerberos ticket cache file in its environment! Let’s see if it works…
$ gio mount smb://myserver.mydomain.local/share/
$ gio mount -l
Mount(0): share on myserver.mydomain.local -> smb://myserver.mydomain.local/share/
$ klist
Ticket cache: FILE:/tmp/.krb5cc_[UID] Default principal: myuser@MYDOMAIN.LOCAL Valid starting Expires Service principal 28/12/2023 13:39:29 28/12/2023 23:39:29 krbtgt/MYDOMAIN.LOCAL@MYDOMAIN.LOCAL renew until 29/12/2023 13:39:29 28/12/2023 13:45:31 28/12/2023 23:45:31 cifs/myserver.mydomain.local@MYDOMAIN.LOCAL renew until 29/12/2023 13:45:31
No questions asked. GVFS
mounted the SMB share, and a new ticket has been appended to the cache file.
Observation: GVFS
was able to mount an SMB share with SSO
Result: PASS
Test #4: Mount shares at login
This test is pretty simple. Is the share mounted like pam_mount
was supposed to?
$ mount
//myserver.mydomain.local/share on /home/myuser@mydomain.local/Network Drives/Share type cifs (rw,nosuid,nodev,noexec,relatime,vers=3.0,sec=krb5i,cruid=[UID],cache=strict,username=myuser,uid=[UID],noforceuid,gid=[GID],noforcegid,addr=[IP OF MYSERVER],file_mode=0755,dir_mode=0755,soft,nounix,serverino,mapposix,rsize=4194304,wsize=4194304,bsize=1048576,echo_interval=60,actimeo=1,closetimeo=1)
YES IT IS! Options are correct, mount point as well.
Observation: the required SMB share was properly mounted at login
Result: PASS
Final Word
With the right set of tools, it is possible to consider using GNU/Linux workstations in an Active Directory (and consequentially Microsoft-dominated) environment.
During this how-to, you’ve learned how to use such tools to:
- join an Active Directory domain;
- properly adapt the system to foster accounts from this distant domain;
- authorize administrative actions (
sudo
) based on domain groups; - authenticate domain users;
- allow access to remote resources using their credentials.
Most importantly, everything in this how-to is configured at the system level. It is possible to implement these steps in a model system that can be cloned and deployed to multiple machines across a network.
One Last Thing
If you also want to use SSO with websites and web applications hosted on servers with SPNEGO enabled, you can configure Mozilla Firefox to allow Kerberos negotiation via a set of policies. Red Hat’s documentation on the matter is simple enough so here it is.
Hi, struggling to get this to work. First off, I am completely new to Linux (any flavours) and just wanting to get a server set up and on a home lab AD Domain in order to test out some monitoring software.
I notice that on boot (after restarting from the above config/changes) it says failed to start the sssd.service and then it looks like a couple of dependency services marked as depend
When I eventually get to the login screen (I have set up with the interface), I can no longer login with the user I set up when I installed Debian 12 and it just says authentication hasn’t worked. I also tried with multiple formats of domain accounts in my AD, one of which is Domain Admin with the same message. I also tried with root and that told me the same.
The only thing I can do is to choose the option on boot up for advanced and go in to maintenance where I can connect with root and edit files etc but as soon as I reboot it repeats and I cannot login normally.
As I say, I am completely new to Linux but hoping you can possibly spot what I have done (or haven’t done) 🙂
Thanks
Hi Andy,
What’s the output of `systemctl status sssd.service` and/or `journalctl -b -u sssd.service`?
This should print out the latest logs from SSSD, possibly leading to what’s at cause here.
Regards,
Pierre
For me on a test LMDE 6 environment, sssd would crash when “use_fully_qualified_names” was set to False AND default_domain_suffix was set. I had to comment out default_domain_suffix and set use_fully_qualified_names to True for it to actually start. I’m not sure why, just thought I would throw that out there in case anyone else was having a similar issue.
Also, I had to add “ad_gpo_ignore_unreadable = True” to my /etc/sssd/sssd.conf as well.
Otherwise, my LMDE 6 VM sort-of works. I can log in as a domain user, but I haven’t been able to figure out why my network drives aren’t being found + mounted.
Before your article, I couldn’t figure out how to get domain user authentication working with sssd. Thank you for making this!