Fully Functional AD-Joined Debian Servers

Tags: Debian, Active Directory

Introduction

My preferred platforms are Windows Server (for Windows applications obviously) and Debian (for Linux - which in my case includes Docker). However, I want a single username to be valid and useful in similar ways across the two environments.

I've always preferred joining my Debian servers to AD, and getting authentication to work is pretty easy. Recently, though, DNS dynamic update has not been so cooperative.

Goals

Ideally I want similar basic functionality across the Windows and Debian environments. Note that most of what is here probably applies to Ubuntu without much change, but RHEL and its relatives obviously use slightly different configuration files, commands and tools (especially for package management). Since I haven't installed a RHEL variant for at least a decade, it's probably safe to assume I have zero knowledge there.

So what does that mean to me?

DescriptionHow it works on WindowsHow it works on Debian
Standard User Authentication Log on to Windows with a domain account, either username or UPN format. A local profile is created in C:\Users and the user has rights granted by groups, including nested groups. Log onto Debian with a domain account, local username or UPN format. A local profile is created in /home, and the user has rights granted by groups, including nested groups.
Administrator Elevation User uses UAC to transition to an elevated context, and administrative user accounts can perform appropriate tasks. User uses sudo to transition to an elevated context, and administrative accounts can perform appropriate tasks.
Network Access RDP enabled by policy. Administrators can remotely access the machine for administration. SSH enabled. Administrators can remotely access the machine for administration.
DNS registration Windows computers register in DNS with their hostname and update when IP addresses change. Debian computers register in DNS with their hostname and update when IP addresses change.

That's really not a huge list, but it does make it simpler to correlate actions by individuals across the estate.

Platform

This is all native Windows functionality; Linux is a different beast. I started with Debian Stretch 9.7 (booting from the 9.6 NetInst ISO image). It's a simple base build:

  • 2 vCPUs;
  • 2GB RAM;
  • 64GB Disk 0;
  • 1 x NIC on appropriate network;
  • 1 x optical drive for installation.

Running quickly through the installer screens:

Mode Install You can also do a graphical install, but this is a server - no GUI needed for the most part. If your application does need X, install as appropriate.
Language English I don't speak Russian, so this is the obvious choice.
Location Australia I also don't live in Upper Volta, so again, choose wisely.
Keymap American English Australia tends to get US keyboards.
Hostname dockerimage I'm building a common image for docker roles - both masters and workers
Domain Name ad.domain.net Match the internal AD domain name. You don't have to do this, but it might make it simpler later.
Root Password SomethingSecure I have a password manager I tend to use, but because this is an image it's a long but memorable password
Local User Local Admin (local-admin, password) I haven't yet found a way to avoid creating a local account, and it's not the worst fallback if something goes wrong. I don't think I've ever used it, though.
Timezone New South Wales I hope I don't have to tell you to select this appropriately
Partitioning Mode Guided - use entire disk and set up LVM Personal preference. You could go any approach, but I like to have the ability to expand with LVM if I need to.
Force UEFI Yes I run Gen 2 VMs on Hyper-V, so I force UEFI installation mode.
Debian Mirror Local to me (iiNet, Aarnet etc) Choose wisely, this takes at least ten seconds to change later
Popularity-Contest Disable Personal preference, again
Install Components Deselect everything except SSH and the standard utilities Again, this is a template for a standard server. You may want Gnome on every machine - but I see no reason for it.

Install takes me about 5-10 minutes depending on prevailing conditions, including local wind speed, the direction of flight of any observed birds, and the amount of coffee left in the cup. Once install is complete, restart and log in as root.

Edit /etc/network/interfaces if you're not planning to use DHCP - mine looks roughly like this:

  iface eth0 inet static
    address a.b.c.d/24
    gateway a.b.c.1
  iface eth0 inet6 static
    address 2001:aaaa:bbbb:cccc::ffff:gggg/64
    gateway 2001:aaaa:bbbb:cccc::ffff:1

Check /etc/resolv.conf to make sure DNS configuration is correct, perhaps similar to:

domain ad.domain.net
search ad.domain.net
nameserver a.b.c.11
nameserver a.b.c.12

I then added the following components with apt:

  • hyperv-daemons - because it's a Hyper-V VM, and I like when things actually work
  • curl - I prefer it over wget, but this is preference
  • apt-transport-https - some repos use https, and with everything transitioning to https only (bloody Google) I think it's only a matter of time before this is common
  • realmd - needed for AD membership
  • adcli - needed for AD membership
  • sssd - needed for AD membership
  • ntp - needed for AD membership (time sync)
  • packagekit - needed for AD membership
  • sssd-tools - needed for AD membership
  • cifs-utils - mapped CIFS paths
  • sudo - preferred over plain su for auditing, I think
  • dnsutils - needed for AD membership

Join the domain with realm:

# realm join -U Administrator ad.domain.net

Edit /etc/sssd/sssd.conf to tweak the sssd configuration to preference:

OperationSettingValueNotes
Change use_fully_qualified_names False This setting allows logon with unqualified usernames (like Bob) and also with a UPN (Bob.Ford@ad.domain.net)
Change fallback_homedir /home/%d/%u Home directories are in /home/ad.domain.net/samAccountName
Add chpass_provider ad Passwords can be changed in AD
Add ad_hostname hostname.ad.domain.net DNS name to register
Add dyndns_update true Perform DNS updates (I believe this is the default, but possibly wasn't when I started joining systems to AD)
Add dyndns_refresh_interval 43200 Time between refreshes. This needs to be the same as, or longer than the minimum refresh time to be effective (I believe this is the default, but possibly wasn't when I started joining systems to AD)
Add dyndns_update_ptr true Also update the reverse record (I believe this is the default, but possibly wasn't when I started joining systems to AD)
Add dyndns_ttl 3600 Recommend 1 hour, which matches the minimum refresh interval possible in AD (I believe this is the default, but possibly wasn't when I started joining systems to AD)
Add ad_gpo_access_control disabled Enforcing mode (default) prevents logon by default - I haven't had time to diagnose this, but disabling is OK for my environment at the moment

Create the base folder for home directories:

# mkdir /home/ad.domain.net
# chown root:root /home/ad.domain.net
# chmod 755 /home/ad.domain.net/

Update /etc/pam.d/common-session, adding the second line as shown:

session required pam_unix.so
session required pam_mkhomedir.so umask=077 skel=/etc/skel

Restart sssd to effect the changes

# systemctl restart sssd

Enable sudo for Domain Admins (and/or other groups as desired) - this example is very permissive, but it suits the environment:

# echo %domain\ admins ALL=(ALL) ALL > /etc/sudoers.d/domain-admins

At this point, you're probably expecting everything to work. I did. I was wrong. The Debian server just will not register with AD DNS.

Diagnosis

On the AD side, I turned on debug logging, for updates only, but all other details:

DNS Debug Logging screenshot

Then, I set debug_level to 10 in /etc/sssd/sssd.conf, and restarted sssd:

# systemctl restart sssd

/var/log/sssd/sssd_ad.domain.net shows the details of the DNS registration - the error was as shown below:

show_message()
Out of recvgss
update_completed()
; TSIG error with server: tsig verify failure
show_message()

The debug log on the AD side showed a similar failure pattern, with the AD DNS server refusing the update.

Turning off Secure updates permitted the name to register, but that's not a long term solution. A number of other causes were proposed:

  • Updates to the forward zone must be configured for Secure Only (this is the default state);
  • The correct reverse DNS zone must exist (it does, the lack of it annoys my OCD);
  • Updates to the reverse zone must be configured for Secure Only (again, default state).

Research suggested there was a bug in sssd, v1.5.0 - which just so happens to be the version available in Debian Stable. It was in a Reddit post I cannot find at the moment.

The Fix

The naive approach (which obviously was the one I selected first) would be to upgrade just sssd using the testing repository - as at this writing, the testing repository is current at v1.16.3. That doesn't work.

Instead, I had to update the rest of the system from stable to testing - I don't think this is the most terrible scenario in the current "everyone on the bleeding edge all the time" world. It's still software that has two weeks without bugs being logged against it, so it's not generally rushed.

Hypothesis: It's not just sssd that requires updates, it's also nsupdate (dnsutils package) and all the dependencies of those packages.

The suite of changes are all to files that drive apt behaviour. Note that there might be better ways to do this, but it worked for me:

OperationFile PathContentsNotes
Move /etc/apt/sources.list to /etc/apt/sources.list.d/stable.list
#
deb http://ftp.iinet.net.au/debian/debian/ stretch main
deb-src http://ftp.iinet.net.au/debian/debian/ stretch main

deb http://security.debian.org/debian-security stretch/updates main
deb-src http://security.debian.org/debian-security stretch/updates main

# stretch-updates, previously known as 'volatile'
deb http://ftp.iinet.net.au/debian/debian/ stretch-updates main
deb-src http://ftp.iinet.net.au/debian/debian/ stretch-updates main
This probably isn't needed, but keeping it can't hurt when you want to revert to stable
Create /etc/apt/sources.list/d/testing.list
#
deb http://ftp.iinet.net.au/debian/debian/ testing main
deb-src http://ftp.iinet.net.au/debian/debian/ testing main

deb http://security.debian.org/debian-security testing/updates main
deb-src http://security.debian.org/debian-security testing/updates main

# testing-updates, previously known as 'volatile'
deb http://ftp.iinet.net.au/debian/debian/ testing-updates main
deb-src http://ftp.iinet.net.au/debian/debian/ testing-updates main
Testing will likely be the source of almost everything after we update.
Create /etc/apt/preferences.d/stable.pref
Package: *
Pin: release a=stable
Pin-Priority: 500
Stable seems to default to priority 500, based on apt-cache policy.
Create /etc/apt/preferences.d/testing.pref
Package: *
Pin: release a=testing
Pin-Priority: 600
I set this to 600, leaving a sizeable gap between stable and testing, and between testing and security patches. I think. Hope.

Now update and upgrade. There were more than 310 changes for me:

# apt update
# apt upgrade

You're going to want to restart, now, because there are changes to systemd among other things. Restarts are needed.

Remove your debug level from sssd.conf, too, else you'll end up with huge log files in /var/log/sssd.

No Comments