Saturday, September 1, 2018

Jenkins role based access control (RBAC) with Azure AD Plugin - Step by Step

In this blog I wanted to detail out steps in enabling role based access control (RBAC) in Jenkins using Azure AD Plugin and that too without having enterprise edition of Jenkins (Cloudbees).

Here are few scenarios where this will be useful

  • When you want to control access to your Jenkins only for designated users within your organization. Only users in certain group can create, delete jobs but other can see build console outputs, trigger a new build etc.
  • You want to ensure that project team members can access only their own jobs, logs etc. 
  • Note: You don't need to expose Jenkins endpoint externally unless you want to have webhooks configured from your git repositories (if they are hosted and accessible over internet
  • and many more


PRE-REQUISITES:
  1. Have Jenkins latest version installed. I had Jenkins running on Ubuntu 16.04 Azure VM and the DNS is configured to be myjenkins on VMs public IP. Thus your Jenkins Fully Qualified Domain name would be http://myjenkins.<region>.cloudapp.azure.com:8080
  2. Install Azure AD plug-in installed from available plugins in your jenkins instance (Manage Jenkins -> Manage Plugins -> Available tab -> Search for Azure AD)


  3. Azure AD Plugin










  4. You have valid Azure Subscription and have privileges to create service accounts in the Azure AD that you are planning to use. If you do not have access, you can request your administrator to create a service account as per the instructions provided below
  5. In addition, to test Role based access control (RBAC), you will need permissions to create couple of groups and few user accounts to be able to login to Jenkins who are assigned in those different groups.
CONFIGURATION STEPS
  1. Create Service Account in Azure AD- This service account is used by Jenkins to query user profile and group memberships
    • Navigate to Azure Active Directory from the left navigation once you login on Azure Portal (https://portal.azure.com)
    • Click on App Registrations -> New Application Registration
    Creating new Azure AD Service Account

    • Enter Name (e.g. in my case I named it as Jenkins) for your service account
    • Select Application Type as Web App / API
    • Enter Sign-On URL as your home page URL for your Jenkins instance (as mentioned in pre-requisites i.e. http://myjenkins.<region>.cloudapp.azure.com:8080)
    Azure AD Service Account Creation for Jenkins - Initial Settings

    • Click Create, and take a note of Application ID (this is your Client Id) once service account creation is succ 

    • Also take a note of your Directory ID (this is your Tenant ID). You will find this when you navigate to Azure Active Directory menu on the left hand side and go to properties and copy the Directory ID
    Extracting Azure AD Tenant ID / Directory ID

  2. Configure Azure AD Service Account 
    • Configure ReplyURL - This is the URL where Azure AD posts the ID token once the user is authenticated and is in the format http://<Jenkins-dns>.<region>.cloudapp.azure.com:8080/securityRealm/finishLogin i.e in our case it would be http://myjenkins.eastus.cloudapp.azure.com:8080/securityRealm/finishLogin as shown below

    • Configure App ID URI - This is unique App ID URI and can be same as Reply URL as configured above
    • Configure Home Page URL - This is the root url of your jenkins (i.e. http://myjenkins.eastus.cloudapp.azure.com:8080). Please note, no securityRealm/finishLogin appended in the URL. Both these settings are shown below
    Configuring Azure AD App ID and Home Page URL for Jenkins Service Account

    • Edit Manifest to include group memberships as part of your claims when Azure AD posts the ID token to your Jenkins application. You do this by changing gropuMembershipClaims value from null to "All". Without this your group memberships will not be issued as claims and you cannot get role based access control working. 
    Editing Azure AD Service Account Manifest

    Including Group Membership Claims for Azure AD Service Account

    • Next we need to Assign Permissions to this service account to be able to Read Directory Data and and read user profile information. You do this by navigating to settings -> Required Permissions for the service account as shown below. Please note the last step once you have selected the appropriate permissions - you need to Grant Permissions (last image in this step)
    Assigning Permissions to Azure AD Service Account for Jenkins

    Read Directory Data Permissions to Azure AD Service Account for Jenkins

    Sign-in and Read User Profile Permissions to Azure AD Service Account for Jenkins

    Grant Selected permissions to Azure AD Service Account for Jenkins

  3. Creating Azure AD Users
    • Create couple of Active Directory Users (or use the existing ones) by navigating to Azure Active Directory -> Users -> New User. Also take a note of the user name, this includes the name of your active directory. 
    • You can also choose to assign AD groups at this point (if you already have some AD Groups that you want to use) but we will do this in the next step (we will create JenkinsAdmins and JenkinsReader groups).
    • Also take a note of the temporary password. It is better to change the temporary password when you login first time to azure portal using portal.azure.com instead of getting this prompt during authentication process of Jenkins
    • In this exercise let us say we created two users jadmin@<yourdirectory>.com and jreader@<yourdirectory>.com
    Azure AD - Create New Users

    Azure AD - Create New User - Properties

  4. Creating Azure AD Groups and Adding Users
    • Create JenkinsAdmins Group and add one of the user(s) created earlier (jadmin)
    • Create JenkinsReader Group and add a different user(s) (e.g. jreader) than the one added in JenkinsAdmins group.
    • We will be using these 2 different users in 2 different groups to see if authorizations are really working in Jenkins
  5. Configure Jenkins
    • One other important thing to change in Jenkins installation is to change the jenkins.model.JenkinsLocationConfiguration.xml configuration to include your FQDN so that when user is trying to access Jenkins, and it is being redirected by Jenkins to Azure AD for authentication, it correctly sends the redirect_uri that matches with the reply URL we configured in the Azure AD Service account. 
Changin jenkins.model.JenkinsLocationConfiguration.xml

    • If we don't do this, by default jenkinsURL in the above configuration will use the public IP address of your VM and this will not match with your service account reply URL and you will face issue mentioned in this Jira ticket. i.e.  

java.lang.IllegalStateException: Invalid nonce in the response at 
com.microsoft.jenkins.azuread.AzureSecurityRealm.validateAndParseIdToken(AzureSecurityRealm.java:239) at 
com.microsoft.jenkins.azuread.AzureSecurityRealm.doFinishLogin(AzureSecurityRealm.java:202) at 
java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:627) at org.kohsuke.stapler.Function$MethodFunction.invoke(Function.java:343) 
at 


Enabling RBAC, Adding Roles and Assigning Permissions in Jenkins

  1. Configuring Global Security
    • Navigate to Manage Jenkins -> Configure Global Security
    • Check "Enable Security"
    • Select Access Control -> Security Realm -> Azure Active Directory
    • Configure the Client ID, Client Secret and Tenant ID which we noted down during the Service account creation steps above. This is shown below
    Manage Jenkins - Configure global security - Azure AD


    Manage Jenkins - Configure global security - Azure AD - Service Account Configuration
    • Save above changes. Without Saving above changes, you will not be able to add roles based on Azure AD Group and assign permissions

  2. Configuring Jenkins Roles and Permissions
    • Under Configure Global Security, navigate to Authorization section
    • Select Azure Active Directory Matrix-based Security
    • Now when you start typing in Azure user / group to add, you will Azure AD roles being populated in the list.
    • Add desired roles (in our case JenkinsAdmins and JenkinsReader) and assign necessary permissions.
    • In my case, for JenkinsAdmin, I just granted the Overall Admin permissions and for JenkinsReader, I granted only overall read, Build permissions under Agent, Build Cancel Discover and Read permissions for Jobs, Replay permissions in Run, Read permissions in View. This is shown in below pictures
Adding roles in Jenkins Azure Active Directory Matrix-based Security

Granting Permissions in Jenkins Azure Active Directory Matrix-based Security

    • If you want to configure project level roles and permissions, you can navigate to a particular job (assuming you are logged in as administrator) and enable project-based security. You will get the same interface as in Global Security, where you can configure required permissions for your project specific roles.
Jenkins RBAC - enabling project level authorization




Testing RBAC in Jenkins

  • To test the authorizations, I suggest using in-cognito mode of your preferred browser. 
  • First login with the user who has administrative permissions in Jenkins. To do this, hit the Jenkins url, this will automatically redirect you to azure ad authentication page, login with your administrative account (i.e. in our case jadmin@<yourad>.com) and once authenticated successfully, you will be taken to Jenkins home page.
  • Logout and close the incognito window
  • Now open new in-cognito instance of your browser and in similar way, login with your other account who has only read permissions (i.e. in our case jreader@<yourad>.com). You should see you do not have permissions to manage Jenkins and neither you have permissions to create new jobs. You can build existing jobs, look at the console logs etc.


Let me know what you think of this blog and you were able to configure Jenkins for RBAC with Azure AD plugin without any issues

Happy Deployments !!



7 comments:

  1. I keep getting the following error after the config above. Does this work with http or does it have to be https?

    java.lang.IllegalStateException: Can't extract id_token

    ReplyDelete
    Replies
    1. It works on http. At what stage are you getting this error? Are you sure you did the configuration as mentioned in the blog?

      Delete
    2. I believe I did do the config as per the blog. I get this at the end when I try to login to Jenkins.

      Delete
    3. Although maybe I have not set something up properly cause in Step 2 where we are meant to type in the Azure Group in never auto populated. Could this indicate something else?

      Delete
    4. Did you grant appropriate permissions to the service account i.e. to read directory data and in delegated permissions you need to allow sign-in and read profile data. Usually people miss clicking on "Grant Permissions". In addition ensure that you enable groupclaims in the manifest as mentioned in the steps above

      Delete
  2. Yes.
    Manifest has been changed to use ' "groupMembershipClaims": "All", '

    and the following permissions have been granted.
    - APPLICATION PERMISSIONS
    -- Read directory data
    - DELEGATED PERMISSIONS
    -- Sign in and read user profile

    ReplyDelete
  3. I am facing this issue:

    Sorry, but we’re having trouble signing you in.
    AADSTS700016: Application with identifier 'abab' was not found in the directory 'xxxx'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.

    Can you advice on how to proceed further?

    ReplyDelete