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
needs.

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:
[libdefaults]
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:
[realms]
NONTOONYT.LAN = {
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:
[domain_realm]
.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:
[domain/nontoonyt.lan]
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
information.

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 pam_sss.so lines as next to last in each section (account, auth, password and session):

auth        sufficient    pam_sss.so use_first_pass
account [default=bad success=ok user_unknown=ignore] pam_sss.so
password    sufficient    pam_sss.so use_authtok
session     sufficient    pam_sss.so
You might want to add a pam_mkhomedir.so line to the session section as well:
session     required      pam_mkhomedir.so 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
cumbersome

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!

[1] https://bugzilla.redhat.com/show_bug.cgi?id=713313

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!

Saturday, October 1, 2011

Fixing suspend on my laptop permanently with grub2

I own a slightly aging Sony Vaio VGN-FW21E, which still is a nice piece of hardware, but has one quirk: it doesn't suspend right. It hasn't done so for the past four or five kernel releases and it still doesn't work properly :(

With older versions of Fedora (<16), it was easy to edit grub.conf (or menu.lst, whatever) and fix this problem by appending "acpi_sleep=nonvs" to the kernel line. With grub2, however, things have changed a bit.

To permanently add something to the kernel line to the grub2 configuration, you need to edit /etc/default/grub (which I consider to be an odd filename on a Fedora-based system) and edit the GRUB_CMDLINE_LINUX variable defined there. For me, it needs to read:

GRUB_CMDLINE_LINUX="quiet rhgb acpi_sleep=nonvs"


If you would like to have a more verbose kernel during boot, or disable Plymouth, you could remove the "quiet" and "rhgb" keywords respectively.