{"id":397,"date":"2020-11-08T20:24:00","date_gmt":"2020-11-08T20:24:00","guid":{"rendered":"https:\/\/lab.rapternet.us\/?p=397"},"modified":"2020-10-18T19:51:38","modified_gmt":"2020-10-18T19:51:38","slug":"jenkins-ldap-authentication","status":"publish","type":"post","link":"https:\/\/lab.rapternet.us\/?p=397","title":{"rendered":"Jenkins LDAP Authentication"},"content":{"rendered":"\n<p>Continuing on my journey through centralizing my authentication for a number of services, I come to my Jenkins instance. This was setup recently to handle CI\/CD on some of my personal projects, and has been working extremely well on automated build\/test\/deploy. Since none of the software it supports is going anywhere anytime soon, I decided to add the instance to the grouping of services going to LDAP for authentication. Setting up Jenkins with LDAP is a relatively straight forward process. It requires a few plugins to support it fully (authentication, group based authorization). The difficulty comes in authorization, as the role-based authorization strategy plugin isn&#8217;t as well documented as one would hope. It is at least not too hard to work through and works as intended.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Plugins to Use<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/plugins.jenkins.io\/ldap\/\">LDAP Plugin<\/a><ul><li>Default Plugin included in Jenkins<\/li><\/ul><\/li><li><a href=\"https:\/\/plugins.jenkins.io\/role-strategy\/\">Role-Based Authorization Strategy<\/a><\/li><li><a href=\"https:\/\/plugins.jenkins.io\/authorize-project\/\">Authorize Project<\/a><\/li><\/ul>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Setting it Up<\/h2>\n\n\n\n<p>All of the plugins were used in this guide, however for just simple LDAP authentication, only the LDAP plugin is needed. The other plugins are only needed to support role-based authorization. By default, all users who log into Jenkins are treated as administrators. The role-based authorization plugin allows us to limit the rights of different accounts based off of their LDAP groups.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Determining the Group Attributes<\/h2>\n\n\n\n<p>While reading the LDAP plugin documentation, I noticed the following interesting snippet.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>OpenLDAP with the memberof overlay active (untested, and as memberof is an operational attribute in OpenLDAP it must be explicitly requested, so likely some hacking of LDAPSecurityRealm.groovy required)<\/p><cite>LDAP Plugin Documentation<\/cite><\/blockquote>\n\n\n\n<p>So according to this, the memberOf attribute in OpenLDAP can be used for getting a users group memberships, it is not the most effective way to do so for Jenkins, and isn&#8217;t well supported. For this reason we decided to go the other way, see which users belong to which groups. To start out, I ran an LDAP search on the administrators group to see what attribute name the membership would be under, and the class of LDAP object the group belongs to.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Administrator@ucs:~$ ldapsearch -xLLL -b cn=administrators,cn=Builtin,dc=rapternet,dc=us -D uid=auth-grafana,cn=users,dc=rapternet,dc=us -W \\* +\nEnter LDAP Password:\ndn: cn=Administrators,cn=Builtin,dc=rapternet,dc=us\nsambaGroupType: 2\ncn: Administrators\nobjectClass: top\nobjectClass: univentionGroup\nobjectClass: posixGroup\nobjectClass: univentionObject\nobjectClass: sambaGroupMapping\nuniventionObjectType: groups\/group\nsambaSID: S-1-5-32-544\ngidNumber: 5052\nuniventionGroupType: -2147483643\ndescription: Administrators have complete and unrestricted access to the compu\n ter\/domain\nstructuralObjectClass: posixGroup\nentryUUID: bc1e8154-1220-1036-9551-016bd08a0cd5\ncreatorsName: cn=admin,dc=rapternet,dc=us\ncreateTimestamp: 20160918192042Z\nmemberUid: Administrator\nuniqueMember: cn=domain admins,cn=groups,dc=rapternet,dc=us\nuniqueMember: cn=enterprise admins,cn=groups,dc=rapternet,dc=us\nuniqueMember: uid=administrator,cn=users,dc=rapternet,dc=us\nentryCSN: 20160918192044.102359Z#000000#000#000000\nmodifiersName: cn=admin,dc=rapternet,dc=us\nmodifyTimestamp: 20160918192044Z\nentryDN: cn=Administrators,cn=Builtin,dc=rapternet,dc=us\nsubschemaSubentry: cn=Subschema\nhasSubordinates: FALSE<\/pre>\n\n\n\n<p>From the output, we can see that the object class for our group is posixGroup, which is a part of the default filters for the Jenkins LDAP plugin, and the membership attribute is uniqueMember, which is also a part of the default filters in Jenkins. This is lucky for us as its less configuration to have to do to get our centralized authentication working.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up the LDAP Plugin<\/h2>\n\n\n\n<p>The LDAP plugin settings are located under Manage Jenkins \u2192 Configure Global Security. The security realm can then be changed from \u201cJenkins own user database\u201d to \u201cLDAP\u201d. Make sure that LDAP searches are working properly using the \u201cTest LDAP settings\u201d button before saving the settings changes. Click the \u201cAdvanced Server Configuration\u201d button to show the full set of settings for the LDAP plugin.<\/p>\n\n\n\n<p>The LDAP plugin is straight forward to setup. The only feature I was missing was to allow self signed certs (or to not check the certificates) when using LDAPS. Due to this, and my lack of signed certificates for my LDAP server, I used the unencrypted connection. For an encrypted connection, make sure you have your certificates in order and use an LDAPS url to tell Jenkins to encypt the connection.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Server: <a href=\"ldap:\/\/ucs.rapternet.us:7389\">ldap:\/\/ucs.rapternet.us:7389<\/a><\/li><li>Root DN: dc=rapternet, dc=us<\/li><li>User search base: blank<\/li><li>User search filter: uid={0}<\/li><li>Group search base: blank<\/li><li>Group search filter: blank<\/li><li>Group membership: search for LDAP groups containing user: blank<ul><li>blank by default goes to: (| (member={0}) (uniqueMember={0}) (memberUid={1}))<\/li><li>This covers the univention uniqueMember field on the groups<\/li><li>This also avoids memberOf issues that occur with the plugin that require extra work to go around<\/li><\/ul><\/li><li>Manager DN: uid=jenkins-auth,cn=users,dc=rapternet,dc=us<\/li><li>Password: stronk_password<\/li><li>Display name LDAP attribute: displayname<\/li><li>Email address LDAP attribute: mailPrimaryAddress<\/li><\/ul>\n\n\n\n<p>All of the inputs left blank were to use the default values for them. These were found to cover the attributes in UCS and were found to work (so why complicate it).<\/p>\n\n\n\n<p>As far as the group membership goes, I did try to use memberOf in the most basic sense to no success. The LDAP plugin didn&#8217;t request the memberOf attribute when searching for users, so without extra work, I couldn&#8217;t use that to flag what groups my user was a part of. I setup Jenkins to search for users within my groups using the uniqueMember attribute. Also ensure that your search user has a strong password to login with.<\/p>\n\n\n\n<p>Our administrator test reveals that groups are working now that we are using the uniqueMember attribute on the groups themselves.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Login\nAuthentication: failed for user \"administrator\" with empty password\nLookup\nUser lookup: successful\nUser ID: administrator\nUser Dn: uid=Administrator,cn=users,dc=rapternet,dc=us\nUser Display Name: Administrator\nUser email: administrator@rapternet.us\nLDAP Group membership:\n\n    Domain Admins\n    piwigo_admins\n    Enterprise Admins\n    Group Policy Creator Owners\n    Schema Admins\n    piwigo_webmasters\n    piwigo_users\n    Domain Users\n    Administrators\n    DC Backup Hosts\n\nLDAP Group lookup: successful (10 groups)<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Role-Based Authorization Strategy<\/h2>\n\n\n\n<p>I installed the role-based authorization strategy plugin as a way to provide some authorization based on the LDAP groups on my univention server. This was simple enough to setup after some of my earlier work with Portainer and Grafana.<\/p>\n\n\n\n<p>Authorization settings can be found under Manage Jenkins \u2192 Configure Global Security in the \u201cAuthorization\u201d section. The default uption is that logged in users can do everything, essentially making all users admins. We&#8217;re going to change that to Role-Based strategy. This will allow us to change how much access different LDAP groups will have over the Jenkins instance. After saving the settings to use the role-based strategy, the settings will show up in the Manage Jenkins panel, this time at the bottom under \u201cManage and Assign Roles\u201d.<\/p>\n\n\n\n<p>Under Mange and Assign Roles, there are a few panels:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Manage Roles<ul><li>Create new roles<\/li><li>Determine the access for each role<\/li><\/ul><\/li><li>Assign Roles<ul><li>Assign LDAP groups to each grouping of roles (Global, Item, Node)<\/li><\/ul><\/li><li>Role-Strategy Macros Info<ul><li>I didn&#8217;t utilize these settings<\/li><\/ul><\/li><\/ul>\n\n\n\n<p>Under the Manage roles panel, I setup two sets of roles in Jenkins, viewer, and administrator. My viewer role was given a global read only in the overall set of options. My admin role was given every checkbox concievable in the global roles. I created an item_admin and node_admin roles as well, each with \u201c.*\u201d for the pattern, and every access available. These need to be unique names for each grouping.<\/p>\n\n\n\n<p>Under the Assign Roles panel, I provided anonymous logins the viewer role, and added my administrators group to use the admin role. My Jenkins instance is only accessible from my intranet (and will forever stay that way), so I&#8217;m not worried about a local user being able to view my builds. I wanted at least a basic way to spy on whats going on quickly if needed. Adding a new LDAP group is easy enough, just add in the name to the User\/Group to add, click the add button, and pick the checkboxes to each role the group should have.<\/p>\n\n\n\n<p>In the future, I will likely add a less privileged user for my general user account, being able to manage projects but not resources. This would be another group to add in both LDAP and in this panel, providing the group in Jenkins a subset of the permissions.<\/p>\n\n\n\n<p>Since I could verify that my user was seen in the group in Jenkins, I guessed that this would work right based on the documentation, though I wish they had a little checker widget that first log out log in is a bit stressful if that role didn&#8217;t work right. Luckily it did work correctly and my administrator user could still administrate the Jenkins installation after enabling the role based auth plugin.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Authorize Plugin<\/h2>\n\n\n\n<p>Under the Manage Jenkins \u2192 Configure Global Security, there is an \u201cAccess Control for Builds\u201d section that this plugin handles. For my case, I want builds to all be run as the system as the system user is the user with access to the docker.sock for building with. I set this as such in my security settings, though Jenkins will complain about this setting as it isn&#8217;t the most secure option available.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Setting up the centralized authentication on Jenkins was also pretty straight forward. The difficulty came in the Role-Based Authorization plugin and determining if it was setup correctly (lack of a widget to verify its working correctly). For this reason, I would only suggest setting that up if needed (at the time of this writing, the plugin is also not currently supported, which has me nervous). This worked out well on my Jenkins instance and I&#8217;ve been using my LDAP users on it since configuring it. The Role-Based Authorization and LDAP authentication work well.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Continuing on my journey through centralizing my authentication for a number of services, I come to my Jenkins instance. This was setup recently to handle CI\/CD on some of my personal projects, and has been working extremely well on automated build\/test\/deploy. Since none of the software it supports is going anywhere anytime soon, I decided &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/lab.rapternet.us\/?p=397\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Jenkins LDAP Authentication&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[46,40,45,44,43],"class_list":["post-397","post","type-post","status-publish","format-standard","hentry","category-how-to","tag-authentication","tag-jenkins","tag-ldap","tag-ucs","tag-univention"],"_links":{"self":[{"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=\/wp\/v2\/posts\/397","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=397"}],"version-history":[{"count":1,"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=\/wp\/v2\/posts\/397\/revisions"}],"predecessor-version":[{"id":398,"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=\/wp\/v2\/posts\/397\/revisions\/398"}],"wp:attachment":[{"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=397"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=397"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lab.rapternet.us\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=397"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}