The holy grail: Single Signon RT with Windows Server, Active Directory and Apache 1.3 on unix
A lot of people use RT to track helpdesk requests, problem reports and other incident data at their jobs. An even larger number of people use or are forced to use Microsoft Active Directory as the central repository of username and password information at their jobs. As a result, probably the single most-asked question on the rt-users mailing list is "how do I unify logins between RT and ActiveDirectory?" Following close on behind that is "how do I get RT to use Windows authentication so people don't have to type in their password twice?" Strangely, these are questions that seemed to lack any authoritative answers.
Until now.
The tools
- RT 3.4.4 from http://bestpractical.com/
- Apache 1.3.3x from http://httpd.apache.org/
- mod_ntlm 0.4 from http://modntlm.sf.net/ (There is an updated version available here that may be more reliable.)
- User_Local.pm and MailFrom_Local.pm from http://www.usit.uio.no/it/rt/modifications/
- Web_Local.pm from http://blank.org/memory/work/Web_Local.pm
- Microsoft Windows 2000 server or Windows Server 2003
The steps
- Build and install a stock RT 3.4.4 and Apache 1.3.33
- Build and install mod_ntlm
- Create a Windows user called "LDAP User" for the RT ldap hooks to bind as. I recommend creating a "Role Accounts" security group and putting "LDAP User" into the "Role Accounts" group and removing it from the "Domain Users" group.
- Create at least one RT user with the same name as an ADS user and give that user superuser privs; otherwise, you'll keep having to switch ldap off on and off again. Alternatively, create a Windows user called "root".
- At a minimum, give the "Everyone" RT group the ability to create tickets. Additional ACLs as appropriate for your site's needs and policies.
- Configure RT for external web authentication. In
RT_SiteConfig.pm:
Set($WebExternalAuth , '1'); Set($WebFallbackToInternalAuth , '1'); Set($WebExternalGecos , undef); Set($WebExternalAuto , '1');
- Configure the apache virtual host to use mod_ntlm for
authentication:
<VirtualHost *> ServerName rt.example.com DocumentRoot /opt/rt3 AddHandler fastcgi-script fcgi Alias /NoAuth/Images/ /opt/rt3/share/html/NoAuth/images/ ScriptAlias / /opt/rt3/bin/mason_handler.fcgi/ <Directory "/opt/rt3"> AddDefaultCharset UTF-8 SetHandler fastcgi-script AuthName "Request Tracker" AuthType NTLM NTLMAuth on NTLMAuthoritative on NTLMDomain DOMAINNAME NTLMServer pdc NTLMBackup bdc require valid-user </Directory> <Location /REST/1.0/NoAuth> satisfy any allow from all </Location> <Location /NoAuth> satisfy any allow from all </Location> <Location /NoAuth/images> SetHandler default-handler </Location> </VirtualHost>
- Install User_Local.pm from http://www.usit.uio.no/it/rt/modifications/ in /opt/rt3/lib/RT/.
- Edit /opt/rt3/lib/RT/User_Local.pm to fix a small logic
error in UIO's code. At line 374, get rid of the extraneous "defined"
test and the erroneous "!=" test, so that the line reads:
if ($RT::LdapUser) {
- Install MailFrom_Local.pm from http://www.usit.uio.no/it/rt/modifications/ in /opt/rt3/lib/RT/Interface/Email/Auth.
- Add the local RT configuration for LDAP. In
RT_SiteConfig.pm:
Set($LDAPExternalAuth, '1'); # Enable LDAP auth Set($LdapServer, "ldapserver.example.com"); Set($LdapCAFile, undef); Set($LdapUser, 'cn=LDAP User,CN=Users,dc=example,dc=com'); Set($LdapPass, 'password'); Set($LdapAuthStartTLS, '1'); # Need to use TLS or ldaps to check passwords Set($LdapAuthBase, "cn=Users,dc=example,dc=com"); Set($LdapAuthUidAttr, 'sAMAccountName'); Set($LdapAuthFilter, '(objectClass=user)'); Set($LdapMailBase, 'cn=Users,dc=example,dc=com'); Set($LdapMailFilter, '(objectClass=user)'); Set($LdapMailScope, 'sub'); Set($LdapMailSearchAttr, 'mail'); %RT::LdapMailResultMap = ( 'sAMAccountName' => 'Name', 'mail' => 'EmailAddress', 'cn' => 'RealName', );
- Install Web_Local.pm from http://blank.org/memory/work/Web_Local.pm in /opt/rt3/lib/RT/Interface.
- Restart apache
- Add the URL of your RT apache vhost to either the "trusted sites"
or "intranet sites" zone of Internet Explorer. You can do this on a
site-wide basis via the Group Policy Object for your domain
controller:
- Run "MMC" from Start->Run
- From the "Console" menu, select "Add/Remove Snap-in"
- From the "Add/Remove Snap-in" dialog, hit the "Add" button.
- Select "Group Policy" and hit "Add". This will bring up the "Select Group Policy" wizard.
- Hit the "Browse..." button, then select the Default Domain Policy for your domain and hit OK.
- Hit the "Finish" button in the Select Group Policy wizard, then the "Close" button on the "Add Standalone Snap-in" dialog, then the "OK" button of the "Add/Remove Snap-in" dialog.
- From the Default Domain Policy root, browse down to: User Configuration -> Windows Settings -> Internet Explorer Maintenence -> Security, and double-click on the "Security Zones and Content Ratings" object.
- From the "Security Zones and Content Settings" tab, select the "Import the current security zones and privacy settings" radio button and click the "Modify Settings" button.
- This will bring up the GPO version of the "Internet
Properties" control panel. Select either the
"Local Intranet" or "Trusted Sites" zone as
appropriate for your organization and hit the "Sites"
button in order to add your RT vhost to that zone.
If your RT vhost is not SSL-secured, you will need
to un-check the "Require server verification"
checkbox before adding the site.
- Hit "OK" to close the "Internet Properties" panel, and then "OK" to close the "Security Zones and Content Ratings" panel. You can then close the entire MMC console.
- Your end-users may need to reboot for the Group Policy to be applied to them.
What the hell did I just do?
You've just made two major changes to the way RT operates:
- Access to the web interface of RT is now authenticated by apache, using mod_ntlm to verify passwords against Active Directory. If the account does not exist in RT, it is automatically created as an unprivileged user and therefore is directed to the SelfService interface. Because Internet Explorer sends NTLM authentication tokens to any site in the "Trusted" or "Intranet" zones, users do not even have to type their password: successfully logging into the windows domain is sufficient.
- Incoming email to rt-mailgate is checked against the LDAP server to see if the address is known there. If it is, the mail is treated as if it came from the Windows account that has that email address. If the account does not exist in RT, it is automatically created as an unprivileged user.
The upshot of all of this is that other than the creation of the initial superuser account, all RT accounts are created on-the-fly the first time a user interacts with RT, and RT usernames and passwords will always be the same as Windows usernames and passwords.
Notes
- If you are not starting from scratch, but are trying to integrate
a pre-existing RT installation with an Active Directory server, you are
going to have to decide what to do about the pre-existing
auto-created internal non-privileged accounts, if any.
The problem is that in a typical RT installation, you are going to
have a lot of non-privileged accounts that look like the
following:
Once you've activated ADS SSO as above, however, the first time that user100 logs in to the selfservice interface or sends an email to rt-mailgate, RT will attempt to autocreate the following user:Name: user100@mycompany.com EmailAddress: user100@mycompany.com RealName: "(name taken from email From line)" Comment: Auto-created as watcher on ticket submission
...but will fail, because you can't have two users with the same EmailAddress. You'll need to manually change the Name attribute of this account from "user100@mycompany.com" to "user100"... and the same for all of the other auto-created users in your domain. I'm working on a script to do this semi-automatically, and will post it here when/if it's ever finished. Alternatively, RT::Extension::MergeUsers may be of help.Name: user100 EmailAddress: user100@mycompany.com RealName: "(name taken from LDAP directory)"
- There's probably no reason that you couldn't do this with apache2 and mod_ntlm2. If you try it that way and are successful, please let me know. UPDATE: Ian Goodacre cracked the problem. You can read his instructions here.
- The NTLM protocol has some well-known security vulnerabilities, especially in Windows 2000 Server's default configuration. At a bare minimum, you want to follow the procedure documented in Microsoft Support Document 299656 to turn off LanMan password hashes.
- mod_ntlm is effectively abandonware, and is missing some very obvious features like group restrictions. mod_ntlm_winbind looks promising, but is not quite ready for primetime yet (using it disables BASIC auth across the entire apache server).
- The obvious solution for the problems with both NTLM auth in general and mod_ntlm specifically would be to use kerberos/gssapi authentication, but I have yet to manage to get this to work reliably: If anyone has managed to make GSSAPI authentication work with windows clients (ideally IE6 and Firefox), please let me know how you did it.
- If you are using mod_ssl to secure your RT web interface, you
must comment out the following section of httpd.conf that is
installed by default with mod_ssl:
If you do not, mod_ntlm will never successfully authenticate users accessing the server.#SetEnvIf User-Agent ".*MSIE.*" \ # nokeepalive ssl-unclean-shutdown \ # downgrade-1.0 force-response-1.0
- The Firefox browser does not by default send NTLM auth tokens to any site. If you want Firefox to login transparently, you must add the full URL to your RT instance to the "network.automatic-ntlm-auth.trusted-uris" option in "about:config"
- In case it's not immediately obvious: under this system, while users without ADS accounts can submit tickets via the mail interface, they cannot, ever, use the web interface, even via SelfService, because web authentication is being enforced at the Apache level, not the RT level. If this is a problem, the solution would be to turn off WebExternalAuth and let the UIO User_Local.pm overlay handle authentication, but you'd need to add something like the AutoCreateFromExternalUserInfo extension to auto-create users and do the right thing with non-AD accounts.
- This whole thing keys off the "sAMAccountname" LDAP attribute in Active Directory. But sAMAccountName is just a friendly human-readable field in AD, and is not the primary key (that's your objectSID). If the AD administrator changes a user's account name, RT has no way of knowing that this has happened, and will create a new RT user the next time that user logs in or sends email. Absent an RT-side mapping of objectSID to sAMAccountName, there's no getting around this. I expect that at most sites, it won't be an issue, but you never know...
- The UIO MailFrom_Local.pm matches the "From" address of an inbound email against the "mail" LDAP attribute in ADS to come up with the username. But of course, in an ADS+Exchange environment, that might not be the only "From" address available to the user: if a user sends mail with one of their non-primary address, RT will have no way of tracking the message back to the original user. Again, probably not an issue 99% of the time, but keep it in mind...
- The reason for using both the UIO LDAP overlay extensions and RT's WebExternalAuto is to minimize administrative headaches: a valid ActiveDirectory user will get an account created for them regardless of whether their first contact with RT is via email or the SelfService interface, and any user logged into the Windows domain will not have to re-authenticate with RT. Users who are not in the AD domain will still have accounts created the old-fashioned way via the mail gateway.
- The UIO User_Local.pm redefines methods that the RT installer uses! You will definitely not be able to do a "make initialize-databases" from the RT source tree while User_Local.pm is in place, and you may not be able to do a "make upgrade" either. Be sure to move User_Local.pm out of the way before attempting either.
- The default configuration example above uses SSL to communicate with the LDAP server. This will not work unless you have activated SSL on the Active Directory server. Instructions for doing this on Windows 2000 domain controllers are in Microsoft support article 247078. On Windows 2003 Server, the procedure in article 321051 appears to apply.