Configuring TeamCity to use HTTPS/SSL on Windows with a PFX file, and redirecting HTTP to HTTPS

Tonight I'm having fun setting up a new build server with TeamCity. Since my build server is accessible over the internet, I wanted to use SSL. I also wanted requests using HTTP to be redirected to HTTPS.

I ran into a few problems because I was using a wildcard certificate that had been issued from a root CA, while most of the guides focus on using self-signed certificates, but I eventually got it working. Here are my notes in case they help you, or in case I need them later.

My configuration

I'm using TeamCity 7.1.5. I installed TeamCity into a non-standard location, T:\TeamCity, for reasons I won't go into in this post, so of course the paths I'm using below will be different to yours.

Exporting the certificate

The certificate I'm using is a wildcard certificate that had been issued months ago and installed into the Windows certificate store on a web server. My first step was to export the certificate:

Exporting the certificate

When prompted, the private key needs to be included. The file will be saved as a PFX file:

Export options for the PFX file

I secured the PFX file with a password, which I wrote down. Then I copied the PFX file to the server.

Converting the PFX to a keystore

TeamCity relies on Tomcat, so PFX files aren't much use. The next step is to convert the PFX file into a Java Key Store file.

Since these are Java Key Store files, we'll of course be needing a Java tool to do the conversion. But fear not, conveniently there's a Java runtime bundled inside of TeamCity. In my case it's at T:\TeamCity\jre\bin.

The command to do the conversion was:

T:\TeamCity\jre\bin\keytool.exe  \ 
  -importkeystore                \
  -srckeystore T:\TeamCity\conf\ssl\OctopusHQ.com.pfx \
  -srcstoretype pkcs12 \
  -destkeystore  T:\TeamCity\conf\.keystore2

During this conversion you'll be prompted to enter and confirm a new password for the keystore file that you are creating. You'll also be prompted to enter the password for the PFX file when it was exported.

While you may think you can enter two different passwords here, don't be fooled; make sure you enter the same password for both the new key store and the imported PFX file. Otherwise you'll get an error message when Tomcat tries to load the private key:

java.security.UnrecoverableKeyException: Cannot recover key

You can verify the contents of the keystore file using:

T:\TeamCity\jre\bin\keytool.exe -list -keystore T:\TeamCity\conf\.keystore2

Changing the connectors

Next you need to tell the TeamCity bundled Tomcat server to use SSL. Open T:\TeamCity\conf\server.xml and look for the <Connector> elements. I replaced the existing connectors with:

<Connector port="80" protocol="HTTP/1.1" 
    redirectPort="443"
    />

<Connector port="443" protocol="HTTP/1.1"
    SSLEnabled="true"
    scheme="https" secure="true"
    connectionTimeout="60000"
    redirectPort="8543"
    clientAuth="false"
    sslProtocol="TLS" 
    useBodyEncodingForURI="true"
    keystoreFile="T:\TeamCity\conf\.keystore2" keystorePass="<password used for the PFX and keystore>"
    />

The second connector is my HTTPS listener, the first connector just forwards connections to it. At this stage after restarting TeamCity I was able to browse to either http://myserver or https://myserver, but the HTTP endpoint still served the contents over HTTP rather than redirecting to HTTPS.

UPDATE: The example above was for TeamCity 7. For TeamCity 10, use the following connector (thanks to Matt Richardson):

<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
    SSLEnabled="true"
    scheme="https"
    secure="true"
    connectionTimeout="60000"
    redirectPort="8543"
    clientAuth="false"
    sslProtocol="TLS"
    useBodyEncodingForURI="true"
    keystoreFile="T:\TeamCity\conf\.keystore2"
    keystorePass="<password used for the PFX and keystore>"
    socket.txBufSize="64000"
    socket.rxBufSize="64000"
    tcpNoDelay="1"
    /> 

According to Matt, that now handles the WebSocket communication and prevents the "using legacy polling" warning showing up.

Requiring SSL

The final step was to edit T:\TeamCity\conf\web.xml. At the bottom of this file, just before the closing </web-app> element, I added a constraint to force HTTPS:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>HTTPSOnly</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

Diagnostics

If you have problems with your SSL configuration, the best place to look is:

T:\TeamCity\logs\catalina.*.log

Also, don't forget to enable port 443 in Windows firewall!

This took me a few hours to get working, so hopefully my notes can save you some time. The following resources were helpful to me:

A picture of me

Welcome, my name is Paul Stovell. I live in Brisbane and work on Octopus Deploy, an automated deployment tool for .NET applications.

Prior to founding Octopus Deploy, I worked for an investment bank in London building WPF applications, and before that I worked for Readify, an Australian .NET consulting firm. I also worked on a number of open source projects and was an active user group presenter. I was a Microsoft MVP for WPF from 2006 to 2013.