Linux LDAP authentication with Samba4

Given a fresh Samba4 domain setup and a bunch of Linux/Windows client machines, how do you configure them to allow logins from domain users–ideally with a shared userspace mounted from Samba4? In this post, I’m going to share my experience on how I setup Ubuntu clients to authenticate via Active Directory. The default behavior in Samba4 is to use winbind, which keeps a local flat file of uid/gid mappings. I prefer letting LDAP hold that data. It’s possible to edit user accounts in the Active Directory tree to include the posixAccount LDAP class with uidNumber/gidNumber/unixHomeDirectory/loginShell attributes, and setup each Linux client to talk to the Active Directory domain controller merely as a Kerberized LDAP server. As a result, we can have a single account and password for both Windows and Linux users across the domain. We don’t have to join the Linux machines to the domain, we just need to export the correct Kerberos credentials to each client. I learned about this technique primarily from this blog, although I also heavily referenced the Samba4 mailing list.

Note: If security is top priority for you, you may prefer the full domain join route for Linux machines. In fact, it’s probably best not to bother with Samba4 at all in that case…

Samba4

You’ll need to make some minor changes to Samba4. Under /usr/local/samba/etc/smb.conf (or wherever this file is stored), add the following line:

idmap_ldb:use rfc2307 = Yes

This instructs Samba4 to rely on LDAP for uid/gid mapping for users. Otherwise, it will use the local winbind flat file database.

Admittedly, I haven’t found a lot of information on how this works, so I’m speaking mainly from what I’ve observed. Take it with a grain of salt. If you want to be able to resolve usernames/groups on your Samba4 server, you’ll need to perform the following steps on the server machine as well as any clients.

nss-pam-ldap

The key package you will need to make this work is nss-pam-ldap. You can find it here. As stated on the website, this package provides a PAM module and daemon (nslcd) for querying and authenticating to an LDAP server. Follow the instructions here on how to set it up. I don’t want to reiterate everything that’s stated in the documentation, so I’ll just add some things.

  • If you want to connect to a remote LDAP server, make sure you specify the address in the form ldap://IP_ADDRESS. I made the mistake of using the hostname and got strange errors…it took me a while to track that one down.
  • The /etc/nsswitch.conf portion tells your machine where to look for user/group/host name resolution. It’s an essential step, so don’t forget it.
  • When I installed the package, I’m pretty sure it edited the pam configuration files for me. Check to be sure. Also, be careful editing these files because you could easily lock yourself out of the machine.

Active Directory requires Kerberos authenticated queries. You might be able to specify a domain user and password, but a kerberos keytab is probably better. The keytab is a principle/encryption key pair that grants the right to request keys without a password. I’ll explain more about this in a bit. Before we make changes to /etc/nslcd.conf to integrate that component, we need to do some Kerberos related setup.

Kerberos

I recommend reading up on Kerberos; it’s a really neat technology that Active Directory utilizes heavily. Here are some terms you’ll find thrown around a lot:

Realm – The Kerberos Realm identifies a specific Kerberos database stored on a single machine (KDC). It’s usually the same as the domain name, but it doesn’t have to be.

Principle – *A Kerberos principal is a unique identity to which Kerberos can assign tickets. Principals can have an arbitrary number of components. Each component is separated by a component separator, generally `/’. The last component is the realm, separated from the rest of the principal by the realm separator, generally `@’. If there is no realm component in the principal, then it will be assumed that the principal is in the default realm for the context in which it is being used.

Keytab – *All Kerberos server machines need a keytab file, called /etc/krb5.keytab, to authenticate to the KDC. The keytab file is an encrypted, local, on-disk copy of the host’s key.

*Taken straight from the MIT documentation.

Before messing with these though, we need to create a domain user.

samba-tool user add ldap-service
samba-tool user setexpiry ldap-service --noexpiry

We then need to export a keytab for this user, which will allow us to request keys without a password:

samba-tool domain exportkeytab /etc/nslcd.keytab --principal=ldap-service

You’ll need to copy this key to any of your clients that use nslcd. Keep in mind that this key grants domain user privileges without using a password, so limit the permissions on this file heavily.

Before continuing, you should make sure you having these packages installed: kstart, libsasl2-modules-gssapi-mit. If you forget the latter, you’ll get a “no suitable SASL mechanism found” error.

Once again, open up nslcd.conf and add the following lines:

# samAccountName holds the username in AD. It's a suitable substitute for uid. We can map it to avoid duplicating data.
map passwd uid samAccountName

# homeDirectory is already in use by AD for storing the Windows directory. We'll store ours in unixHomeDirectory
map passwd homeDirectory unixHomeDirectory

# Enable Kerberos authentication
sasl_mech GSSAPI
sasl_realm YOURREALM.DOMAIN.COM
krb5_ccname /var/run/nslcd/nslcd.tkt

The last line points at a ticket file that we haven’t done anything with yet. The keytab file just gives you the right to request tickets. A ticket actually gives you access to resources, and has an expiration date associated with it. We need to tell nslcd to request a tickets at a certain interval using this keytab file. To do this (in Ubuntu), edit /etc/default/nslcd to look like the following:

K5START_START="yes"
K5START_BIN=/usr/bin/k5start
K5START_KEYTAB=/etc/nslcd.keytab
K5START_PRINCIPLE=ldap-service

The nslcd service will use a daemon called k5start to request a new ticket at specified certain interval.

As an important sidenote, there is a service called nscd (not to be confused with nslcd) that caches name lookups. It’s good to have running on all your clients because it takes the load off your LDAP server. However, during testing you should turn it off via `service nscd stop` so that you’re actually talking to the LDAP server and not a local cache.

User Accounts

At this point, you should be able to start nslcd and run getent passwd without things hanging. Make sure to check your system log to verify that things are working. Keep in mind that you can run nslcd -d (with multiple d’s for more output) directly on the command line to see error output. You’ll want to check that LDAP binds are working. However, you won’t actually see any domain user accounts listed in the getent output because we haven’t added posixAccount classes to them yet.

The easiest way to do this is through the ldb* set of commands provided by samba. If you run kinit administrator, you should be able to connect to your Samba4 LDAP server with Kerberos authentication by using the “-k yes” flag. As an example:

 ldbsearch -H ldap://SERVER_HOSTNAME -k yes

should print the entire tree. I noticed that when using these utilities, I have to specify the hostname of my LDAP server. Using the IP address fails with NT_STATUS_INVALID_PARAMETER. I’m not sure why that’s the case.

In our case, we want to modify a user entry to include the posixAccount data. If you haven’t already, make a domain user using the samba-tool user add interface.

Next, create a .ldif file and add the following lines:

dn: USERCN
changetype: modify
add: objectClass
objectClass: posixAccount
-
add: uidNumber
uidNumber: UIDNUMBER
-
add: gidNumber
gidNumber: GIDNUMBER
-
add: unixHomeDirectory
unixHomeDirectory: HOMEDIRECTORY
-
add: loginShell
loginShell: /bin/bash

Obviously, you’ll need to replace the upper case entries with your specific data. Once you have this file, you can write it to the LDAP tree with the following command:

ldbmodify -H ldap://SERVER_HOSTNAME -k yes /tmp/YOURFILE.ldif

Now your user has all the data it needs to authenticate and login to a Linux client! If everything works, you should now be able to type getent passwd YOURUSER and see their entry in the output. You should also be able to su – YOURUSER and authenticate. If you’re lucky and it works, congrats! You’ve just logged in to your Linux client through Active Directory! It you’re like me, things probably didn’t work the first time. Make sure to check the logs and utilize debug mode for the nslcd and samba services. Good luck!

Advertisements

Joining Samba4 to an Existing Windows Domain.

Note: See my first post for more context.

I am developing an small Active Directory infrastructure for the Computer Science Department Linux/Windows lab environment at Taylor University. Lab users have a central user space shared by both the Linux and Windows profiles, which authenticate from to Active Directory. Our Windows profile setup utilizes folder redirection to the network share for most profile folders (Desktop, Music, Pictures, Downloads, AppData/Roaming, etc), while our Linux space is NFS mounted by clients. Since we favor Linux administration, we prefer to keep our user files stored on a Linux file system. The Samba4 server grants CIFS access to Windows users, while Linux users can automount via NFS. LDAP authentication is handled through Kerberos on both the Windows and Linux clients. While Windows clients are joined to the Domain, Linux treats Active Directory merely as a Kerberized LDAP server. Finally, each user account is extended with a posixAccount class allowing us to give each user the uid, gid, unix home directory, and login shell attributes they need to properly interface with nslcd (the ldap client daemon) for Linux logins.

Prerequisites

I started by setting up two Windows Server 2008 R2 machines. I created a new domain, installed the Active Directory Domain Services and DNS roles via the dcpromo.exe utility, configured both machines as domain controllers. This process is very well documented by Microsoft so I won’t describe it here. It’s important to ensure that directory and DNS replication is happening between these machines. I chose two Windows domain controllers for a few reasons:

  1. Demoting any DC appears to fail when a Samba4 DC exists in the domain. I’m not sure if this is specific to my setup, but it’s just one of the many bugs I’ve found so far (I hope they fix it soon). As such, I don’t really trust S4 to keep my production directory data intact, and if I have to demote (read: manually delete) my Windows DC for any reason, I want the directory data housed within Windows DC.
  2. Related to the above comment, I’m not comfortable yet entrusting S4 with master roles, thus I need two Windows boxes in case I need to bring one down.
  3. Active Directory is highly reliant on DNS. As such, I strongly prefer the Windows DNS to a hacked up BIND solution or S4’s brand new internal system. Having a two replicating Windows DNS servers is even better.
  4. Apparently, the Samba4 DNS doesn’t play well with Windows DNS yet. Once again, if I have to choose…

Anyway, after configuring the Windows machines, the next step was to create an Ubuntu Linux server (I chose Ubuntu because it’s heavily favored by the Samba4 team) and join it as a DC to the existing Windows domain. You first need to prepare your server for Samba4. Then, you need to setup Samba to join an existing domain.

Rather than regurgitate what is already stated on that Wiki, I’ll just note some things.

Kerberos

When you setup the Kerberos configuration file as stated in the Wiki, it’s important to test it out via kinit administrator@YOURREALM. By authenticating, you’ve requested a ticket from the Kerberos server, which is then placed in the default ticket cache, located at /tmp/krb5cc_0. If this isn’t working properly, the provision step will fail–probably with an LDAP authentication error.

Domain Name Service

If you’ve followed the Wiki instructions correctly up to this point, the provision step should work. After doing lots of test provisions, I’ve noticed that the DNS replication doesn’t always work (this is actually a known documented issue I guess). You can run the test commands they give you in the Wiki, or use the Windows DNS GUI to check that entries exist–my preferred approach. The Active Directory DNS tree heavily utilizes SRV records for services. The main ones we care about are Kerberos, LDAP, and GC (Global Catalog). Browse the DNS tree and make sure that you can see your Samba4 server listed along with the other Windows DC’s. In my configuration I noticed that under DomainDNSZones and ForestDNSZones, my Samba4 server is not listed. I think this is intended behavior because the Windows DNS and Samba4 DNS don’t work together quite yet.
If you notice that those entries don’t exist, there’s a handy script called samba_dnsupdate, located in INSTALL_DIR/samba/sbin/. That should replicate the DNS entries you need to the Windows DC’s. In the cases where the provision step failed to do this for me, running that script fixed the problem. Your mileage may vary.

Directory Replication Service

You can view the replication status by running the following command: samba-tool drs showrepl. If things don’t look like they’re working, first make sure you’ve started Samba! It may take it a few minutes to “warm up.” You can force replication by running:

samba-tool drs replicate <destinationDC> <sourceDC> <NC>

The NC term is your LDAP naming context; to replicate the whole tree, use your base Distinguished Name, e.g. DC=SOMETHING,DC=TEST,DC=COM. Under the Active Directory Sites and Services subtree in the Windows Server Manager GUI, you can view the connections between servers (Sites -> Default-First-Site-Name -> Servers -> YourS4Server). I’ve done several test configurations, and every one of them throws an error when I try and replicate to the Samba4 server from a Windows DC. This is the error I get:

The following error occurred during the attempt to synchronize naming context DomainDNSZones.<DOMAINNAME> from Domain Controller <Samba DC> to Domain Controller <Windows DC>: The naming context is in the process of being removed or is not replicated from the specific server. The operation will not continue.

Interestingly enough, if I replicate via samba-tool using the same source and destination servers, everything works. I’m assuming there’s some incompatibility there that hasn’t been addressed yet by the Samba team. If anyone has a solution for this please let me know. So far it hasn’t caused any problems.
Another good test of replication is to create a user account and see if it transfers across domain controllers. You can do this via samba-tool user add.

Distributed File System Replication

Samba4 does not yet support DFS replication. For our purposes, the main issue this causes is group policy synchronization issues; namely, the SYSVOL folder–which houses group policy objects–is not replicated from the Windows domain controllers to the Samba server. As a result, you’ll need to manually mount the SYSVOL share, copy the data over, and then run samba-tool ntacl sysvolreset to fix the permissions. Ideally, you should write a script to do it. This only needs to be updated when you’ve changed a group policy object. This is a bit annoying, but it’s not a show stopper. If you don’t do this, then your clients will apply different group policy settings based on which controller they happen to talk to, which is bad.

Coming Up

At this point in the setup process, I had my two Windows and Samba4 domain controllers communicating pretty happily. In my next post I’ll talk about how I setup my Linux and Windows clients to authenticate, login, and access user data from the Samba server. Stay tuned!

Permutations and Combinations

In the last couple years I’ve regained interest in solidifying my math chops. One mathematical concept that I see popping up again and again is that of permutations and combinations. I tried searching around for quick explanations, but I wasn’t really pleased with what I found. I thought I would share an approach that really helped me get it.

Permutations

Finding the number of permutations of a unique set is simple, but tricky to understand. Once it “clicks,” you’ll think to yourself, “why did I think that was so hard?” (if you’re like me anyways).

Given a set with unique elements, you can permute it by mixing up the elements. Finding the permutations of a set involves figuring out all the unique ways you can mix those elements. The key concept in a permutation is ordering: it matters how the elements are ordered. Thus, the permutation \left \{ab \right \} is distinct from \left \{ba \right \}. The question, “how many permutations are there of a set?”, is asking for all possible orderings of the elements in that set. Consider the set:

S = \left \{abc \right \}

What are all the permutations of S? One way to do this by hand is by picking an element to go first and enumerating the permutations of the remaining elements:

(a goes first)

\left \{abc \right \} \left \{acb \right \}

(b goes first)

\left \{bac \right \} \left \{bca \right \}

(c goes first)

\left \{cab \right \} \left \{cba \right \}

As you can see, there are 6 permutations of this set. One thing to notice is that there are 3 members in the set, and we could pick any of those to be the first element. More generally, given N elements in a set, there are N ways to pick the first element.

But what about the second element? Assuming all members are unique, we can take the subset of N-1 elements and pick any of those to be the second. For example, let’s assume we’ve picked a already, we’re left with a subset of S without a:

\left \{bc \right \}

This particular set is trivial, but let’s stick with our plan:

(b goes first)

\left \{bc\right \}

(c goes first)

\left \{cb \right \}

We end up with only two permutations of this subset. More generally, we’ve got N-1 elements, and we can pick any of those to go first in our subset.

You may be spotting a pattern here, and that’s the key insight. We started with N elements, and we’re free to pick any one of those to go first. Then, for every one of those N choices, we have a subset of N-1 elements. Following our pattern, we can pick any N-1 of those elements to go first in that subset. If we keep going, we end up with N-2, N-3, etc–all the way down to 1. When we reach a set of 1 element, we’re done. This is our base case.

The trickiest part is knowing how to combine these results.

Starting with N elements, we have N choices for the first one. For each of those N choices, we have N-1 sub-choices. These are multiplied together to give us the total number of choices, N * (N-1). But we aren’t done, because have to consider the N-2 choices for each of the N-1 sub-choices, for each of the N original choices, which is N * (N-1) * (N-2). If you continue this pattern all the way down to 1, you get the familiar factorial function, N! Going back to our example, we had 3 choices for our first element, which gave us the three lines above. For each of those 3 lines, we had a 2 element subset, of which the permutations are merely the 2 possible “flippings” of those elements. We ended up with 3*2*1 = 6 permutations, just as we expected.

Thus, the answer to our question, “how many permutations are there of a set?” is N!, where N is the number of elements in our set.

Combinations

You may be familiar with the “n choose r” notation, which is denoted mathematically by the following equation:

\binom{n}{r} = \frac{n!}{r!(n-r)!}

This is a pretty scary looking equation, and it’s not easy to “reverse engineer.” the meaning by just looking at it. Fortunately, there’s an easy way to understand it that builds on the idea of permutations presented in the previous section, so bear with me.

First of all, what is a combination? Remember how permutations were ordered sets? The key behind a combination is that it is unordered. How many ways can you divide a group of 4 students into 2 person teams? That’s a combination problem. We don’t care who’s first or second on each team–only who is on each team. Thinking about it, combinations must somehow relate to permutations. More precisely, the combinations must be a subset of the permutations. Why? Well, if you have any combination of students on a team, you could generate permutations from that–by picking someone to go first, etc., like we learned above. It “feels” like there are duplicate sets being counted that we don’t care about–which is exactly the case. The equation above is basically computing all possible permutations and then “trimming” the duplicate results to give us the combinations.

With that insight, let’s examine that scary equation once again, just for a bit.

\binom{n}{r} = \frac{n!}{r!(n-r)!}

This equation is saying, “how many ways can we choose r elements from n elements?” Using our team example above, we’d be choosing 2 person teams from 4 total people. Let’s plug it in and see what we get:

\binom{4}{2} = \frac{4!}{2!(4-2)!} = \frac{24}{2*2} = 6

Given a set, \left \{abcd \right \}, it’s not too hard to pick out those 6 combinations:

\left \{ab\right \} \left \{ac \right \} \left \{ad \right \} \left \{bc \right \} \left \{bd \right \} \left \{cd \right \}

As you can see, each combination includes a unique group of people, in no particular order. We could visit each of these groups, permute them by picking all possible orderings, and combine the results to get 24 total permutations. I’ll leave that as an exercise for you. The key insight is that we can compute permutations from combinations. Each permutation is the same combination, just mixed up differently. Let’s just examine one of these combinations: \left \{ab\right \}.

Finding the permutations in this set is trivial. Here they are:

\left \{ab \right \} \left \{ba \right \}

There are 2! = 2 permutations here. These are the permutations of the combination, \left \{ab\right \}. Hmmm, it seems like we’re ending up with twice as many results as we want. For each combination, we have a duplicate. We need to take our original n! permutation estimate and somehow remove these duplicates. We do this by dividing by r!. Think about it, if we have a 2 element set, we end up double counting everything. We need to divide by 2. If we had a 3 element set, we could have even more duplicates. We would need to divide by 3! = 6.

Using our previous example, dividing 24 (the number of permutations of n elements) by 2 (the number of permutations of r elements) gives us 12–not 6. What gives?

We’ve still got the (n – r)! term in there. If you think about it, n-r counts the remaining elements not chosen in our combination. Let’s take a look at our team example once more to see why we need this.

Let’s say we’ve chosen the combination, \left \{ab \right \}. We know that this expands into 2 permutations, so we divide our total number of permutations (24 in this example) by 2, but we still have duplicates. The issue is that our n! permutations still include permutations of the remaining elements, which are incorrectly included in our count. Let’s examine our first combination once again:

\left \{ab \right \}

We know we can permute this, but we still have to combine it with the remaining unused elements, which combined form a permutation of the original set. Thus, even if we purge duplicates from our subset, we still end up with more duplicates. Assuming we’ve purged duplicates from our example subset, our combined set still includes the permuted unused elements:

\left \{abcd \right \} \left \{abdc \right \}

To correct this, we need to divide by the (n-r) element permutations, which gives us the missing (n-r)! term. If we divide our original n! permutations by these two terms, we finally get the correct result:

\binom{n}{r} = \frac{n!}{r!(n-r)!}