How we moved Crowd user management to a centralized cloud directory
A common problem experienced by Atlassian Server administrators using Atlassian Crowd for user directory management is that Jira Service Desk (JSD) customers must exist as Crowd users, while the Crowd Single Sign On (SSO) is used across the application stack.
This can lead to a large number of licensed Crowd users, ultimately resulting in unnecessary business costs. Additionally, a Crowd Server user directory introduces a single point of failure when combined with SSO, if the Crowd Server is unreachable then authentication is impossible for any application that uses Crowd.
So, how did we go about solving this problem at New Verve?
Requirements
First, we captured our requirements. We needed:
- a centralized cloud user directory that could be used for more services than just our Atlassian stack of applications
- to reduce costs for our Crowd license (and handle Jira Service Desk customers elsewhere)
- implement Single Sign-On (SSO) across all applications
- the ability to enforce Two Factor Authentication (2FA) at application login
Possible solutions
To avoid storing Jira Service Desk customers in Crowd (and hence reduce the license cost), we first considered using a modified Seraph Connector which could default back to Jira’s home directory. However, this approach introduces technical overhead, as you would either have to rely on an open source implementation which has not been actively maintained, or develop your own custom Seraph Connector.
In the end, we opted for a more comprehensive solution that could deliver on all of our requirements.
Centralized cloud directory
There are many SaaS user directory services available, offering similar functionality. The ones we considered were:
Google SSO
GSuite customers can create SAML SSO Applications free-of-charge, which can be used by any licensed GSuite users. However, GSuite does not offer an LDAP service, although it is possible to synchronise GSuite and JumpCloud, with JumpCloud providing LDAP access.
JumpCloud
JumpCloud offers a range of different user directories, including LDAP and SAML based single sign-on. There are different user tiers available, offering different functionality at a range of per-user prices.
OneLogin
OneLogin offers a huge list of features, most of which were not part of our requirements. The custom branding functionality is very interesting though, compared to say JumpCloud where you are currently unable to customise the login experience. There are different user tiers available, offering different functionality at a range of per-user prices. Each tier offers LDAP as an add-on, priced at an additional €2/user/month.
Okta
While Okta offers a huge range of features in different tiers, but the SSO tier met all of our requirements at a reasonable price per user. They’re also used by some big names, including Experian and Adobe.
SSO
As we cannot use additional user directories in combination with Crowd’s SSO connector, we could have decided to remove SSO all together, authenticating each application separately. This is a pain though, as each user would then have to authenticate with each application.
There are numerous SSO apps available on the Atlassian Marketplace which provide similar functionality to the Crowd SSO connector, but allowing the use of multiple internal and external user directories.
Example marketplace apps:
- SAML Single Sign On (SSO) Jira - Resolution
- Single Sign On/SSO for Jira SAML - miniOrange
- Jira Single Sign On, SSO SAML/Kerberos - Kantega SSO
- Global SSO Single SignOn for Jira (SAML) - Cprime
- EasySSO (Jira) Kerberos /NTML/SAML - TechTime
2FA
While planning our migration of the Crowd user directory to the Jira User Server and an External User Directory, we looked into solutions for enforcing Two Factor Authentication (2FA) for our staff members, so that we could add an additional authentication requirement to user logins.
Sadly, there is no out-of-the-box functionality provided by Atlassian for 2FA. While add-ons do exist on the Market Place to provide 2FA, we chose to include the 2FA enforcement in the requirements for the external user directory.
Our chosen solution
We chose JumpCloud as our centralized user directory, as it provides all the functionality we need to meet our requirements (SAML SSO, 2FA) as well as providing authentication for desktop PCs and a wide range of other features. In addition to this, we currently sit in the free tier due to our user number requirements.
We made the internal decision to phase out Crowd usage, in favour of moving to a Jira provided user server. This allows us to create as many JSD customers as is required, without exceeding our Jira user licence limits. Migrating from Crowd Server to Jira’s User Server does not solve the single-point-of-failure that exists when centralising all application access in a single Server application instance. To mitigate this single-point-of-failure for company accounts, we decided it was best to introduce a secondary user directory for staff use, and to store customer accounts within Jira’s User Server.
For SSO, we opted for Resolution‘s SAML Single Sign-On apps, available for all Atlassian Server products. We found that the SSO options all provided a similar set of core functionality and vary mostly in user interface. Some add-ons offer Kerberos support, but this was not something we required.
To roll out our chosen solution, we had a couple of challenges:
- first, we had to migrate from Crowd and then retire it;
- then, we needed to figure out a way of enforcing 2FA for employees but not customers (this was a medium-term business requirement).
Configuring JumpCloud with Resolution’s SAML app
We followed Resolution’s guide to setting up a generic SAML provider.
At this stage we realised that JumpCloud only supports a single Entity URL, meaning that each Atlassian application had to have its own JumpCloud SAML Application configured and integrated, which took more time than a single Entity.
Staff user directory
We hooked up our Atlassian applications to JumpCloud for user directory management using JumpCloud’s LDAP-as-a-service.
This is a standard OpenLDAP configuration that was applied to Jira, Confluence, Bitbucket and Bamboo following this guide. We discovered that JumpCloud does not at the time of writing support nested groups, which meant they had to be disabled across all of Jira’s user base.
This is something we hope JumpCloud implements soon, as nested groups make user management a more streamlined process.
Migrating customers from Crowd to Jira
We had a few options in migrating our existing Crowd users over to Jira’s internal user directory. These included directly migrating via database queries, exporting users to CSV and importing into Jira or using an add-on from the Atlassian marketplace.
After evaluating a few options, we decided that TechTime’s User Management for Jira was the bit fit for our needs. It supports bulk migration of users between user directories, as well as a lot of other useful features.
The only caveat we discovered was that passwords would have to be reset for each user. Currently Jira doesn’t allow bulk password resets to be sent out, so we had to notify each customer to reset their password through the standard Jira forgotten login form.
Enforcing SAML SSO
Now that our employee accounts are migrated to JumpCloud’s active directory, using SAML SSO for sign on with 2FA, we needed to enforce the usage of JumpCloud sign on. This was performed by using Resolution’s DenyPasswordAuthenticator Seraph connector - which needed to be installed to the lib directory in the installation directory for each application. We had to then create the allowed-password-login group within Jira, and add all non-employee accounts to this group so that customers could still sign on using Jira’s standard login, but employees would have to use JumpCloud with 2FA.
As we’re enforcing SAML SSO for employees only, we needed a way to automate adding customers to the allow-password-login group, as this would have to be a manual step on user creation, including for Service Desk customers. To handle this, we wrote a Groovy script and set this up as a custom listener using ScriptRunner for Jira Server, triggered by the UserCreatedEvent. Once a new user has been created, the script checks their email address to make sure it doesn’t match one of our internal domains (@newverve). If no match is found, then they are added into the allow-password-login group.
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.crowd.event.user.UserCreatedEvent import com.atlassian.jira.user.util.UserUtil import com.atlassian.jira.security.groups.GroupManager import com.atlassian.jira.user.util.UserManager import com.atlassian.jira.user.ApplicationUsers import com.atlassian.jira.user.ApplicationUser import com.atlassian.jira.util.ErrorCollection import com.atlassian.jira.util.SimpleErrorCollection // Settings String AdminUser = "sysadmin" //User with admin rights String defaultGroup = "allow-password-login" String emailFilter = "@newverve" // Get manager components UserManager userManager = ComponentAccessor.getComponent(UserManager) GroupManager groupManager = ComponentAccessor.getComponent(GroupManager) UserUtil userUtil = ComponentAccessor.getComponent(UserUtil) // Run as a system admin ApplicationUser adminApplicationUser = userManager.getUserByName("${AdminUser}") ComponentAccessor.getJiraAuthenticationContext().setLoggedInUser(adminApplicationUser) // Retrieve event and user details def event = event as UserCreatedEvent def eventUser = event.user def eventUserEmail = eventUser.getEmailAddress() def appUser = userManager.getUserByKey(ApplicationUsers.getKeyFor(event.user)) log.info event // Check filter, exit if match found if (eventUserEmail.contains(emailFilter)) { log.info "User ${appUser} has an email (${eventUserEmail}) containing filter ${emailFilter}, skipping" exit(0) } // Get user and group def gname = groupManager.getGroup(defaultGroup) log.info "User: ${appUser}" userUtil.addUserToGroup(gname, appUser) //Adds the user to the above group log.info "User ${appUser} has been added to group ${defaultGroup}"
Comments:
Great article, but Google does support secure LDAP - https://gweb-cloudblog-publish.appspot.com/products/identity-security/simplifying-identity-and-access-management-for-more-businesses/amp/
By Paul Lees
on Fri, December 07, 2018
Thanks Paul! Indeed, secure LDAP is supported but it’s through another product Cloud Identify which comes with its own subscription costs. We’ll edit the article to make this clear!
By nigel
on Fri, December 07, 2018
Still a great resource after 1.5 years giving a good overview about the topic. Thank you!
One question regarding JSD: I aasume you didn’t use knowledge base articles in a linked Confluence instance, right? Otherwise keeping the customer accounts in a Crowd directory seems like the way to go to maintain a common user base between both applications. Linking Confluence to Jira user server when you only need to sync non-SAML / non-sync accounts doesn’t look good to me. Or do I miss a point? Thanks again!
By Norman
on Tue, May 12, 2020