Pages

August 8, 2008

Twiki Authentication via Kerberized Apache

I've got a lot of information locked up in my head. Without a doubt, I know that others do as well. And many times, that information can be useful to folks in other departments. So I'm building a wiki. During the Olympic Games opening ceremony.

I've selected Twiki as it's touted as an appropriate wiki for corporate environments. I like the idea of an open wiki like Wikipedia, but I'm a realist and know that _some_ control is required in an office setting. It's collaboration with structure. If Twiki doesn't work out, I'll try another one on.

Since I already manage several password stores (and scores more personal passwords), I don't want to maintain another set of credentials. I also want to easily enable this new wiki for my users without them needing to login with a new ID. It's a struggle sometimes to strike that fine balance between security and usability, but it's not impossible. To that end, I've decided to integrate Twiki with Active Directory; my users will be able to log in to the wiki using their workstation credentials once for their entire session. To do this, I need to kerberize Apache. If you're new to Kerberos, see my previous post for a beginner's overview.

What follows is a brain dump of my trials to achieve this goal.

There are several online pages that provide information on this subject, some more relevant or verbose than others. However, I did not find any single source that provided all the steps I needed to get this working, nor the fixes for some snags I encountered. Hopefully, this helps you.

Assumptions
  • You're familiar enough with Linux/UNIX systems to administrate them (i.e. edit config files, compile source code, restart services)
  • You're comfortable working in Windows environments (GUI and command line) and you're familiar with administration tools such as the Active Directory Users and Computers MMC Snap-in
  • You _have_ a Linux system, running Apache, and you want to kerberize it!
Let's start.

Steps

Configure Kerberos
Build the Apache module 'mod_auth_kerb'
Active Directory and Service Principals
Creating Service Principals with ktpass
Configure mod_auth_kerb to Authenticate Against AD
VirtualHost Caveat

----------||----------

Configure Kerberos

Edit the Kerberos configuration file (typically /etc/krb5.conf; check your distro). The notable bits are in bold:
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log

[libdefaults]
default_realm = MYDOMAIN.COM
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
forwardable = yes

[realms]
MYDOMAIN.COM = {
kdc = dc1.mydomain.com:88
kdc = dc2.mydomain.com:88
admin_server = myadmin.mydomain.com:749
default_domain = mydomain.com
}


[domain_realm]
.mydomain.com = MYDOMAIN.COM
mydomain.com = MYDOMAIN.COM

[kdc]
profile = /var/kerberos/krb5kdc/kdc.conf

[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
forwardable = true
krb4_convert = false
}
Confirm successful authentication using a known-good account (like your own!)...
[root@logjam]# kinit jdoe@MYDOMAIN.COM
Password for jdoe@MYDOMAIN.COM:
As long as 'kinit' doesn't present any errors, you're good to go. You can confirm that you've been granted a ticket-granting ticket (TGT) with 'klist':
[root@logjam]# klist -5
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: jdoe@MYDOMAIN.COM

Valid starting Expires Service principal
08/07/08 15:07:13 08/08/08 01:07:43 krbtgt/MYDOMAIN.COM@MYDOMAIN.COM
renew until 08/08/08 15:07:13
Destroy the Kerberos ticket; we don't need it:
[root@logjam]# kdestroy
Build the Apache module 'mod_auth_kerb'

Apache natively supports a few authentication protocols such as Basic and Digest but we want to use Kerberos. In order to add in that functionality, we'll need to install the module 'mod_auth_kerb'. I downloaded the source (current package == mod_auth_kerb-5.3) from Sourceforge. Unpack the code in your favorite build directory; I like '/usr/local/src':
[root@logjam src]# tar zxf mod_auth_kerb-5.3.tar.gz
And change into the source directory:
[root@logjam src] # cd mod_auth_kerb-5.3
Configure the package for installation. If you have a custom location for your Apache location, be sure to specify that with the --with-apache= argument so that the module will get installed in the correct location (i.e. /usr/local/apache2/modules). I also like to explicitly disable Kerberos 4 support and make sure my Kerberos 5 binaries/libraries are found for linking:
[root@logjam mod_auth_kerb-5.3]# ./configure \
> --with-apache=/usr/local/apache2 \
> --with-krb4=no \
> --with-krb5=/usr/kerberos
Assuming that 'configure' hasn't complained about missing support for anything, build and install the module:
[root@logjam mod_auth_kerb-5.3]# make
[root@logjam mod_auth_kerb-5.3]# make install
Next, tell Apache to load in the module to provide support for Kerberos authentication. Edit the Apache config file, httpd.conf, and add the following line in the section where modules are explicitly loaded:
LoadModule auth_kerb_module modules/mod_auth_kerb.so
Restart the Apache service and check for errors.

Active Directory and Service Principals

This is the part I spent most of my time with. Apache's been kerberized and now I need a valid service principal to allow my users to authenticate from Twiki. From my research I found that Microsoft, in their usual wisdom, decided that they would implement their own flavor of Kerberos. They adhere to the protocol (as much as I can tell considering I've not bothered to read the RFCs in full!) except for one critical convention that was staring me in the face the whole time and didn't fully regsiter: Windows account names are different from strict Kerberos principals. This note from Step-by-Step Guide to Kerberos 5 (krb5 1.0) Interoperability explains it (emphasis mine):
Windows 2000 account names are not multipart as are Kerberos principal names. Because of this, it is not possible to directly create an account of the name host/hostname.dns.com. Such a principal instance is created through service principal name mappings. In this case, an account is created with a meaningful name hostname and a service principal name mapping is added for host/hostname.dns.com. This is the purpose of using Ktpass with the princ and mapuser switches.
To summarize, AD creates account names (loosely, principals, in this context) using the convention username@domain.tld and Kerberos is looking for principals whose convention is service/hostname@REALM. (Sidenote: Bascially, a Windows domain and Kerberos realm are synonymous. Both require a functioning DNS implementation. At the least, some DNS server needs to know about your domain and can return records so that AD and Kerberos work.) So we need to do a few things to ensure interoperability:
  1. Create a user account that will act as the Linux host's principal (i.e. computer account). Some folks suggest installing Samba and joining the doman, but it doesn't need to be that complicated.
  2. Create a second user account that will be used to create the service principal that Apache will depend on for authentication.
First, use the Active Directory Users and Computers (ADUC) snap-in to create two user accounts. This is important; even Microsoft's documentation (see the previously linked page) calls for this step. Do not create computer accounts. We'll call our accounts 'twiki' and 'twikiService'. They don't need any special configuration; just be sure to check the 'Password never expires' box when creating the accounts.
  • twiki = Host principal (i.e. "machine"/computer account)
  • twikiService = Service principal (account for the web service, Apache)
You may be wondering why we need these two accounts and honestly, I was wondering the same when I started this project. You can't have the service principal without the host principal. The service principal includes a valid DNS hostname (remember: service/hostname@REALM ?). And since we don't have Samba installed so that our Linux box can join the AD domain, we're effectively tricking AD into believing we've got a computer account that matches the hostname in our service principal. This will become clearer as we progress.

Creating Service Principals with ktpass

Now we need to generate the service principals using 'ktpass'. This command is available on domain controllers or in the Windows Resource Kit. 'ktpass' can also generate a keytab (key table) file that we'll use later. The keytab file allows a service to request tickets without needing to send a password for each request. We'll start with the host principal. We'll create a principal named HOST/twiki.mydomain.com@MYDOMAIN.COM and map it to the user account twiki. The -princ and -mapuser arguments to 'ktpass' create the service principal and map the user account, respectively.

In some cases, I've explicitly specified arguments whose values are the same as the command's defaults; I've done this so that the reader can see exactly what's happening. Of note is the +rndpass argument, however. It tells ktpass to generate a random password. You don't need this password to match the account's password. This also allows you to change the account's password without affecting the keytab file and hence authentication by the service.

The command and its output are below (line breaks added for easier reading where necessary):
C:\Resource Kit>ktpass -princ HOST/twiki.mydomain.com@MYDOMAIN.COM
-mapuser twiki -mapop add -out twikiHOST.keytab +rndpass -crypto DES-CBC-CRC
+desonly -ptype KRB5_NT_PRINCIPAL +dumpsalt

Targeting domain controller: dc1.MYDOMAIN.COM
Using legacy password setting method
Successfully mapped HOST/twiki.mydomain.com to twiki.
Failed to retrieve values for property msDS-KeyVersionNumber: 0x10.
The msDS-KeyVersionNumber attribute does not exist on the target DC.
Assuming this is a Windows 2000 domain, and setting
the Key Version Number in the Keytab to 1.

Supply "/kvno 1" on the command line to skip this message.
Failed to retrieve values for property msDS-KeyVersionNumber: 0x10.
Building salt with principalname HOST/twiki.mydomain.com and domain MYDOMAIN.COM...
Hashing password with salt "MYDOMAIN.COMHOSTtwiki.mydomain.com".
Key created.
Output keytab to twikiHOST.keytab:
Keytab version: 0x502
keysize 69 HOST/twiki.mydomain.com@MyDOMAIN.COM ptype 1
(KRB5_NT_PRINCIPAL) vno 1 etype 0x1 (DES-CBC-CRC) keylength 8 (0xc49754ce6e15e902)
Account twiki has been set for DES-only encryption.
If you're running a Windows 2000 Active Directory domain, ignore errors regarding the key version number (aka kvno). They're harmless.

To confirm that the service principal has been created, you can use the 'setspn' command (also available on domain controllers and the resource kit). Pass -L accountName to the command to display the service principal it's mapped to:
C:\Resource Kit\>setspn -L twiki
Registered ServicePrincipalNames for CN=twiki,OU=Service_Accounts,DC=MYDOMAIN,DC=com:
HOST/twiki.mydomain.com
Next, we'll create the service principal for Apache. The service name will be HTTP so that the service principal will look like this: HTTP/twiki.mydomain.com@MYDOMAIN.COM. Using 'ktpass', we'll map this service principal to the account named twikiService.
C:\Resource Kit>ktpass -princ HTTP/twiki.mydomain.com@MYDOMAIN.COM
-mapuser twikiService -mapop add -out twikiHTTP.keytab +rndpass -crypto DES-CBC-CRC
+desonly -ptype KRB5_NT_PRINCIPAL +dumpsalt
Targeting domain controller: dc1.MYDOMAIN.COM
Using legacy password setting method
Successfully mapped HTTP/twiki.mydomain.com to twikiService.
Failed to retrieve values for property msDS-KeyVersionNumber: 0x10.
The msDS-KeyVersionNumber attribute does not exist on the target DC.
Assuming this is a Windows 2000 domain, and setting
the Key Version Number in the Keytab to 1.

Supply "/kvno 1" on the command line to skip this message.
Failed to retrieve values for property msDS-KeyVersionNumber: 0x10.
Building salt with principalname HTTP/twiki.mydomain.com and domain MYDOMAIN.COM...
Hashing password with salt "MYDOMAIN.COMHTTPtwiki.mydomain.com".
Key created.
Output keytab to twikiHTTP.keytab:
Keytab version: 0x502
keysize 69 HTTP/twiki.mydomain.com@MYDOMAIN.COM ptype 1
(KRB5_NT_PRINCIPAL) vno 1 etype 0x1 (DES-CBC-CRC) keylength 8 (0xe0374fc44373f138)
Account twikiService has been set for DES-only encryption.
Run 'setspn' again to confirm the service principal is mapped:
C:\Resource Kit>setspn -L twikiService
Registered ServicePrincipalNames for CN=twikiService,OU=Service_Accounts,DC=MYDOMAIN,DC=com:
HTTP/twiki.mydomain.com
Note the keytab output (twikiHTTP.keytab). We'll need that file in the next section.

Configure mod_auth_kerb to Authenticate Against AD

Now that we have our keytab file, we need to securely transfer it to our Apache host. I used the PuTTY SSH command suite (pscp in this example):
C:\PuTTY>pscp twikiHTTP.keytab me@logjam:
me@logjam's password:
twikiHTTP.keytab | 0 kB | 0.1 kB/s | ETA: 00:00:00 | 100%
Move/copy this file to the root of your Apache installation (or whereever you prefer) and secure the file for reading only by the Apache service:
[root@logjam apache2]# mv ~me/twikiHTTP.keytab .
[root@logjam apache2]# chown apache:apache twikiHTTP.keytab
[root@logjam apache2]# chmod 600 twikiHTTP.keytab
Make sure that you can grab a ticket-granting ticket using the 'kinit' command:
[root@logjam apache2]# kinit -k -t twikiHTTP.keytab HTTP/twiki.mydomain.com@MYDOMAIN.COM
Confirm that the we have a ticket-granting ticket (TGT):
[root@logjam apache2]# klist -5
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: HTTP/twiki.mydomain.com@MYDOMAIN.COM

Valid starting Expires Service principal
08/08/08 14:35:33 08/09/08 00:35:33 krbtgt/MYDOMAIN.COM@MYDOMAIN.COM
renew until 08/09/08 14:35:33
And destroy the TGT; we don't need it:
[root@logjam apache2]# kdestroy
If you encounter any issues with the kinit command, you may want to check the logs on your domain controllers for more information.

Now that we have a working keytab file, we need to configure the mod_auth_kerb module within Apache's configuration file. Add the following lines within the Directory block where the main Twiki site exists:
Order Deny, Allow
Allow from all

AuthType Kerberos
KrbAuthRealms MYDOMAIN.COM
KrbServiceName HTTP
Krb5Keytab /usr/local/apache2/twikiHTTP.keytab
KrbMethodNegotiate on
KrbMethodK5Passwd on
Require valid-user
Be sure to comment out the line AuthType Basic to disable Basic authentication.

At this point, when accessing the main Twiki page, users should be prompted for a username and password. Their AD credentials will get them logged in for the entire session.

VirtualHost Caveat

Well, the story isn't quite over. Even after all the above steps, I continued to run into an issue preventing me from logging into my Twiki site. My Apache error_log file was full of the following message:
[Fri Aug 08 16:19:41 2008] [error] [client 1.2.3.4] failed to verify krb5 credentials:
Server not found in Kerberos database
The reason for this error was that I'm hosting Twiki in an Apache instance that serves other websites as well. In other words, Twiki is defined as a virtual host in a VirtualHost block. Its DNS name (twiki.mydomain.com) is not the same as the physical host (logjam.mydomain.com) running Apache. What really bit me was that my /etc/hosts file had an entry for the local host in it (common for most Linux distros):
1.2.3.4   logjam.mydomain.com
So, anytime mod_auth_kerb tried to authenticate, it was passing HTTP/logjam.mydomain.com@MYDOMAIN.COM to my domain controllers and they kept replying that they did not know who/what logjam.mydomain.com was. I commented that line out of the hosts file and all is well.

Now, some may argue that I should just have created the service principal for logjam.mydomain.com; but I'm inclined towards flexibility and want to be ready for the chance that I may indeed move my Twiki site to a dedicated host that uses the same fully qualified domain name (FQDN). If that happens, I'm all set. If not, or I migrate it to another multi-site host, I'll just refer back to this page!

No comments:

Post a Comment