Last week we got a TeamCity Enterprise license at work. After using this great product for about a year we found ourselves running out of available build configurations. (There are 20 in the fully-functional free Professional edition which should be enough to evaluate the product. I recommend giving it a try.) There are a couple of advanced features in the TeamCity Enterprise edition we were looking forward to, for example authentication against a LDAP directory, an Active Directory in our case (AD = LDAP + DNS + a bunch of other stuff).
TeamCity uses LDAP to determine if a user should be able to access the TeamCity web interface. It does that through the LDAP bind operation, asking LDAP to validate the username and password combination entered at the login page.
After hitting the login button TeamCity will connect to the LDAP server, basically taking the text entered in the dialog above passing it to the LDAP bind operation. If the server accepts the username/password combination this means that access is granted. Some things to take into consideration when using LDAP authentication are:
- TeamCity does not authenticate against an organizational unit in Active Directory (X.500 address). It just determines if the user (authenticated by username and password) exists anywhere in the directory. You can vote on this ticket to get that fixed.
- Because TeamCity does not try to get additional information on the user’s groups memberships it is currently (as of TeamCity 4.0) not possible to automatically assign TeamCity roles to an LDAP user.
- If you use the default LDAP configuration settings as shown in the TeamCity documentation, the LDAP connection will be unsecured, making the username and password vulnerable to eavesdropping by anyone who knows how to use packet sniffer.
- Specific to Windows: You do not need an Active Directory infrastructure with a Domain Controller in place. Windows also supports Active Directory Application Mode (ADAM) on Windows Server 2003, renamed to Active Directory Lightweight Directory Services (AD LDS) in Windows Server 2008.
Given the things above, what are your options to secure the LDAP connection? You could change the authentication scheme to not use "simple” LDAP authentication, but choose from a variety of SASL options. I didn’t go down that road, because when I started to configure LDAP for TeamCity I basically knew nothing about neither LDAP nor SASL.
Using LDAPS (LDAP over SSL), which is also supported by Windows servers running some AD mode, appeared to be a viable option to enforce secure communication between TeamCity and the LDAP server.
Installing The LDAP Server
Setting Up LDAPS with Active Directory (Domain Controller mode)
There’s not much set up needed with this configuration. When you install Active Directory in Domain Controller mode you should also get an instance of Certificate Services that will create a self-signed certificate for your domain controller. This certificate will be used for LDAPS connections to the directory server, which is typically the domain controller.
As an aside, I’m not expert in setting up AD, please refer to your network administrator.
Setting Up LDAPS with Active Directory Application Mode (ADAM) or Active Directory Lightweight Directory Services (AD LDS)
As noted above, this setup is supported on any Windows Server and does not require the full-blown “Domain Controller” version of Active Directory. ADAM/LDS supports user authentication either against the ADAM/LDS instance (users created in the directory) or a against local Windows accounts (through a user proxy, see below)
Installing ADAM or AD LDS
Installing ADAM/LDS differs depending on which Windows Server version you have. I did it with Windows Server 2003:
- Navigate to the Control Panel and open up the Software control panel applet, appwiz.cpl
- Click “Add or remove Windows features”
- Select Active Directory Services, click on the Details… button and select Active Directory Application Mode. Close the window.
- Scroll down to Certificate Services entry and check it. (IIS will also be installed as part of Certificate Services to support web registrations.)
- Click Next.
- On the next dialog, you will be asked what type of Root Certificate Authority (CA) to install. Select “stand-alone“ CA and also check the “Create key pair” option.
- The next dialogs allows to select different options for the Root CA keys and the CA itself. I went with the defaults.
- Certificate Services and ADAM will be installed.
- Under Programs in the Start Menu there will be a new folder named “ADAM”. Click on the “Create ADAM instance” link.
- The ADAM wizard pops up, click Next.
- Choose “Create new unique instance” and click Next.
- Enter the name of the ADAM instance. I chose “TeamCity”, because we’re using ADAM to authenticate TeamCity users. Click Next.
- Write down the ports that are presented in the next step. The default LDAP port is 389, and the port for LDAPS is 636. Click Next.
- In the next step, choose to create a directory partition. Mine is called
CN=TeamCity, DC=test, DC=local. Click Next until you reach the “Import LDIF files” dialog.
- Import at least the MS-User.ldf and MS-UserProxy.ldf schemas to enable the creation of local directory users and user proxies for Windows accounts.
- Click Next and wait for ADAM to be configured.
Setting Up ADAM or AD LDS to Accept SSL Connections
There are two good tutorials that I used to enable SSL on ADAM, so I won’t reiterate them here. I suppose the process of enabling SSL on LDS is similar.
You now have a LDAP server running that will serve requests for the LDAP and LDAPS protocols. Next, you would have to add users to the directory, which could either be
- Local directory users: user and password stored in the directory; used with “simple” bindings, or
- Windows users: users password stored by the local Windows account manager or in a full-blown AD domain; used with “proxied” bindings (from the outside, these also appear as “simple” bindings).
Windows users require a user proxy in the directory, linking the proxy to a Windows account SID. The link between the proxy and the Windows account is established though the Windows account’s Security Identifier (SID) which must be supplied when the proxy is created. Setting up user proxies is a bit complicated and well worth another post.
Please note that by default authenticating users through their respective proxies (proxied binding) requires a secure connection, unless you explicitly disable it. Unfortunately the attribute to change is not given in the linked article: it is
msDS-Other-Settings. You can either require security for simple or proxied bindings by setting
RequireSecureProxyBind (defaults to
RequireSecureSimpleBind (defaults to
0) to either
The net result of the default ADAM configuration (
RequireSecureProxyBind=1) together with the default TeamCity configuration (
ldap://some-server, which is unsecured) is that authentication requests for user proxies will always fail.
Setting Up TeamCity
Setting Up TeamCity to Use The LDAP Server
The easiest way is to start with the default TeamCity configuration in <TeamCity data directory>/config/ldap-config.properties:
Unless you want to require your users to enter their login in the
DOMAIN\username format I recommend adding the loginFilter property:
Now we need to set up the correct "user name" string to present it to the LDAP server. This string is created from the text entered in the "Username" text box on the login screen (
$login$) and differs depending on whether you use LDAP with AD or ADAM/LDS:
# AD - authenticate against the TEST domain
# ADAM and presumably AD LDS - users will have to reside in the CN=Users,CN=TeamCity,DC=test,DC=local container
Setting Up LDAPS Security
Enabling LDAPS is pretty easy from a TeamCity perspective. You just have to change line 2 of the configuration above to use the secure LDAP protocol:
Changing the protocol to use ldaps:// will not instantly work and users would not be authenticated. Why?
Trusting The Certificate
What does LDAPS mean from a Java perspective? If you work on a domain (AD) or use ADAM/LDS with SSL you are very likely to work with self-signed SSL certificates. Such certificates are inherently untrusted as they are not issued by some trusted party (and this trusted party will charge money). Nevertheless they are perfectly okay for your environment.
When TeamCity establishes the SSL connection to your LDAP server, it is first presented with that untrusted certificate – and bails. Here’s a snippet from the TeamCity log files:
[2009-01-27 16:14:39,864] ERROR - Side.impl.auth.LDAPLoginModule -
javax.naming.CommunicationException: simple bind failed: ldap.test.local:636
[Root exception is javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]
To establish LDAPS connections successfully, you have to tell Java to trust your LDAP server’s certificate. Andreas Sterbenz has created a little Java utility called InstallCert that helps in that regard. Unfortunately you will have to compile it yourself against the Java 1.5 runtime, so here’s a compiled version that works with TeamCity.
Place the files from the ZIP in your <TeamCity root>\jre\bin directory. Open a command prompt and enter
java InstallCert ldap.test.local:636
Following the procedure described in Andreas' post, the utility will create a file called jssecacerts in the same directory. Overwrite <TeamCity root>\jre\lib\security\cacerts with that file.
After re-starting the TeamCity web server, it is now able to establish secured connections to the LDAP server. The user names and passwords transmitted over these connections will not be visible to outsiders.
Wrapping It Up
In this article I’ve shown you how to enable and secure TeamCity’s LDAP authentication in any Windows environment, be it an Active Directory domain or a couple of stand-alone Windows Servers. For both scenarios user management is centralized, either though the AD console or LDAP console in combination with the Windows user management console.
Figuring out all that has taken a considerable amount of time for me and hopefully saves you a couple of minutes that you can spend outside in the sun.