Active Directory Single Sign-On for Linux Intranet Servers

I mentioned a while ago that I have a Linux web server set up with Kerberos SSO in our AD domain. Setting it up was a lot more tedious than it seems like it should have been. I found bits and pieces of useful information here and there, and some step-by-step guides to help with specific sub-tasks, but I couldn’t find a good, intranet-specific guide to help me understand the big picture—what pieces I needed (and didn’t need) and how they fit together. So here’s part 1 of my attempt to rectify that situation (part 2 will be the WordPress integration—I’m still working on that part).

Requirements

The basic requirement was to set up a LAMP server running WordPress, which should integrate transparently into our Active Directory network and become the CMS and web front-end for our intranet. To make the integration work, I needed AD-based authentication for:

  • SSH (for server administrators)
  • SFTP (for server administrators and web developers)
  • Apache (for all domain users)

The latter needed to be a true, transparent single sign-on (SSO), so the end user is never prompted for credentials while using the intranet (at least, when using it from a PC on the network, which is the primary use case for our intranet). That left me two options: NTLM and Kerberos. For maximum flexibility I chose Kerberos, which will allow our site to access other resources (on SharePoint, for example) on the user’s behalf (that’s beyond my scope for today—see “Understanding Kerberos Double Hop” for an introduction).

Note: In principle I could also have set up SAMBA, but I didn’t want the added complexity or security liability. I don’t think any of this setup would preclude the use of SAMBA if you need it in your environment.

Step 1 – Linux

Install Ubuntu Server 11 64-bit, install all the updates, and do the usual initial setup stuff.

Note: some of the file paths and commands in here are Ubuntu-specific; if you’re running a different flavor YMMV.

Caution! Before you go any further, make sure you have a local user account that can SSH, SFTP, and sudo. Keep one SSH session always logged in with this local account. Every time you make a change, open another SSH window and make sure you can still log in with the local account. It’s so easy to make a mistake and lock yourself out. (Not that I’ve ever done that, of course…)

Resources

Step 2 – AD integration for OS, SSH, and SFTP

I was all set to do this the hard way, hand-editing configuration files, but then I stumbled on BeyondTrust Power Broker Identity Services Open (formerly called Likewise Open). I signed up, they sent me an email with some helpful links (download, documentation). I downloaded the PBIS Open 6.5 64-bit Debian package, followed a few directions in their admin guide, and had the machine joined to the AD domain and authenticating me with SSH and SFTP in just a few minutes. Slick!

One little tweak: I wanted to be able to log in with just my username—not EXAMPLE\username or username@example.com—so I added one line in the [libdefaults] section of /etc/krb5.conf:

default_realm = EXAMPLE.COM

Note: Kerberos realms must be ALL CAPS or things won’t work.

Caution! Remember to lock down which users can log in with SSH and SFTP. I won’t cover that here, but don’t forget to do it!

BeyondTrust also followed up with a phone call within about an hour and got me in touch with a support guy to help answer my questions about the next step…

Resources

Step 3 – Apache, PHP, and mod_auth_kerb.so

I know Apache 2.4 is released, but for now I stuck with 2.2 because there’s an Ubuntu package for it, it does everything I need, and the custom version of mod_auth_kerb.so that’s part of PBIS supports it. Make sure PHP 5 is installed—you’ll need it to test your SSO setup.

BeyondTrust Power Broker Identity Services Open will help us here, too. We just have to make a few adjustments to the standard setup. Find the section in the PBIS Installation and Administration Guide about configuring SSO for Apache (starts on page 188)—I followed their procedure for the most part but hit a few pitfalls along the way.

Big thanks to Joshua McClintock at BeyondTrust, who helped me find my way through these pitfalls and come out with a better understanding of how the pieces fit!

Pitfall #1

The PBIS documentation says to run ldd against the Apache binary and check the version of libgssapi_krb5.so.2. Don’t worry about that part—mine didn’t list that library at all and everything still worked fine for me.

Pitfall #2

The documentation says that the Apache integration bits (tweaked version of mod_auth_kerb.so) are in their AppIntegration package (which isn’t yet available for PBIS 6.5), but I think that’s an error—at least in version 6.1 it was in the main package. The PBIS 6.5.561 package (current as I write this) doesn’t include the /opt/pbis/apache directory you’re looking for. I’m hoping they fix that packaging error (or release the AppIntegration package) soon, but meanwhile I found what I needed in the 6.1 package. I stashed the 6.1 package in my home directory and then did a little digging:

chmod 700 ~/LikewiseOpen-6.1.0.8780-linux-amd64-deb.sh
~/LikewiseOpen-6.1.0.8780-linux-amd64-deb.sh --noexec
dpkg --extract ~/LikewiseOpen-6.1.0.8780-linux-amd64-deb/packages/linux/x86_64 ~/LikewiseOpen-6.1.0.8780
sudo cp ~/LikewiseOpen-6.1.0.8780/opt/likewise/apache /opt/pbis/apache

Note: I already had PBIS 6.5 installed and working, so I didn’t want to install 6.1–I just extracted the package contents so I could recover the one file I neeeded.

If all worked out, /opt/pbis/apache/2.2/mod_auth_kerb.so should now exist. Here’s how we tell Apache to use it:

sudo echo LoadModule auth_kerb_module /opt/pbis/apache/2.2/mod_auth_kerb.so > /etc/apache2/mods-available/auth_kerb_pbis.load
sudo rm /etc/apache2/mods-enabled/auth_kerb.load
sudo ln –s /etc/apache2/mods-available/auth_kerb_pbis.load /etc/apache2/mods-enabled/auth_kerb_pbis.load

Pitfall #3

If you try to restart Apache at this point, you’ll get an error and it will fail to start up. To avoid that, edit /etc/apache2/envvars and append this line at the end:

export LD_LIBRARY_PATH=/opt/pbis/lib:/opt/pbis/lib32

That tells Apache to use the PBIS-tweaked version of libcom_err.so.3.

Now we’re ready to restart Apache, so our changes take effect:

sudo service apache2 restart

Resources

Step 4 – AD Domain Admin Stuff

If you’re not an AD domain admin, find one and get on their good side—you’re about to ask them to run a command they’ve probably never heard of, on a domain controller, with full admin privileges. We need to set up a Kerberos Service Principal Name (SPN) for the HTTP service; an SPN needs to be tied to an AD account. An SPN and its associated crypto key are stored in a keytab file (forgive me if my Kerberos terminology isn’t quite right, but you get the idea).

First, create a service account in AD for the web server (separate from its machine account). Set the password never to expire, and remember the password for the next step.

Next, create the keytab file:

ktpass /out webserver.keytab /princ HTTP/webserver.example.com@EXAMPLE.COM /mapuser serviceaccountname /pass serviceaccountpassword /ptype KRB5_NT_PRINCIPAL /crypto RC4-HMAC-NT

Note: ktpass must be run on a domain controller, with Administrator privileges. Right-click the Command Prompt icon and choose Run As Administrator. See this post from Scott Lowe describing the error you’ll get otherwise—the answer is in the first comment.

Another note: rumor has it that ktpass.exe wasn’t included with Windows Server 2003—it was part of the Support Tools pack that you had to download and install separately.

Transfer the webserver.keytab file to /etc/apache2/auth on the Linux server.

Caution! Use a secure method like SFTP to transfer the keytab file—it contains the keys to the authentication kingdom!

Lock down permissions on the keytab file:

sudo chown www-data /etc/apache2/auth/webserver.keytab
sudo chmod 400 /etc/apache2/auth/webserver.keytab

Resources

Step 5 – Testing SSO

Create a test directory (/var/www/sso for example) with an index.php file:

<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SSO Test</title>
</head>
<body>
<?php
  if (preg_match('/(?P<domain>\w+)\\\\(?P<username>\w+)/', $_SERVER['REMOTE_USER'], $m)) {
    $username = strtolower($m[username]);
    $domain = $m[domain];
  } else {
    $username = $remote_user;
  }
?>
<h1>Single Sign-On Test</h1>
<p>Welcome! You are logged in as <?=$username?> (from the
  <?=$domain?> domain).</p>
</html>

Here’s where we tell Apache to require Kerberos authentication to access the sso directory. Create a new file under /etc/apache2/conf.d (when Apache starts up, it includes any files in that directory as part of its configuration). I called mine dir_sso, but the file name doesn’t really matter:

<Directory "/var/www/sso">
  Options MultiViews FollowSymLinks
  AllowOverride None
  Order allow,deny
  Allow from all
  Require valid-user
  AuthType Kerberos
  AuthName "Kerberos Login"
  KrbAuthRealms EXAMPLE.COM
  Krb5Keytab /etc/apache2/auth/webserver.keytab
  KrbMethodNegotiate on
  KrbMethodK5Passwd off
  KrbSaveCredentials on
</Directory>

Note: remember to restart Apache so the change takes effect.

From a Windows machine that’s a member of the AD domain and logged in as a domain user, point a browser (Chrome and IE work; I haven’t tested anything else) at http://webserver.example.com/sso. If everything is working, it will tell you your AD username and domain.

Resources

Step 6 – MySQL and WordPress

Make sure you have MySQL and the Apache mod_rewrite module installed, and then follow the WordPress “Famous 5-Minute Install.”

There’s an Ubuntu package for WordPress that might work for you, but it does some stuff with symlinks that made me nervous: my goal was to have three independent WP installations (development, test, and production) that I could manage, upgrade, break, and fix independently. So I moved everything from /var/www to /var/www/default, changed the path in /etc/apache2/sites-available/default, and restarted Apache. Once I had the default site stuff out of the way, I unzipped WordPress into three different directories:

  • /var/www/wpdev
  • /var/www/wptest
  • /var/www/wpprod

I set up a separate DNS alias and Apache virtual server for each instance and then followed the “Famous 5-Minute Install” for each WP installation.

Note: all three WP installs share a single instance of MySQL, but to keep things cleanly separated, I created 3 separate MySQL users and 3 separate databases; for clarity, username and database name are same, e.g. wp_dev, wp_test, wp_prod. There are other ways to do it (one DB with each WP install having a different prefix), but to me this seems like the best balance between isolation and administrative overhead. Your environment may have different requirements, so give it some thought before you commit.

You should now have one or more working WordPress installations.

Resources

Step 7 – Enable SSO for WordPress Directories

Note: just to be clear, my end goal is automatic creation of WP accounts, mapping them to AD accounts, and transparent single sign-on to WordPress. I’m not there yet. What this step does is tell Apache to require Kerberos authentication when a user accesses the WP site. WordPress itself—probably via a plugin—will have to handle the rest (and I’ll write it up when it’s done).

You’ll need an additional Kerberos keytab entry for each virtual server (DNS alias) that needs SSO. Again, get on a domain admin’s good side, and then ask them to do the ktpass.exe thing again, just like in step 4.

ktpass /out dev.keytab /princ HTTP/intranet-dev.example.com@EXAMPLE.COM /mapuser serviceaccountname /pass serviceaccountpassword /ptype KRB5_NT_PRINCIPAL /crypto RC4-HMAC-NT
ktpass /out test.keytab /princ HTTP/intranet-test.example.com@EXAMPLE.COM /mapuser serviceaccountname /pass serviceaccountpassword /ptype KRB5_NT_PRINCIPAL /crypto RC4-HMAC-NT
ktpass /out prod.keytab /princ HTTP/intranet.example.com@EXAMPLE.COM /mapuser serviceaccountname /pass serviceaccountpassword /ptype KRB5_NT_PRINCIPAL /crypto RC4-HMAC-NT

SFTP these files to /etc/apache2/auth on the Linux server, and remember to lock down the permissions like we did in step 4.

Optional: if you end up with multiple keytabs and want to consolidate the SPNs into a single keytab file, /opt/pbis/bin/ktutil is the tool you seek.

The configuration files for Apache virtual servers live under /etc/apache2/sites-available. For each virtual server that needs SSO (these don’t really have to be WordPress sites—that just happens to be what I’m using), make sure the corresponding config file looks like this:

<VirtualHost *:80>
  DocumentRoot "/var/www/dev"
  ServerName intranet-dev.example.com
  ServerAlias intranet-dev
  <Directory "/var/www/dev">
    Options +Indexes
    Allow from all
    Require valid-user
    AuthType Kerberos
    AuthName "Kerberos Login"
    KrbAuthRealms EXAMPLE.COM
    Krb5Keytab /etc/apache2/auth/dev.keytab
    KrbMethodNegotiate on
    KrbMethodK5Passwd off
    KrbSaveCredentials on
  </Directory>
</VirtualHost>

Resources

Next Steps

I’m currently working through the details of getting SSO to work transparently with WordPress. Once I get that working, I plan to write about that experience, so stay tuned!

Edit – September 5, 2013

Rob A. shared a couple of comments. I haven’t looked into or tested any of this (none of my current work involves Linux intranet web servers), but I thought they might help someone else (or perhaps Future Me). Thanks, Rob!

Advertisements

Author: Jeff Garretson

Husband, father. Data Scientist. Explorer of Powerful Ideas. Faith and Reason in one package.

5 thoughts on “Active Directory Single Sign-On for Linux Intranet Servers”

  1. Hello. Nice effort! I also have to set up sso for a wiki running on apache. It work great for the internal network, but I tried forwarding the 443 port to have access from outside. It open the page, asks me from credential but after that I get an Server Internal error. Did you try to access the server from outside? Do you have any idea where shoud I look? Because I spent all day trying but I found no solution…

Comments are closed.