Kubernetes Authentication using Dex and LDAP

Written by Greg Keller on March 22, 2019

Share This Article

You’re working with containers or orchestration environments, but that doesn’t mean that you want to manage user identities from your Kubernetes (k8s) cluster. Whether you’re a DevOps engineer or a software developer, you may have good reason to use an external source of identity for authentication instead.

By authenticating using an external source of identity, organizations can sync Kubernetes identities with their core credentials or simply use their identity provider to auth into K8s services. This streamlines access for users and enables IT to manage everything from one pane of glass. These external identity providers can range from standard directory services (e.g. LDAP) to social platforms leveraging OAuth such as Google.

In this article, we will briefly explain the integration paths and provide examples for integrating the Dex open-source service with JumpCloud’s cloud LDAP endpoint.    


Before we begin, the reader should familiarize themselves with two key external resources which will underpin this brief article. The first is related to Dex itself. Dex is an open source OIDC (OpenID Connect) authentication service launched by CoreOS. This service provides an essential abstraction layer between other services (e.g. an app, microservice or a Kubernetes cluster itself) and sources of identity such as LDAP, Google, Linkedin, etc.  

The second is a great piece by Medium author, Krishna, who was the inspiration for our piece and a deep dive on integrating a Kubernetes-based service with JumpCloud’s Cloud LDAP.

Configuring Kubernetes to LDAP

In Krishna’s article, he lays out an extremely simple model to follow. His tutorial, offered on his GitHub repo, “Kubernetes – LDAP authentication with Dex”, establishes a Kubernetes environment, lays down a simple app and associated services, “loginapp”, in addition to all of the Dex infrastructure required to integrate the app to authenticate with an LDAP service (JumpCloud in this case).

The simple interaction that is unpacked is generally, not precisely, as you see below in this sequence diagram:


This article will not serve as the instructions for building out Krishna’s demo environment. We assume the reader has the skills to do so, and specific questions if they arise, should be directed to Krishna.

Implementation with JumpCloud LDAP

In the steps Krishna details, the critical area as it relates to integrating with JumpCloud specifically relates to the dex-cm.yml config file. It is here that the parameters to point the Dex service to the JumpCloud LDAP endpoint must be input. As a prerequisite to understanding JumpCloud’s LDAP structure, readers should review our instructional guide to setting up the cloud directory service… – e.g., establishing LDAP BindDN service accounts, selecting users in the directory that should be presented via our LDAP service and more can be found here. This article is foundational, easy to follow, and will prepare your JumpCloud’s Cloud LDAP and its user and groups correctly. Once set up, you can even practice native ldapsearch queries against it to see how the Dex service will do so in kind, behind the scenes once integrated.

Below is a sample of the Dex Yaml file. Where you see ‘-’ we have left what the file contained as defaults. Where you see ‘+’ you can see what we modified to talk accurately to JumpCloud’s Cloud LDAP.

            $ cat changes.txt 
diff –git a/dex-cm.yml b/dex-cm.yml
index 40fd682..d2877d3 100644
— a/dex-cm.yml
+++ b/dex-cm.yml
@@ -5,7 +5,7 @@ metadata:
   namespace: auth
   config.yaml: |
–    issuer: https://dex.k8s.example.org/dex
+    issuer: https://dex.k8s.example.org:32000/dex
       type: kubernetes
@@ -29,18 +29,18 @@ data:
           # If the port is not supplied, it will be guessed based on “insecureNoSSL”,
           # and “startTLS” flags. 389 for insecure or StartTLS connections, 636
           # otherwise.
–          host: ldap.k8s.example.org:389
+          host: ldap.jumpcloud.com:636

           # Following field is required if the LDAP host is not using TLS (port 389).
           # Because this option inherently leaks passwords to anyone on the same network
–          insecureNoSSL: true
+          insecureNoSSL: false
           # If a custom certificate isn’t provide, this option can be used to turn on
           # TLS certificate checks. As noted, it is insecure and shouldn’t be used outside
           # of explorative phases.
–          insecureSkipVerify: true
+          insecureSkipVerify: false
           # When connecting to the server, connect using the ldap:// protocol then issue
           # a StartTLS command. If unspecified, connections will use the ldaps:// protocol
@@ -54,16 +54,16 @@ data:
           # server provides access for anonymous auth.
           # Please note that if the bind password contains a `$`, it has to be saved in an
           # environment variable which should be given as the value to `bindPW`.
–          bindDN: cn=admin,dc=example,dc=org
–          bindPW: admin
+          bindDN: uid=<redacted>,ou=Users,o=<redacted Org ID>,dc=jumpcloud,dc=com
+          bindPW: ‘<redacted>’

           # User search maps a username and password entered by a user to a LDAP entry.
             # BaseDN to start the search from. It will translate to the query
             # “(&(objectClass=person)(uid=<username>))”.
–            baseDN: ou=People,dc=example,dc=org
+            baseDN: ou=Users,o=<redacted Org ID>,dc=jumpcloud,dc=com
             # Optional filter to apply when searching the directory.
–            filter: “(objectClass=posixAccount)”
+            filter: “(objectClass=inetOrgPerson)”
             # username attribute used for comparing user entries. This will be translated
             # and combine with the other filter as “(<attr>=<username>)”.
             username: mail
@@ -73,20 +73,20 @@ data:
             # Required. Attribute to map to Email.
             emailAttr: mail
             # Maps to display name of users. No default value.
–            nameAttr: uid
+            nameAttr: cn

           # Group search queries for groups given a user entry.
             # BaseDN to start the search from. It will translate to the query
             # “(&(objectClass=group)(member=<user uid>))”.
–            baseDN: ou=Groups,dc=example,dc=org
+            baseDN: ou=Users,o=<redacted Org ID>,dc=jumpcloud,dc=com
             # Optional filter to apply when searching the directory.
–            filter: “(objectClass=posixGroup)”
+            filter: “(objectClass=groupOfNames)”
             # Following two fields are used to match a user to a group. It adds an additional
             # requirement to the filter that an attribute in the group must match the user’s
             # attribute value.
–            userAttr: uid
–            groupAttr: memberUid
+            userAttr: DN
+            groupAttr: member
             # Represents group name.
             nameAttr: cn

With the file configured as you see above, and given Krishna’s instruction set was straight forward, the process was relatively quick to get LDAP-backed authentication for Kubernetes working. Note, we have leveraged Krishna’s example to demonstrate LDAP authentication of his ‘loginapp’, yet this same process can be leveraged to point the Kubernetes Dashboard, via Dex, to also use JumpCloud’s LDAP for its own directory-based authentication.

Additional Resources

You may want to visit GitHub contributor, Parallax, to leverage their examples using JumpCloud LDAP to integrate with the Kubernetes Dashboard.

Of course, you can always reach out to us directly for information or visit our Knowledge Base.

Greg Keller

JumpCloud CTO, Greg Keller is a career product visionary and executive management leader. With over two decades of product management, product marketing, and operations experience ranging from startups to global organizations, Greg excels in successful go-to-market execution.

Continue Learning with our Newsletter