Pages

November 7, 2008

Sudowin: One Answer to the Struggle Between Security and Usability

On any given day, I guarantee that a sysadmin is fighting the battle between protecting a company's assets and maintaining usability. Most folks will argue that you can't have both. I will always counter that you can; security and usability are not mutually exclusive. That's not to say that it's easy. Without a doubt, striking the right balance can be difficult in the face of company culture, policy, and the technology itself. But given a fair mix of review, design, and most importantly, patience, it's an attainable goal.

I've always longed for Windows equivalents to many Unix utilities that I use, one of them being the excellent 'sudo' (a full treatment of sudo is beyond the scope of this post so I'd encourage readers to research the utility further for more information). I've seen several attempts to implement sudo or sudo-like functionality for Windows but I never found anything that _was_ sudo. I gave up looking for a while.

Recently, I had a need to revisit the subject and found, among many options, sudowin. It's a slick implementation of sudo available for Windows systems and so far has not disappointed. The project provides an MSI file for easy installation as well as the source code for the curious hacker. The project site (on Sourceforge) doesn't provide documentation, but the folks at SANS have posted a PDF version of the help file.

The current version is 0.4.2-r208 and was released on 9/30/2008. Written in C#, the program installs itself as a service that monitors calls to the sudo command. By default, the service verifies that the user is part of the local 'sudoers' group before checking that the user is authorized to execute the target command/program. Authorized users and commands can be configured in the 'sudoers.xml' file beneath the project's program directory (C:\Program Files\Sudowin). One caveat: the current version of sudowin does not yet support resolving group membership for either local groups or Active Directory groups. You must explicitly define _each_ user that is authorized via the local PC's 'sudoers' group. This does not scale well in large environments, but again, it's open source so feel free to contribute.

The documentation provides great guidance on the configuration file, so I won't go into great detail. However, I will suggest some guidelines:

  • Set the top-level value 'allowAllCommands' to "false". A "default deny" policy is the safest bet.
  • Make use of the block to group sets of commands for groups of users. It will help organize your configuration file.
  • For all values, note the 'argumentString' and 'md5sum' attributes. Use these to further restrict execution of commands to prevent crafty users from implementing workarounds to sudowin.
  • Test. Test. Test. Verify that your configuration works before rolling it out to your users. There's nothing more frustrating (to you _and_ the users) than dealing with something that doesn't work (especially when those users had elevated privileges before).

So far, I've had great luck with sudowin and the users I've deployed it to are able to do the work they always have done. Despite some minor annoyances such as a very short password cache time and some quirks with how some commands handle arguments (not sudowin's problem), this solution works well. I continue to test its application in other scenarios and am finding it to be very useful. If anyone has used sudowin and would like to provide feedback, feel free to do so (especially if you know how to increase that pesky password cache time period).

September 27, 2008

Using Perl and ADSI to Update Active Directory

Recently, the CEO of my company informed me that he's been really loving our VoIP-based phone system (Enterprise Interaction Center from Interactive Intelligence). In particular, he really digs the available company directory provided by the front-end application, Interaction Client. It allows users to click on a phone number to automatically dial out. It's extremely convenient and many users don't even touch their handsets anymore. On top of that, the built-in company directory integrates with Active Directory and can display many of the attributes available for user objects (i.e. department, title, address). Now that we've expanded into additional office space and have a significant body of telecommuting staff, it's become very important to be able to organize and locate information on all of those users. So the question came: "Can we include additional information in the company directory that displays each employee's department, title, and location?" Yes, my friend, yes.

Centralized Management

I'd like to pause for a moment to provide some background. We use Active Directory for domain accounts, printers, permission/ACL administration, group policy definitions, etc. It's a great tool in our IT belt. And since, at its core, it's LDAP, I've been lobbying for a while now to leverage its capabilities in other areas of our environment. I despise the duplication of effort (i.e. reinventing the wheel) so when we need to provide directory-related services (i.e. organizing contact information or authentication), I prefer to use Active Directory (or OpenLDAP, depending). I've provided proofs of concept, attempted to influence developers' designs, and used it whereever I possibly can. My argument has fallen on deaf ears, until now.

Updating Active Directory

The request was simple enough: we needed each employee's department, title, and some location information added to their respective accounts so that they would display in Interaction Client. And since there is already integration between the phone system and Active Directory (AD), I just needed to update a few attributes and wait for the two to sync. I decided to do so programmatically. Especially because we have 140 employees. Especially because I don't trust my typing skills across a large enough body of data.

All we need is an input file that contains the employees' names, and all the attributes that need to be updated. That's simple enough; HR provided everything I needed in a simple spreadsheet. I simply stripped out any needless headers and then exported the file as a pipe-delimited text file (I chose the '|' symbol as my delimiter because some names may contain commas and this would cause a big headache if I tried to export the data as a comma-separated file).

One last thing: we need access to the 'dsquery' command. This is a simple command that queries AD and returns information on the object specified. We'll need to grab the distinguished name for our user objects before we can update them. You can find the 'dsquery' command in the Windows Server Resource Kit.

Perl and ADSI

My language of choice is Perl. It's partly because that was one of the first languages I was introduced to and partly because of its utility; its cross-platform capabilities are crucial for me as I work in a heterogeneous environment. To interface with Active Directory, I decided to use the ADSI API (a COM derivative). I could have used Perl's LDAP support (Net::LDAP) for this project, but where possible, I always try to use native functionality. Perl supports COM interaction via its Win32::OLE module. For those that haven't used Win32::OLE, note that if you're handy with VBScript, it's usually a very easy matter to convert VBScript code to Perl using this module. In fact, the Microsoft Scripting Guys provide a handy utility that demonstrates how easy it is to write COM-related programs in various languages via their Scriptomatic tool. Download it and give it a go.

NOTE: Neither a discussion of Perl nor ADSI is within the scope of this post. For more information, see perl.org and msdn.microsoft.com.

The Code

The script is short and sweet. It will open our input file for reading, find the DN for each user object (the full name is created from the first and last names for each user), instantiate a COM object based on the DN, and finally, update the attributes we need (please forgive any ugly line wraps):

#!/usr/bin/perl

#
# adUpdate.pl - Update Active Directory using the ADSI API (via the LDAP provider)
#

use warnings;
use strict;
use Win32::OLE qw( in );

my $employeeAttributeFile = 'employeeAttributes.txt'; # our pipe-delimited input
open( ATTRIBUTES, $employeeAttributeFile ) or die "Unable to open \'$employeeAttributeFile\' for reading: $!\n";
while ( < ATTRIBUTES > ) {
   my ( $lastName, $firstName, $streetAddress, $department, $title ) = split(/\|/); # our input is pipe-delimited
   my $fullName = $firstName . ' ' . $lastName; # combine the first and last name for queries later
   print "name: " . $fullName . "\n"; # debug

   # call 'dsquery' to hunt for this user object in our directory and return the DN
   # 'dsquery' is available in the Windows 200x Resource Kit
   my $dsqueryCMD = "dsquery user -name \"$fullName\"";
   my $dn = qx( $dsqueryCMD ); # qx// allows us to shell out and grab the results/output (unlike 'system()')
   $dn =~ s/"//g; # strip the surrounding double-quotes from dsquery's output
   #print "DN: $dn\n"; # debug

   my $adsPath = "LDAP://$dn"; # ADSI path using the LDAP provider
   #print "\$objUser = Win32::OLE->GetObject( $adsPath );\n"; # debug
   my $objUser = Win32::OLE->GetObject( $adsPath );

   # if $objUser isn't set, we have a problem; report it and jump to the next record in our file
   if ( ! $objUser ) {
      print "ERROR: " . Win32::OLE->LastError() . "\n\n";
      next;
   } # end "if ( ! $objUser )..."

   # update the attributes we need and set them
   $objUser->Put( "streetAddress", $streetAddress );
   $objUser->Put( "department", $department );
   $objUser->Put( "title", $title );
   $objUser->SetInfo();

   print "ADDRESS: " . $objUser->{streetAddress} . "\n"; # debug
   print "DEPT: " . $objUser->{department} . "\n"; # debug
   print "TITLE: " . $objUser->{title} . "\n"; # debug

} # end "while ( < ATTRIBUTES > )..."
close ATTRIBUTES;

# fin!

At this point, I've successfully updated specific attributes for all users in AD without any manual data entry. I'm happy and more importantly, the CEO is too.

Disclaimer

This code is provided "as-is" and is simply meant to demonstrate a solution to a problem I needed to solve. As such it carries no warranties or guaranties regarding its use. You can use this code as you see fit; it's hereby in the public domain.

August 31, 2008

Password Management

Of all the tasks that a system administrator is charged with, I would have to say that managing passwords is by far the most banal, frustrating, and frightening. This is true whether one is in a corporate environment or even doing the best to illuminate non-technical family members on the merits of strong protection (perhaps an analogy to teen pregnancy might make them sit up and take notice!).

We all know, and agree, that we need to protect our assets and information from unauthorized access. Further, we need to ensure the confidentiality, integrity, and accessibility of our data. So passwords are better than nothing. But they're far from perfect. This fact is exacerbated by the myriad ways in which password policies are implemented and enforced. For sysadmins this is amplified by the fact that we are responsible for overseeing these policies for everyone and everything on top of remembering account credentials for every system and service we manage in the enterprise.

Simply imagine all the devices and services a sysadmin interacts with and logs onto on a daily basis. I personally have accounts for all the equipment I manage (i.e. Cisco routers, Netgear switches, Windows and Linux hosts) and the services I operate (i.e. email, instant messaging, databases). I've also got numerous personal accounts for banking, blogging, personal email, my credit cards, Facebook, and the fantasy football league. I'm swimming in dozens of credentials!

The intent of this post is not to wax technical about the technologies such as single sign-on or 2-factor authentication that aim to address and simplify this problem as it exists in limited, corporate environments; those are huge topics of their own that each deserve individual attention. In fact, I probably will write about them in future postings. Rather, the scope of this discussion is much broader in that it encompasses password management for several disparate systems, services, and my need to maintain various personal account credentials.

Users hate passwords. And sysadmins hate dealing with password-related issues (password resets, account lockouts, etc.). And no matter how much training you provide to your users, you will inevetiably field a call to assist a user with a password problem. There has been an ever-evolving arms race, if you will, between those that need to implement security and those that work to tear security down (or circumvent it). To wit, the battle has unfolded as such:
  1. Passwords were implemented.
  2. Users selected weak passwords subject to easy guessing or brute-force dictionary attacks.
  3. Password complexity rules were implemented to force users to select stronger passwords and prevent password reuse.
  4. Users, frustrated with those rules, invariably stored their passwords on Post-It notes strategically hidden around their cubes. Wary passers-by could easily collect that information, sometimes without having to look hard to find it.
And password expiration? No amount of time, short or long, will suffice. Users will get fatigued with short expiration periods. Long expiration periods just mean that users forget the password complexity rules and are more likely to get frustrated. This is a classic case of security getting in the way of usability.

So, without segueing into a discussion about 2-factor authentication (that _will_ be my next post!), how do we address this issue when it's magnified across mutiple accounts? I usually suggest to my users that they think up clever passphrases whose characters are slightly mangled. For example, the following passphrase:

I love Technical Panacea

could be turned into a mangled passphrase thusly:

ILov3T3chn!calP@nacea

That may work for a single password, but what about your other accounts? You don't want to use the same password for all accounts; that's a huge risk in and of itself. If someone cracks my Facebook page, no big deal. But if they're savvy enough to try that same password to log into my Gmail or banking account, and I've used the same password, I'm up the creek. Long passphrases like the above example are also prone to being written down to ease the user's pain too.

It's at this point that I normally suggest the use of a common prefix or suffix that one combines with something relevant to the system or service that can easily be remembered. A good example would be if I used a service by ACME Inc. I really like ACME, I think they're the best at what they do (supplying Wile E. Coyote with innumerable gadgets to capture that speedy roadrunner!). Maybe I decide on the following suffix:

IsTheBest!

Since it's ACME's website I'm logging onto (to order some anvils), I create an account with the following password:

acmeIsTheBest!

I can apply this formula to all my accounts now:


acmeIsThe Best!
warnerBrothersIsTheBest!
technicalPanaceaIsTheBest!


Every time I log in to a system or service, I only need to remember that special initial key (i.e. acme, warnerBrothers) and my suffix. I have unique passwords derived from a very simple algorithm. However, we still have an issue. For every system or service that I log onto, the username requirement is likely to be different. Some websites like you to supply an email address. This is convenient as you don't need to think up a clever name anymore ('wileecoyote' is often already taken!). Some services require something different such as a full name or a name limited to 8 characters. We're still in the same boat as before: multiple accounts with different requirements and too many to remember.

Now, I neither agree with, nor condone users that maintain their password information on paper around their desks. But some have been savvy enough to lock that information in their desk drawers. They at least have a physical layer of access to protect that sensitive information. For that, I applaud them. This very same idea has struck many others as well. Why not maintain a list of credentials in a secured fashion that only the user can access? Without a doubt, the credentials used the most will be remembered the easiest (kind of like cached data), so maybe this isn't necessary. But what about those credentials used less often? When's the last time you _had_ to log into your Cisco router? Those things just work!

I've researched a few solutions that may provide an answer to this problem. Now, anyone that knows me knows that I cannot stand duplicated effort. In almost all cases, there's no need to reinvent the wheel; many good products are available that will suit most, if not all, of your needs. I don't want to waste precious resources, like time, spinning my wheels when I could be more productive elsewhere. That being said, there are times when it is appropriate to roll your own solution (as we'll see below).

Before we proceed, let's define the scope of the problem so that we can easily apply a solution.
  1. I want a single, simple, secure place to store my account credentials.
  2. That information must be easily updated.
  3. I'll pay for a solution but would prefer something free.
I reviewed, to varying degrees, the following solutions:
  • Clipperz
  • Keepass
  • PasswordPlus
  • RoboForm
  • GPG-encrypted CSV File
Clipperz (http://www.clipperz.com)
Billing itself as a "web rolodex", Clipperz will store your account credentials, bank account information, addresses, and more online using what it calls a zero-knowledge application. This simply means that the folks at Clipperz know nothing about you or the information you're storing. They simply provide an interface that you use to access your information. The account you create with them is not linked to you in any way (not even via your email address). On the flip side, if you forget your account credentials for this service, don't bother shooting an email to the Clipperz admins; they cannot and will not help you. Remember, they know nothing about you or your account so they have no way to authenticate you.

Each collection of information is stored in what they call a card, keeping with the rolodex theme. After logging in to the service all of your cards are available to you in a simple, clean interface. You can import data from various sources (including most of those bulleted above) and exported for printing or JSON applications.

I really like Clipperz. You can store all sorts of information on top of your account credentials and it offers a very handy feature called 'direct logins'. With direct logins, you can copy the code around the login function for your favorite website (via a simple bookmarklet they provide), store that along with your username and password, and simply log in to that site with a single click! I've used this for some minor services I use (i.e. Facebook, Yahoo! Sports) but I wouldn't use it for anything I deem sensitive like banking information. Call me paraniod, call me old school, but I prefer to take a 'trust but verify' approach to some things, especially in the realm of security. It's fun and exciting to be on the bleeding edge of technology, testing new software and experiencing new services. But it's called "bleeding edge" for a reason. I won't store my important information online with Clipperz or a similar service. The risk outweighs the benefits at the moment.

Verdict: The direct login feature is slick and I like the idea of storing information in "cards" but Clipperz is more than I need and I don't fully trust the service.

Keepass (http://keepass.info)
Keepass is a clean, lightweight, open source solution for Windows systems with available ports for other platforms and a standalone binary that can be deployed on USB sticks. It's bascially a database encrypted with a master password, a key file, or both (referred to as a composite master key). The first time you launch KeePass you will be prompted to create a master password. This is the password that will be used to encrypt the database. I didn't create a key file so I don't have details on that process.

The interface provides 4 general password groups that allow you to organize your passwords into the most common categories (i.e. Windows, Network, Homebanking). You can also create additional groups if you see fit. Adding, modifying, and deleting entries is very easy. Plug-ins from third parties are also availabe that extend the program's usefulness including browser plug-ins that will fill web forms with standard information including account credentials. KeePass' author notes, however, that not every plug-in has been tested or verified for malware. Caveat emptor.

Being open source, KeePass' code is available for scrutiny. At the moment, I'm not inclined to do so other than to determine that it's written in C++. Other than that, I'm unable to comment on how strong the encryption may be or how well the code is written. I can tell you that the database file created by KeePass looks like a garbled hot mess in Vim. I can also determine the number of entries in the database since it appears to be a simple flat file. This by itself isn't interesting, but knowing the structure of the file may lend itself to further cracking. This is similar to the fact that certain letters in the English language are used more often than others; character frequency analysis is a simple tool often used to crack cryptographic codes.

Verdict: KeePass is a good program but it's an overly complex solution based on the fact that it's written in C++ and uses a database backend. In security, the simpler a thing is, the easier it is to manage and therefore less prone to cracking.

PasswordPlus, RoboForm
Both PasswordPlus (http://www.dataviz.com/products/passwordsplus/pwp_techspecs.html) and RoboForm (http://www.roboform.com) are proprietary solutions. They both cost about $30 for a license and offer cross-platform support. Another feature to note is that they provide support for mobile devices (see their respective websites for more information). I don't have any need to store my sensitive information on my mobile phone but this may be a requirement for others. Any road warriors out there reading this?

I haven't tried either of these programs but felt they deserved a mention. They provide many of the features described in the previous two offerings as well as additional support for mobile devices.

Verdict: I didn't want to pay for these programs as there are perfectly good free/open solutions available. Either I'm a spendthrift, or I'm cheap...

Scope Review
Remember when I stated that I wanted "... a single, simple, secure place to store my account credentials"? Now that I think back on it, and have reviewed a few solutions, I think that requirement needs to be amended:

I want a single, simple, secure place to store my account credentials that leverages open, proven standards.

Open, proven standards. This is a biggie, especially in the world of security. I'm one person; if I had evaluated KeePass and determined it to be safe, my judgement is constrained by my experience and knowledge of cryptography. Someone else may make a completely opposite determination. But what about openly scrutinized solutions that have received attention from the brightest collective minds in the world? If hundreds, maybe thousands of people from all walks of life and various professions have scrutinized and certified certain solutions (i.e. SSH, PGP) for use in securing information, then it follows that I can be reasonably assured that those same solutions are viable options for me. This led me to my final solution, the one I have actually implemented in the field.

GPG Encryption
I'm a simple guy, ask my wife. I like cold craft brews (like my own) on a hot summer day, and a good scotch, neat, in the evening. So it is with my choice of password management: a gpg-encrypted CSV file containing all of my sensitive information. I've generated a very strong key pair and locked it up with a very strong (and exceptionally hard to guess) passphrase. I implement additional security by physically separating that data onto a USB stick that I carry with me. I only edit the file from the USB stick and never make copies to any other media. It's important to understand the behavior of your editor of choice. I prefer a WYSIWYG editor like Notepad or Vim because they're, you guessed it, simple. More importantly, they don't save temporary copies of your data in some far off (i.e. not local to the USB drive) directory and forget to clean them up, like Microsoft Word. When I'm finished modifying the file, I encrypt it. Before deleting the decrypted version, I run a shredding program against it a few times, to be safe.

My encryption program of choice is GnuPG (gpg) and I use a standalone version run from my USB stick. gpg is well proven and uses open encryption standards; it's everything I need. Even though my private key is stored on the USB stick, the passphrase required to unlock it is horrendously hard to guess and is therefore well protected.

The fields in my CSV file are based on my requirements. I need to know what the account is for, the username, password, and potentially add some notes. I also like to track the last time the password was updated so that I can easily determine if I've been using a given password for too long.

Verdict: A gpg-encrypted CSV file fits all my requirements. It's a single source for my information; it's simple, secure, and uses open standards for encryption. I can easily update the data using a WYSIWYG editor such as Notepad or Vim. And best of all, it's free.

Final Thoughts
While my solution is simple, secure, and free, it's important to recognize that it is not 100% free of issues. In fact, no security measure is ever a complete solution, hence the need for defense in depth. Of particular note is that I would never insert my USB stick into a public or non-trusted terminal to access my sensitive information. For all I know, a keylogger could be in place that would capture my private gpg key's passphrase. Even worse, with my sensitive data open in an editor, it's available in memory. A savvy cracker could cull that information and then have all the keys to my castle.

What I've selected as a best fit for me may not work for you. Remember that before making any decision, for security purposes or anything else critical, it's crucial to define your needs or project parameters and strive to implement a solution that fits most (hopefully all) of those needs.

Ultimately, there will be a few credential sets that I will decide not to store and will always need to remember. Call me paranoid, call me old school. But you'll never be able to call me a hypocrite: the guy that tried to enforce good security in the form of password management but didn't implement it himself.

August 24, 2008

Promotion

It's official: I'm now the Technical Services Director! I've moved up from my station as Senior System Engineer and am now fully responsible for the management, oversight, and direction of the data center and all systems and services consumed by our employees and clients. I've been acting in this role for some time; now it's on paper and recognized. Hard work really does pay off.

So, despite the fact that today's post isn't specifically a technical one, I thought it important to shed some light on the softer points that folks in our field need to consider in the event they wish to elevate themselves, either on the most auspicious of corporate ladders or in any capacity they see fit (pay attention future entrepreneurs!). The notions below are my own, borne of experiences growing up and in the field. This is in no way a comprehensive list, nor is it ordered in any preference, but I don't think one can succeed without a fair mix of all of them.

Hard Work
Everything your parents, grandparents, and cranky neighbor told you is true: luck, good looks, or brains are no substitute for hard work. When others see your blood, sweat, and tears, they see character. You also get to revel in the results of your labor.

Patience
Without a doubt, patience is a virtue. And not one endowed on everyone. Regardless, developing patience can be of benefit in all situations be it listening to an impassioned (read rude) client, seeing a project through its bumps and scope creep, or driving towards that moment the CEO confirms your new position!

Seize Opportunity
That being said (regarding patience), one cannot always sit around waiting patiently; grab opportunity when it comes around and make the most of it. Per Louis Pateur, chance favors the prepared mind (See Ma? It's not just icky mold!). Judge for yourself.

Reflection
Try to understand people, their needs, and their desires. It's not easy, but it starts with reflection. If you know yourself and what you strive for, it helps to clarify what others are looking for and determines how you treat them. Remember, leaders work with people, and deal with problems, not the other way around.

Structure and Bureaucracy
This isn't so much a personal point as a mention of the inescapable fact that one will always deal with preexisting structure and bureaucracy. Work your channels before attempting to subvert them. This is similar to Mom's dinnertime admonishment, "How do you know you don't like them unless you try them?" I now love peas, by the way. The only way to make change is to try a system and define its weaknesses. Sometimes, just sometimes, a particular system will work. And using it will make it stronger. When you do subvert a system, do it for the right reasons.

Translation
I love languages as much for their utility as their ability to illuminate our commonality and way of thinking, if only expressed in different ways. Not everyone understands IT like you do. And they certainly aren't hip to all the jargon that you rattle off with your buddies in chat sessions or in blogs. Folks know they have a job to get done and would like to use technology to make their lives easier. The onus is on you to understand what they need (see 'Reflection') and translate that into tech. Define the problem and find the solution that fits. Remember, you know the difference between 2.7GHz quad-core processor and a flux capacitor, but you may know squat about large cap investment portfolios or kitty litter widgets. You're part of a team where everyone participates and works together.

Make Technology Work for the End-user
I'll lay down the gauntlet here. The days of the notion of "lusers" or employees with that 'ID-10-T' error are over. Folks are savvier than you think but still rely on you for their technology needs. Make it work for them, don't give them a laundry list of hoops they need to jump through just to sign on to the corporate webmail site. Remember, software is malleable (it is soft) and can be updated to fit users' needs.

Strive to Improve
It goes without saying that technology is always moving and improving. So should you. Stay on top of your game. Take the 30 minutes to read Slashdot (or the occasional blog, ahem!) to glean any useful information on what's out there. Don't hesitate to look at other fields as well. Great strides in our history have been achieved by folks whose varied interests lent valuable insight into seemingly unrelated areas of study and research. In fact, the various 3-letter agencies in the U.S have come around to the notion that it helps to create a team of multiple talents to tackle tough problems and they actively seek out diversified candidates. So, get a hobby. I brew beer and grill with peppers (Bobby Flay ain't got nuthin' on me!).

Finally, a skill that can help assuage the constant flood of problems and fires to fight:

Time Management
I don't know if this is something one ever masters. I've learned a few tips and tricks from folks and developed my own. Everyone prioritizes work differently. I'm not David Allen so I won't espouse any given system, "proven" or otherwise; you've got to find what works for you and stick to it. Whatever you do, be sure not to forget to make your life a priority (remember the hobby!). You'll be more effective and more productive in the long run.

Most importantly, realize that these are just my thoughts based on my accrued knowledge, experience, and late nights at the bar with buddies (aka my support group). As you grow, your focus will invariably shift and what's important or relevant today will assuredly not be so tomorrow. Find your own path and don't let anyone or anything stop you from reaching your goals.

And eat your peas.

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!

August 7, 2008

MIT Kerberos Dialogue. Just because...

At the moment, I'm trying to work out some kinks integrating Twiki with Active Directory (I can't stand superfluous user accounts, especially when there are so many services available). I thought that a Kerberos refresher was in order and I came across a great page provided by MIT's Theodore Ts'o that I hadn't read in a long time. I'm linking it here as it is defintely a useful bit of information for those new to Kerberos (and those that have forgotten some of the nuances!).

Check out Designing an Authentication System:a Dialogue in Four Scenes for a good read.

And once I've got Twiki working with AD, I'll post that information as well.

First Post

I manage a heterogeneous data center whose operations span two offices. I support 150 users (with one staff member) including ~50 telecommuters from around the country. Granted, this isn't MegaCorp (TM) but it can be interesting. Very.

And since I've been working in IT for 10+ years, I decided it was time to share my accumulated knowledge and experience to make newcomers' lives just a little easier. So I built a wiki at the office. And I figured, "Hey! Why not start blogging too?"

So here I am. This blog is an attempt to disseminate those things that I know or how come across that were not won easily. Perhaps the name I've chosen for my blog is a bit of a stretch; after all, nothing can be touted as a cure-all. But if the information on these posts makes a difference in the life of even one poor sysadmin, newbie programmer, or support desk schlep, then it's been worth it.

I don't expect that my subsequent posts will have any direction or structure to them, so you shouldn't either. To start, I'm simply going to perform a brain dump of the most recent bits o' info at the top of the stack, if you will.

Enjoy.