SSL certificate authentication

Jody Sevidal January 31, 2012

This may have been posted in the past but I'm tyring to use our client certificate authentication or PKI environment with JIRA.

Is anyone aware of a plugin that does this already or through other libraries like Seraph?

The intent is to use the client certificate credentials as a proxy for JIRA authentication. We already have the cert authentication in place, i.e. user navigates to url and prompts for password and then gets access to the url. But it would be nice to have an intermediary logic to transparently send this to JIRA for seamless authentication.

Has anyone done this already. Greatly appreciate any suggestions. Thanks in advance.

3 answers

1 accepted

2 votes
Answer accepted
JamieA
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 31, 2012

We used to do it exactly the same way as Nic describes, a custom authenticator that gets the certificate subject, and looks up the details in LDAP.

A few months ago I changed it so that Apache does the authentication... there is nothing funky here, two-way SSL in Apache is well-documented. We use ajp to proxy to jira, so all jira now does is request.getRemoteUser, and logs them in to jira with that. I removed all the custom code that I had written to automatically create accounts, as all that's in jira since 4.3.

The advantage of the apache way is that jira, confluence, and fisheye have hardly any custom code at all and it's pretty much the same between all of them, all the heavy lifting is done by apache.

The key apache config is: SSLUserName SSL_CLIENT_S_DN_CN

Actually there is a bit more to it, I had to write an apache fixup hook because the certificate CN did not correspond with jira login IDs, but hopefully you would not have to do that.

Jody Sevidal January 31, 2012

Jamie - thanks. I believe this is is what I'm looking for. We will have ssl authentication enabled. Would you mind providing details on how you use AJP to proxy to JIRA? Greatly appreciate the reply.

JamieA
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 31, 2012

I will try and post the config tomorrow when I'm near it, but it's pretty standard. Use mod_proxy_ajp to proxy to ajp:/jirahost:port/jira

You need to open the ajp port in your jira server.xml.

We only use https for the /login page, all others get redirected.

Jody Sevidal January 31, 2012

Look forward to your posts. Thanks for the tips.

JamieA
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 1, 2012

Here is the authenticator class: https://gist.github.com/1722771

Apache config:

ProxyPass ajp://$host:$port/$context
ProxyPassReverse ajp://$host:$port/$context

There are not many places that use PKI... yours is the first I've heard of after mine and Nic's. The people responsible should be providing you with guidelines on how to set it up...
Jody Sevidal February 1, 2012

Jamie - this is awesome - truly appreciate forwading the code and will give this a try. Thanks again.

Jim Finnessy November 19, 2014

Jamie, I got it setup with your authenticator and proxy pass through ajp. I updated the seraph-config.xml for CROWD to point at your AjpAuthenticator. It finds the class in the classpath, because it doesn't throw an exception. But, I can't seem to get the authenticator to fire. I've confirmed that it's using AJP to talk to Tomcat, and Apache is prompting me for the cert, but I don't ever seem to invoke the authenticator, and I get directed to the login page. I've tried mucking with the login page, however, that didn't seem to do the trick either. Any ideas for me to try? Thanks!

0 votes
Mohanjit Grewal December 3, 2015

Here's how we did it to look at the email address on JIRA 7.0.2 (should work in 6, if not, change the UserSearchService stuff accordingly, in think to UserManager or something):

 

package your.package.name;

import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.bc.user.search.UserSearchService;
import com.atlassian.jira.security.login.JiraSeraphAuthenticator;
import com.atlassian.jira.user.ApplicationUser;

/*
 * Note that this authenticator gets called everytime a Jira page is loaded, so any
 * logs or any printlns will be output into catalina.out each time any page is loaded
 * so it is not a good idea to have it going all the time...
 */
public class CertAuthenticator extends JiraSeraphAuthenticator {


	private static final Logger log = Logger.getLogger(CertAuthenticator.class);

	@Override
	public Principal getUser(HttpServletRequest request, HttpServletResponse response) {
		// user already set, so keep using it...
		Principal user = super.getUser(request,response);
		if (user != null)
			return user;
		
		// no user set (e.g. they didn't login), so try certificate, if not return null...
		
		X509Certificate[] cert = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
		// no cert, return null
		if (cert==null || cert.length == 0)
			return null;
		
		LdapName ln;
		String email= null;
		// get the email address, the Cert DN is similar to the LDAP name, so exploit that functionality
		try {
			ln = new LdapName(cert[0].getSubjectDN().toString());
			for (Rdn rdn : ln.getRdns()){
				if (rdn.getType().equalsIgnoreCase("EmailAddress")){
					email = (String) rdn.getValue();
				}
			}
		} catch (InvalidNameException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// find the user based on the email address
		if (email != null ){
			UserSearchService search = (UserSearchService) ComponentManager.getComponentInstanceOfType(UserSearchService.class);
			
			for (ApplicationUser u :  search.findUsersByEmail(email)){
				// take the first matching one!
				user = u;
				log.debug("x509 (PKI) email found:  "+ email);
				break;
			};

        }	
		
		log.debug("x509 (PKI) login as  "+ user);
		
		return user;
	}
	
	
}

 

Then enable ajp on the server, add the httpd proxy pass, etc.  as per comments above.   To compile, grab the libs in the WEB-INF/lib , the jira/lib along with the classes in WEB-INF/classes and include them as libraries / class folders in your Eclipse Java Project (IntelliJ didn't work so well with this), then export the jar and throw it in your WEB-INF/lib directory.  make sure that only your class is listed as an authenticator in the seraph-config.xml file and reboot JIRA and it should kick in, you can always turn the log.debug to log.error or add system.out.printlns if things aren't working and tail -f the catalina.out log file.

0 votes
Nic Brough -Adaptavist-
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
January 31, 2012

He, actually, yes - x509 certs here bypass login altogether!

There's a massive hole in the implementation - there's nothing unique in the certificate to point at and say "this cert identifies this login", but otherwise it works fine. We have an apache front end as a basic gate (no cert, no access), then a dodgy algorithm that works out a unique login from info on the cert, then we use that instead of username/password. We also have a simple access policy - anyone with a cert is trusted enough to see the data, so if we can't find an account, we create a new one automatically and whack it into Jira-users and confluence users.

If that sounds like what you want, then the gotcha is the code to work out a user id from a cert, but the rest is a small plugin and a tweak to the authentication file to say "use this class instead".

Jody Sevidal January 31, 2012

Nic - thanks for your reply. So how do you retrieve the username and password from the cert or within apache and how do you send that to JIRA for authentication. Is this an apache modue/plug-in and then sends to JIRA via XML/RPC?

Nic Brough -Adaptavist-
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
January 31, 2012

Not even that complex! I'm not familiar with the Apache side of it - all I know is that the config for the virtual servers includes a certificate check before it'll forward any requests - that's nothing to do with Jira. It's off-the-shelf in Apache too, I don't think you need any modules other than standard SSL. We use it for Subversion, Confluence, Hudson/Jenkins, and a whole stack of other tools for monitoring, digging etc.

Inside Jira, there's a "use this class for authentication" call in the seraph-config.xml - you effectively comment out all the authentication classes and say <authenticator class="com.csl.certs.seraph.certAuthenticator" /> (or whatever you call it)

The authenticator plugin then picks up the certificate data from the incoming request that Apache forwards to it. Sadly, it uses a users email address to id them (which is fundamentally broken, because they can change, so that's something you'll need to consider)

Another weakness - that only applies for https sessions - RPC calls need to use a cert to get through Apache, then they still need username/password, as we've only got this for interactive browser sessions. But they work fine, which is the important bit.

Suggest an answer

Log in or Sign up to answer