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:
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.
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.
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/LDS differs depending on which Windows Server version you have. I did it with Windows Server 2003:
CN=TeamCity, DC=test, DC=local
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
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 1) and RequireSecureSimpleBind (defaults to 0) to either 0 or 1.
msDS-Other-Settings
RequireSecureProxyBind
1
RequireSecureSimpleBind
0
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.
RequireSecureProxyBind=1
ldap://some-server
The easiest way is to start with the default TeamCity configuration in <TeamCity data directory>/config/ldap-config.properties:
java.naming.referral=follow java.naming.provider.url=ldap://ldap.test.local:389 java.naming.security.authentication=simple
Unless you want to require your users to enter their login in the DOMAIN\username format I recommend adding the loginFilter property:
DOMAIN\username
java.naming.referral=follow java.naming.provider.url=ldap://ldap.test.local:389 java.naming.security.authentication=simple loginFilter=.+
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:
$login$
java.naming.referral=follow java.naming.provider.url=ldap://ldap.test.local:389 java.naming.security.authentication=simple loginFilter=.+ # AD - authenticate against the TEST domain formatDN=TEST\\$login$ # ADAM and presumably AD LDS - users will have to reside in the CN=Users,CN=TeamCity,DC=test,DC=local container formatDN=CN=$login$,CN=Users,CN=TeamCity,DC=test,DC=local
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:
java.naming.referral=follow java.naming.provider.url=ldaps://ldap.test.local:636 java.naming.security.authentication=simple loginFilter=.+ formatDN=<some value>
Changing the protocol to use ldaps:// will not instantly work and users would not be authenticated. Why?
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.
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.
a@href@title, blockquote@cite, em, strike, strong, sub, sup, u