Friday, October 7, 2011

Linux in a Windows 2008 Active Directory domain

This article is an attempt at writing up a single source of information of adding your Linux boxes to a Windows 2008 Active Directory domain with modern software.

Introduction and background

If you just want to read the configuration files and instructions, skip to the "Kerberos configuration and domain join" chapter.

Back when Windows 2003 was hot, I did a project integrating a couple of hundred RHEL3, RHEL4 and RHEL5 boxes into an Active Directory domain. Because of the fact that we had three different flavors of RHEL back then, we wanted to use the same, open source, components on all flavors as much as possible, it was slightly tricky.

I still do Active Directory integration work with Linux a lot, but a lot has changed, too. One the one hand, (thankfully) RHEL3 has died last year and RHEL4 will die in early 2012, so I won't have to worry about those flavors again. RHEL5 has gained some interesting options (mainly sssd) to work with AD, bringing it on par with RHEL6. This makes it a lot easier to have both flavors work with AD with the same configuration.

On the other hand, Windows has changed a bit, too. DES and 3DES encryption types have been deprecated and disabled by default. Windows 2008 only allows AES-128, AES-256 and RC4 for encryption, unless you turn DES and 3DES back on, which you generally do not want to do. They are turned off for good reason.

Samba, however, is not able to use all the encryption types used by Windows 2008 though, limiting your options to RC4 for you keytab if you join the domain using Samba.

Another thing used to be the UPN. Over the years, I forgot the exact reason (take notes!), but what needed to be done with Windows 2003 is to create a User Principal Name (UPN) for your Linux server while joining AD. This enabled getting a Ticket Granting Ticket (TGT) for that machine and subsequently doing LDAP queries.

I suspect it has something to do with Windows 2003 not accepting the Service Principal Name (SPN) requesting a TGT. This was very annoying for a couple of reasons. First of all, you needed to add an extra configuration option to the 'net ads join' command. Second, you needed special rights in AD to create a UPN, iirc. And third, creating the UPN sometimes failed, leaving you with an incomplete computer object in AD you either needed to fix manually or recreate anew.

Back to Windows 2008. Windows 2008 *does* allow the Linux servers SPN to request a TGT, which is great news, because it makes life easier.

So, how do we do it? Read on! I'm going to split the article into three part: a part specifically about the Kerberos component, a part about the sssd and LDAP components and a part about the remaining configuration to be done.

My Kerberos test realm is called NONTOONYT.LAN. The domain controller I use is called dc01.nontoonyt.lan. The Linux machine I will be joining to AD will be called box5.nontoonyt.lan. My users in AD are located in the default Users container, my Groups in a top level OU called 'Groups'.

You will need to install the Subsystem for Unix-based Applications on one of your domain controllers. This will setup the needed LDAP attributes, enable the Unix attributes tab on this domain controller and fill the NIS domain field with the name of your domain. You only need to install this subsystem on DC's you want to see the Unix attributes tab on.

I configure my users and groups with UID's above 10000 and below 30000. Adapt this to your

Kerberos configuration and domain join

First off: Kerberos. RHEL5 and RHEL6 ship with MIT Kerberos 1.6 and 1.9 respectively. Both versions are more or less compatible with regard to the options we need to configure, so we'll use one configuration file for both RHEL5 and RHEL6.

Fist, you need to define the [libdefaults] section. It is important to set the default_realm correctly, in caps, and add lines that allow encryption types. The rest of the options are up to you to fill in. My configuration is as follows:
default_realm = NONTOONYT.LAN
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
forwardable = yes
default_tgs_enctypes = rc4-hmac aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
default_tkt_enctypes = rc4-hmac aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
permitted_enctypes = rc4-hmac aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
Note that, in essence, only the rc4-hmac encryption type really needs to be there if we join the machine using Samba, which does not support AES at the time of writing.

If you do decide to add the other encryption types, perhaps for future needs, be sure to start with the rc4-hmac type. Kerberos will break otherwise.

Second is the [realms] subsection. Here you statically couple domain controllers (or KDC's: Key Distribution Centers, in proper lingo) to a realm.

The admin_server entry is probably not needed, since Active Directory does not expose the administration interface to the work on port 749 anyway. This option is meant for proper, Linux based Kerberos setups, where port 749 *is* used.

My configuration is as follows:
kdc = dc01.nontoonyt.lan:88
admin_server = dc01.nontoonyt.lan:749?
default_domain = nontoonyt.lan
Next we map domains to Kerberos realms, which is simple in my test setup:
.nontoonyt.lan = NONTOONYT.LAN
nontoonyt.lan = NONTOONYT.LAN
The [appdefaults] section does not need to be altered.

Download my full krb5.conf file here.

So, on to Samba. I'll start off with explaining how to use Samba to join to the domain and go into some alternatives later.

You can join a Linux machine to an Active Directory domain using the following command:
net ads join createcomputer=my_OU/other_OU osName=RHEL osVer=5 -U my_ad_account
You do not have to provide a domain controller to talk to here, since Samba will query DNS for _kerberos SRV records. You can provide one if you want to though, for example if you have multiple sites and want to talk to a close DC, with the -S option.

The createcomputer option defines the OU I want to create my computer object in. Have your Windows guys create an OU in which you can create objects for this. The osName and osVer options are not mandatory - they just fill some LDAP properties - but they're a nice touch.

This does not work without configuration after installing Samba. You need to configure /etc/samba/smb.conf a bit. Edit the following properties in your Samba configuration:
    workgroup = NONTOONYT
server string = box5 Linux server
netbios name = box5

security = ads
realm = nontoonyt.lan
kerberos method = system keytab
Download an example smb.conf file from here.

Having set up both /etc/krb5.conf and /etc/samba/smb.conf, you should be able to join your machine to a Windows 2008 Active Directory domain!

You can test getting a ticket after joining by running:
klist -k
kinit -k BOX5$
Neither command should give any output. Mind the caps in BOX5$.

Next up: sssd and LDAP!

sssd and LDAP configuration

Since a couple of versions, RHEL5 has an sssd package. Sssd stands for system security service daemon. In previous versions of RHEL, we had to use nss_ldap and a couple of hacks to get Active Directory authentication to work nicely. Now that there is sssd, we can finally do everything the clean way, without hacks. (I love sssd!)

Configuring sssd to provide authentication services through Kerberos and authorization services through LDAP is done by editing one (1) file. Note that your machine will not be able to use AD accounts after this just yet. But we're close :)

In the [sssd] section of sssd.conf, add a line reading
domains = nontoonyt.lan
This is not a Kerberos domain we are talking about, but a domain in sssd lingo. If you want to know more about this, please check the sssd website. At the bottom of the file, we will write our domain configuration:
cache_credentials = true
enumerate = false
min_id = 10000
max_id = 30000
id_provider = ldap
auth_provider = krb5
ldap_uri = ldap://dc01.nontoonyt.lan/
ldap_schema = rfc2307bis
ldap_user_search_base = cn=Users,dc=nontoonyt,dc=lan
ldap_user_object_class = person
ldap_user_modify_timestamp = whenChanged
ldap_user_home_directory = unixHomeDirectory
ldap_user_shell = loginShell
ldap_user_principal = userPrincipalName
ldap_group_search_base = ou=Groups,dc=nontoonyt,dc=lan
ldap_group_object_class = group
ldap_group_modify_timestamp = whenChanged
ldap_group_nesting_level = 5
ldap_account_expire_policy = ad
ldap_sasl_authid = BOX5$@NONTOONYT.LAN
ldap_krb5_init_creds = true
ldap_pwd_policy = mit_kerberos
chpass_provider = krb5
ldap_sasl_mech = GSSAPI
krb5_realm = NONTOONYT.LAN
krb5_validate = true
ldap_user_name = sAMAccountName
ldap_user_uid_number = uidNumber
ldap_user_gid_number = gidNumber
ldap_user_home_directory = unixHomeDirectory
ldap_user_shell = loginShell
ldap_user_principal = userPrincipalName
ldap_group_object_class = group
ldap_group_name = sAMAccountName
ldap_group_gid_number = gidNumber
ldap_force_upper_case_realm = True
Not all the options above are absolutely required. I like to make some defaults explicit though, to prevent possible changing defaults messing things up in the future and making it easy to see how things are configured by viewing one file.

I'll pick out a couple of the options to explain them. The cache_credentials option will allow users to log into this box when a domain controller is not available by caching credentials. You might or might not want this. Think this through for your setup.

Enumeration will allow you to use getent passwd and see AD based accounts. The trade off is a relatively slow sssd start up and some extra load on you DC, depending on the size of your search base. You might or might not want this. Think this through as well.

Take note of the ldap_sasl_authid property. This needs to be set to the SPN you use to get a ticket while testing in the "Kerberos configuration and domain join" chapter. In our case, that would be BOX5$. Mind the caps and the dollar sign.

Further more, notice the krb5_validate option, which prevents spoofing of TGT by crackers and the ldap_krb5_init_creds, which instructs the id_provider (LDAP, in our case) to obtain a TGT and use it to query the LDAP server (the DC, in our case). Many of the other options are defaults I like to make explicit. Read the sssd.conf, sssd-krb5 and sssd-ldap man pages for more

You can download my sssd.conf from here.

If you run into trouble, sssd might be a bit tricky to debug. Just set debug_level to 5 or so in the [sssd] section of sssd.conf and check to log files in /var/log/sssd.

Final configuration

A couple of things remain in order to allow AD based accounts to log into your server. First, we need to configure the name service switch configuration in /etc/nsswitch.conf. Very simple. Make sure stuff looks like this:
passwd:     files sss
shadow:     files
group:      files sss
That's is.

Next, open up /etc/pam.d/system-auth-ac (mine is here) and add the following lines as next to last in each section (account, auth, password and session):

auth        sufficient use_first_pass
account [default=bad success=ok user_unknown=ignore]
password    sufficient use_authtok
session     sufficient
You might want to add a line to the session section as well:
session     required umask=0022 skel=/etc/skel/
Put it as the first line in the session section. RHEL6 will need you to alter /etc/pam.d/password-auth similarly.

Finally, you can add Kerberos awareness to ssh by adding these lines to /etc/ssh/sshd_config (mine is here):
GSSAPICleanupCredentials yes
GSSAPIAuthentication yes
And these to /etc/ssh/ssh_config (mine here):
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
This allows 'hopping' from one Kerberized Linux server to another based on Kerberos alone, without entering your password.

Final considerations: DNS

Please note that Kerberos relies heavily on a properly setup DNS infrastructure. Give all your machines a proper FQDN, an A record, a PTR record and make sure the machine knows what it's called from the outside.

Almost all places I have worked at as a consultant have or had a broken DNS infrastructure to some extent. It always made my life a lot harder that it should have been. Please do this right the first time around!

Final considerations: Linux is not Windows

By enabling login to Active Directory based accounts and editing /etc/pam.d/system-auth-ac, you get AD based authentication and authorization for a lot of services, but not magically for everything. Apache, for example, has to be configured separately.

Also, Linux will not magically update it's password in AD. You can do that (check below at "Final considerations: alternative ways to join"), but not with Samba in this setup.

Linux is not site aware. If you have a large site, spanning multiple physical locations, you will have to specify which DC's you want to use per physical location in the /etc/krb5.conf and /etc/sssd/sssd.conf files. Linux is able to query for _kerberos records in DNS, but will *not* map these to a physical location using the AD "Sites" mechanism.

Final considerations: alternative ways to join

As a rule, most people use Samba's 'net' command to join a Linux server to Active Directory, but there are other ways.

First, there is ktpass.exe on Windows. This tool can help you by exporting a keytab on the domain controller. This keytab can then be copied to the Linux machine and used there. Use must either join the machine using Samba (or msktutil, see below) first or pre-create an object for it.

Pro: the keytab exported with ktpass.exe only has strong encryption types in it (AES-128, AES-256 and RC4).
Con: needs to be done on the domain controller and is therefore slightly

And then there is msktutil. I love this tool. I wish it would be in all distro's. And thankfully, things seem to be moving in the right direction for this: work's already being done to get it in Debian and a fork of it might move into Fedora and EPEL[1].

What this tool can do, is connect to a DC, create a machine object and get you a proper keytab with only RC4 and AES encryption types, all from that comfortable place we call a Linux machine. Love it. Coming soon to a distro near you!


Final considerations: winbind

I don't like using winbind. I have seen it break too exotically and too often in the past. I'm probably biased and it's probably all fixed, but I'm happy with sssd to talk to Active Directory through LDAP and Kerberos at the moment.

That said, there are enough shops that use winbind exclusively to talk to AD, so if it works for you, great, go for it!

The sssd people are allegedly working on a winbind backend for sssd, which will make it a bit cleaner (in my mind), so I might test that when it arrives.

Winbind has some different ways of mapping users to UID's. It can use LDAP (much like we do in this article), it can dynamically generate UID's and it can generate a static (i.e. the same on all servers) UID based on your SSID.

For shops in which you cannot change AD by installing the Subsystem for Unix-based applications, winbind might be the way to go.

Final configurations: LDAPS

A final option to get to AD based accounts on your Linux server would be to create a service account in AD, enable LDAPS (LDAP over SSL) in your AD and then use plain LDAP(S) for authentication and authorization.

Enabling LDAPS on an AD domain is not something you'll easily talk your Windows guys into, but as a final option, a last resort, knowing about it might come in handy.

Let me know if this article helps you out. If you need consulting services for integrating Linux machines in Active Directory, contact me or my employer. I'm sure we can work something out.

Please, if you find typo's or factual errors in this post: please notify me and I will correct them.

Good luck!


Ken Dreyer said...

Hey, thanks for the encouraging comment re: msktutil. Hope the review passes soon.

I'm discussing with my employer the possibility of moving the Git repo to somewhere more "official"; we'll see what happens.

Unknown said...

Out of curiosity, what do you consider 'more official'?

I'd co-maintain the msktutil package with you for Fedora, if you ever need a co-maintainer, btw :)

Challa said...

Thank you. it is very usefull
Hi team,

Recentely we brought new 70 puppy linux desktop. already we have 120 windows desktops, For windows destops we are maintaining with windows server 2008. windows desktops in xxx domain. now i need to control the user activities and take these 70 puppy linux desktops to xxx domain. how can we do it. please advice on this.

Challa said...

Hi Team,

Puppy linux is single user system.
suppose i have taken redhat linux server to maintain the all the puppy linux desktops and intall the dns and samba and ldap. is it possible to maintain the all puppy linux desktops..
please advice

Mike Bentley said...

Have you done any work dealing with multiple domains?
The following scenario in particular:

LinuxBox1 is joined to DomainA
DomainA has a one way trust to DomainB
User@domainB can login to LinuxBox1

Unknown said...

@Mike Bentley: not much experience in that area. Should be possible though. Don't see this very often...

tahersb said...

Hey Maxim,
Awesome resource this turned out to be! Thanks a bunch.
I am trying to integrate RHEL 6 with Windows 2008 R2 domain. I am using the SSSD/Kerberos/LDAP configuration to achieve this.

My LDAP searches are working fine, NTP is synchronized, I have created a Computer object for the Linux machine in AD, Ran setspn and ktpass utilities on windows, created users on AD with thier linux attributes set, Fixed DNS configs.

Now heres what I am doing:
1. login to RHEL6 (lets call it rhel-srv) as root through puTTY. I have to use a ppk file to login to it because it is an EC2 instance.

2. ssh

and I am getting a
"Permission denied (publickey,gssapi-keyex,gssapi-with-mic)" error.

I cant get my head around to fix this issue! Its driving me nuts.
Do I need to use a .pem file with ssh to login ? Would it not defeat the whole purpose of having an AD authenticate your users? or does this belief not apply to instances in the cloud?

I have posted this problem on AWS forums as well. But no1 seems to have any idea about it.

I hope you can help me fix this :(

medievalist said...

if you find typo's or factual errors in this post: please notify me and I will correct them.

There is no apostrophe in typos.

It's short for typographical errors, a phrase which does not denote ownership, is not a contraction, and contains no full glottal stops. Those are the only three valid uses of apostrophes in English.

For example M'buntu's watch says it's three o'clock uses all three forms - it's is a modern contraction of it is and o'clock is a historical contraction of of the clock. Our friend M'buntu has a full glottal stop in his name, and the watch belongs to him.

Hey, you asked! ;) Thanks for the excellent info on sssd.

Unknown said...

Thanks for the guide, I'm trying to roll this with some RHEL5.8 servers and 2008R2.

One question I have, and forgive me if this is a remedial one, is in regards to your linked sssd.conf file:

In the last commented section (the commented example Active Directory LDAP section) before your active config directives, there are directives for the following options that are /missing/ from the directives you're using:

; ldap_default_bind_dn = cn=Administrator,cn=Users,dc=example,dc=com
; ldap_default_authtok_type = password
; ldap_default_authtok = YOUR_PASSWORD

How are you binding to LDAP without these directives?

Unknown said...

Thanks for the guide, I'm trying to roll this with some RHEL5.8 servers and 2008R2.

One question I have, and forgive me if this is a remedial one, is in regards to your linked sssd.conf file:

In the last commented section (the commented example Active Directory LDAP section) before your active config directives, there are directives for the following options that are /missing/ from the directives you're using:

; ldap_default_bind_dn = cn=Administrator,cn=Users,dc=example,dc=com
; ldap_default_authtok_type = password
; ldap_default_authtok = YOUR_PASSWORD

How are you binding to LDAP without these directives?

Unknown said...

@Chris Bellers: you don't need those directives, because you use Kerberos for the bind.

@medievalist: lol. I'll fix that. Some day ;)

@Stabwound: bit hard to debug this like this. Did you get a valid Kerberos ticket prior to trying this out?