I hope this saves someone from wasting nearly a full day like I did.

I'm playing about with a Samba 4 Active Directory and Ubuntu (mainly) clients. On my NAS I'm using winbind but for things such as laptops I need to be able to cache certain things to allow the devices to be used when the Samba AD is inaccessible (e.g. site with no internet connection). For this I'm using SSSD.

So far I have been stumped by two time holes. The first was that in some situations, changing sssd.conf has no effect because it uses the settings it has in it's .ldb cache database. Any time you change sssd.conf, delete the cache database:

sudo rm -f /var/lib/sss/db/*

Don't worry, everything in there is just cached data pulled from your AD. You're not deleting anything important.

The big thing that I lost time on however was sudoers.


In order to store sudoers information in your AD you need to extend your AD's schema slightly. After a bit of reading (sorry, I've forgotten where) I worked out the following process:

On your Schema Master Domain Controller (probably the first DC you created) download the sudoers schema from the sudo project on GitHub:

wget https://github.com/lbt/sudo/raw/master/doc/schema.ActiveDirectory

Reformat the file into two seperate files (class and attributes), remove some entries that Samba doesn't want and replace the example domain with my real domain name:

cat schema.ActiveDirectory | tr -d '\r' | grep -v -e '^changetype: ' -e '^add: ' -e '^schemaUpdateNow: ' | grep -B 10000 '^-$' | grep -v -e '^dn:$' -e '^-$' | sed 's/,DC=X/,DC=ad,DC=ghanima,DC=net/ig' > schema.ActiveDirectoryAttribs
cat schema.ActiveDirectory | tr -d '\r' | grep -v -e '^changetype: ' -e '^add: ' -e '^schemaUpdateNow: ' | grep -A 10000 '^-$' | grep -v -e '^dn:$' -e '^-$' | sed 's/,DC=X/,DC=ad,DC=ghanima,DC=net/ig' > schema.ActiveDirectoryClass

Download the ldb-tools if they're not already installed:

sudo apt install ldb-tools

Import the schema into the AD:

sudo ldbmodify -H /var/lib/samba/private/sam.ldb schema.ActiveDirectoryAttribs --option="dsdb:schema update allowed"=true
cat schema.ActiveDirectory | tr -d '\r' | grep -v -e '^changetype: ' -e '^add: ' -e '^schemaUpdateNow: ' | grep -A 10000 '^-$' | grep -v -e '^dn:$' -e '^-$' | sed 's/,DC=X/,DC=ad,DC=ghanima,DC=net/ig' > schema.ActiveDirectoryClass

Ok. Schema extended. Now we create some default entries. The default process is to create an OU in the root of your directory called “sudoers”. This seemed messy to me so I made a generic “container” object in the “system” container. This might bite me later but it hasn't yet.

dn: CN=sudoers,CN=System,DC=ad,DC=ghanima,DC=net
ObjectClass: top
objectClass: container
cn: sudoers
sudo ldbmodify -H /var/lib/samba/private/sam.ldb sudoContainer.ldif

Into our new container we add a “defaults” entry. This entry sets the basic default options for sudo:

dn: cn=defaults,cn=sudoers,cn=system,dc=ad,dc=ghanima,dc=net
objectClass: top
objectClass: sudoRole
cn: defaults
description: Default sudoOptions go here
sudoOption: env_keep+=SSH_AUTH_SOCK
sudo ldbmodify -H /var/lib/samba/private/sam.ldb sudoDefaults.ldif

Finally, I added the simplest possible entry. This is for a single user called “work” which is a user in the AD. Obviously more complex entries are possible, using groups instead of users, etc. but since I was having issues, I will start with the simplest possible entry:

dn: cn=work,cn=sudoers,cn=system,dc=ad,dc=ghanima,dc=net
objectClass: top
objectClass: sudoRole
cn: work
sudoUser: work
sudoHost: ALL
sudoCommand: ALL
sudo ldbmodify -H /var/lib/samba/private/sam.ldb sudoWorkUser.ldif

On the client, in /etc/sssd/sssd.conf I added ,sudo to the services = line and in the [domain/AD.GHANIMA.NET] section I told it my custom location to find the sudoers entries:

ldap_sudo_search_base = cn=sudoers,cn=system,dc=ad,dc=ghanima,dc=net

We should be good to go now. Stop the sssd service, delete the cache database (as described above) and start the sssd service again. Log in as the work user and see what sudo rules apply to us:

sudo -l

And… it doesn't work.


What's going wrong

Note: If you don't care about how I got to the solution, skip this section.
Note: You might find in the logging output that a sudoRule object is mentioned while the AD object is a sudoRole. This is a red herring. SSSD stores what it finds in the AD into is cache database as sudoRule, regardless of how it's named in the AD. Queries to the AD (not the cache) have this function name in the log: sdap_get_generic_ext_step.

I turned the sssd logging up progressivly until I found this in it's output:

(Sat Mar 25 13:06:34 2017) [sssd[be[AD.GHANIMA.NET]]] [sdap_sudo_load_sudoers_done] (0x0040): Received 0 sudo rules

I turned the logging up further until it gave me the query it was using to query the AD:

(Sat Mar 25 13:06:34 2017) [sssd[be[AD.GHANIMA.NET]]] [sdap_get_generic_ext_step] (0x0400): calling ldap_search_ext with [(&(objectClass=sudoRole)(|(!(sudoHost=*))(sudoHost=ALL)(sudoHost=jd3-clm.ad.ghanima.net)(sudoHost=jd3-clm)(sudoHost=*)(|(sudoHost=*\\*)(sudoHost=*?*)(sudoHost=*\2A*)(sudoHost=*[*]*))))][cn=sudoers,cn=system,dc=ad,dc=ghanima,dc=net].

The part in square brackets at the end is the search base so it's not my moving of the objects out of their original location that's caused issues.

I was unable to run that exact query using the command line ldap tools. I think the slashes and stars were getting mundged. I was able to run a very similar query though:

ldbsearch -k yes -H ldap://ad11-clm.ad.ghanima.net '(&(objectClass=sudoRole)(|(!(sudoHost=*))(sudoHost=ALL)(sudoHost=jd3-clm.ad.ghanima.net)(sudoHost=jd3-clm)(sudoHost=*)(|(sudoHost=*)))))'

The result was a representation of the objects I had defined in the AD so it's not that the AD is broken or anything.

I was able to run the full unmodified query using the Windows AD GUI tools so it still seems like it's not an AD issue.

I then, just to be sure, went back to my AD and created the “sudoers” OU in the root of the directory and added the objects into it. Maybe there's a bug in SSSD that makes it ignore the config and request from the default location. Nope. Still Received 0 sudo rules.

I reasoned that somewhere between SSSD and the AD something was being muddled. But what? I used wireshark to read the traffic between the client and the domain controller and I discovered that the AD was indeed returning zero results when SSSD searched. Why!?

Then it hit me. AD ACLs. Every time I queried the AD I was querying as my own user, a Domain Admin. When SSSD queries it mustn't have permission to retrieve the objects.

Sure enough, adding Everyone read access to the objects through the Windows GUI tool, restart (and delete the cache just to be sure) SSSD on the client and:

$ sudo -l
[sudo] password for work: 
Matching Defaults entries for work on jd3-clm:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,

User work may run the following commands on jd3-clm:
    (root) ALL

I don't want Windows tools and manual steps to be a part of my AD build so how do I adjust the AD ACLs from Samba and is there something more appropriate than Everyone?

In a forum post from a few years ago I found the answer.

The solution

samba-tool dsacl set -H /var/lib/samba/private/sam.ldb --objectdn="CN=sudoers,CN=System,DC=ad,DC=ghanima,DC=net" --sddl="(A;CI;RPLCRC;;;DC)"

Don't ask me what the SDDL stuff means but the above grants the “Domain Computers” group read access to the sudoers container and it's children that inherit ACLs from it.