Kubernetes and LDAP: Enterprise Authentication for Kubernetes
Tags: kubernetes, vcluster, devspace, loft
Published: 2022-03-23 15:35:34

By Daniel Olaogun

Authentication is an important part of functionality across software applications and infrastructures. It allows you to restrict access to users the system recognizes via tokens, username and password pairs, certificates, and other authentication mechanisms. 

You probably use several applications in your day-to-day activities, with most requiring you to sign in or sign up if you are using the application for the first time or returning after a long period. Remembering multiple passwords, as well as a host of email and password combinations to authenticate different systems can be annoying. 

Single sign-on (SSO) takes care of this issue, allowing users to authenticate into multiple systems with a single identification, such as your email and password combination. This means you don't have to remember multiple authentication credentials, and there are one or two fewer steps when registering on a new system.

SSO can also be used to streamline the process of adding developers to your [Kubernetes](https://kubernetes.io/) cluster. Typically, developers use the `kubectl` client to communicate with a Kubernetes cluster. The `kubectl` has a configuration file that contains authentication certificates stored in your local computer. It uses these certificates to verify your identity whenever you want to communicate with a cluster. 

As a cluster administrator, adding multiple developers can become a tedious process; you’re forced to create multiple certificates, which you must then share securely with each developer you want to add. Then, after the developers receive them, they must each update their `kubectl` config file. Adding developers to your cluster in this way is inefficient, and this is where SSO with Kubernetes can introduce significant improvements.

In this article, you’ll learn about SSO for Kubernetes, its use cases, and how to implement LDAP SSO for Kubernetes using [Loft](https://loft.sh).

## What Is SSO for Kubernetes?

SSO for Kubernetes is an authentication pattern that allows developers to use their identities from other authentication systems to access a Kubernetes cluster. For instance, many organizations use Active Directory, a system that contains details of their employees. As a Kubernetes administrator, rather than creating separate identity credentials such as usernames and passwords, certificates, etc. for developers to access the cluster, SSO allows you to integrate the Active Directory with the Kubernetes cluster. Developers can then use the same Active Directory credentials to authenticate themselves into your Kubernetes cluster. There are several other use cases of SSO for Kubernetes, for example:

### Self-Service for Developers

As mentioned, you typically need to create new certificates and share them securely with each developer you want to add to your cluster. With SSO, that is no longer the case. Once SSO is integrated into your Kubernetes cluster, authorized developers can now log in via the SSO user interface and command-line terminal, which automatically creates a certificate and stores it in their Kubernetes configuration file. This eliminates the chore of generating authentication credentials for each developer, allowing them to do it themselves.

### Ease of Account Provisioning and Management

Managing developers' access can be tedious if you rely on static tokens and certificates you have generated for developers connecting to the cluster. Account management tasks, such as revoking developers’ access and removing or adding developers to the cluster, can also be painfully inefficient without SSO. With SSO successfully integrated into your cluster, the burden is removed, and it becomes much easier to provision new developers into the cluster and carry out other related authentication management.

## Implementing LDAP SSO for Kubernetes using Loft

There are several protocols you can use for implementing SSO in your Kubernetes cluster. Examples include [Lightweight Directory Access Protocol (LDAP)](https://www.techtarget.com/searchmobilecomputing/definition/LDAP), [OpenID Connect (OIDC)](https://auth0.com/docs/authenticate/protocols/openid-connect-protocol), [Kerberos](https://www.techtarget.com/searchsecurity/definition/Kerberos), [Open Authorization (OAuth2)](https://auth0.com/docs/authenticate/protocols/oauth), and [Security Assertion Markup Language (SAML)](https://developers.onelogin.com/saml). 

In this section, you will learn how to implement LDAP SSO using an LDAP directory, [Dex](https://dexidp.io/) and [Loft](https://loft.sh). Dex acts as the link to access and authenticate user information from your LDAP directory. Once Dex authenticates the user, it redirects to the Loft service, which prompts the user to fill in their preferred name on your cluster or use the default from the LDAP directory. The user is now recognized by Loft, allowing you to grant the user access to any cluster you have linked to your Loft account.

### Prerequisites

Before embarking on the tutorial, it’s important to have a Kubernetes cluster set up and running. You won't be using Minikube for this tutorial, but for testing purposes, you can use any of the managed Kubernetes service providers such as [AWS EKS](https://aws.amazon.com/eks/), [AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/), or [GKE](https://cloud.google.com/kubernetes-engine), all of which provide sign-up credits for first-time users. 

It’s also important that you have an LDAP directory set up with more than one user. You can create a directory using [Microsoft AD](https://docs.microsoft.com/en-us/azure/active-directory-domain-services/tutorial-create-instance), [OpenLDAP](https://www.openldap.org/), etc. Additionally, you can check out this [tutorial on how to add users to Microsoft AD](https://docs.microsoft.com/en-us/azure/active-directory/fundamentals/add-users-azure-active-directory).

### Setting up Loft in Your Cluster

Loft is the entry point for your users to get the cluster credentials they will use to communicate with the cluster from their local machine.

#### Installing Loft CLI

The first requirement is to install the `loft-cli` in your local machine. Run the following command based on your local machine operating system:

```
# Mac or Linux bash
curl -s -L "https://github.com/loft-sh/loft/releases/latest" | sed -nE 's!.*"([^"]*loft-darwin-amd64)".*!https://github.com\1!p' | xargs -n 1 curl -L -o loft && chmod +x loft;
sudo mv loft /usr/local/bin;
```

```
# Windows Powershell
md -Force "$Env:APPDATA\loft"; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls,Tls11,Tls12';
Invoke-WebRequest -UseBasicParsing ((Invoke-WebRequest -URI "https://github.com/loft-sh/loft/releases/latest" -UseBasicParsing).Content -replace "(?ms).*`"([^`"]*loft-windows-amd64.exe)`".*","https://github.com/`$1") -o $Env:APPDATA\loft\loft.exe;
$env:Path += ";" + $Env:APPDATA + "\loft";
[Environment]::SetEnvironmentVariable("Path", $env:Path, [System.EnvironmentVariableTarget]::User);
```

> For Windows users, it’s important you add the folder *%APPDATA%\loft* to your PATH environment variable after installing Loft CLI via Powershell. Reboot run the following command to confirm that you have successfully installed Loft CLI:

```
loft --version
```

After installing the Loft CLI, run the following command to set up your Loft Admin account and install Loft in your cluster:

```
loft start
```

Your outcome should look something like this:

![Loft initialization](https://loft.sh/blog/images/content/kubernetes-ldap-1.png)

You can reset your password to something you can easily remember by running the command below:

```
loft reset password
```

![Reset password](https://loft.sh/blog/images/content/kubernetes-ldap-2.png)

#### Ingress Controller and Domain Set Up

You also need to have an ingress controller installed in your cluster. If you already have one, you can skip the installation steps. If you don’t, here’s the command to install Nginx Ingress Controller:

```
helm upgrade --install ingress-nginx ingress-nginx --repository-config='' \
  -n ingress-nginx --create-namespace \
  --repo https://kubernetes.github.io/ingress-nginx \
  --set-string controller.config.hsts=false \
  --wait
```

![Nginx Ingress Controller Installation](https://loft.sh/blog/images/content/kubernetes-ldap-3.png)

You should now have Nginx Ingress Controller successfully installed in your cluster with a result similar to the image above.

Retrieve your external IP address by running:

```
kubectl get services ingress-nginx-controller -n ingress-nginx 
```

Configure a domain such as `loft.YOUR_COMPANY.tld` that points to the external IP address by [setting up an "A" DNS record](https://www.google.com/search?q=set+up+a+record+dns) via your domain services provider. 

#### Configuring TLS for your Domain

Next, you will configure your domain to be accessible via HTTPS. Start by running the command below:

```
helm upgrade --install  cert-manager cert-manager  --repository-config=''\
  --namespace cert-manager  --create-namespace \
  --repo https://charts.jetstack.io \
  --set installCRDs=true \
  --wait
```

Once you’ve installed the `cert-manager`, create a file name `loft.yaml` and paste the following:

```
ingress:
  enabled: true
  host: "loft.YOUR_COMPANY.tld" # replace loft.YOUR_COMPANY.tld to the subdomain you configured in your DNS record
  ingressClass: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: lets-encrypt-http-issuer
  tls:
    enabled: true
    secret: tls-loft

certIssuer:  
  create: true                                      # Change this if you already have your own cert-issuer
  name: lets-encrypt-http-issuer
  email: "YOUR_EMAIL"                               # REQUIRED
  secretName: loft-letsencrypt-credentials
  httpResolver:
    enabled: true
    ingressClass: nginx
  resolvers: []
  server: https://acme-v02.api.letsencrypt.org/directory  
```

Then set the $VERSION variable to the Loft version you want to upgrade to **or** set it to the current version using the command below:

```
CHART=$(kubectl get service loft -n loft -o jsonpath={.metadata.labels.chart})
VERSION=${CHART:5}
```

Then, run the following command to allow your Loft dashboard to be accessible on the domain address you have configured:

```
loft start --upgrade --version=$VERSION --values=loft.yaml
```

Voilà! Your Loft dashboard is now accessible via your domain name `https://loft.YOUR_COMPANY.tld`

![Loft login screen](https://loft.sh/blog/images/content/kubernetes-ldap-4.png)

#### Configuring Dex ldap for Loft

As mentioned in the **Prerequisites** section, it’s important you already have an LDAP server up and running.

Once again, configure a domain for Dex such as `dex.YOUR_COMPANY.tld` that points to the same external IP address you used to configure `loft.YOUR_COMPANY.tld` via your domain services provider. 

Next, you have to configure SSL for the domain. Create a `dex-tls.yaml` file and paste in the following:

```
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: dex-issuer
  namespace: dex
spec:
  acme:
    # Staging API
    server: https://acme-v02.api.letsencrypt.org/directory
    email: YOUR_EMAIL
    privateKeySecretRef:
      name: letsencrypt-key
    solvers:
    - http01:
       ingress:
         class: nginx

---

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: dex-certificate
  namespace: dex
spec:
  secretName: dex-tls
  issuerRef:
    name: dex-issuer
  dnsNames:
  - dex.YOUR_COMPANY.com
```

Then, run the following command to create a `dex` namespace and apply the manifest file:

```
kubectl create namespace dex
kubectl apply -f dex-tls.yaml
```

Next, create a `dex-config.yaml` file and paste the following into it:

```
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
  hosts:
  - host: dex.YOUR_COMPANY.tld                             # Use $DEX_HOSTNAME
    paths:
      - path: /
        pathType: ImplementationSpecific
  tls:
     - secretName: dex-tls
       hosts:
         - dex.YOUR_COMPANY.tld
config:
  issuer: https://dex.YOUR_COMPANY.tld                     # "https://" + $DEX_HOSTNAME
  connectors:
  - type: ldap
    id: ldap
    name: LDAP
    config:
      host: ldap.YOUR_COMPANY.tld:636                        # Your LDAP server hostname:port
      # insecureNoSSL: true                               # Change to false if your LDAP server uses ssl for a secured connection 
      # insecureSkipVerify: true                          # If your LDAP server uses a certificate that can not be verified such as self-signed certificates leave it as true, otherwise, change to false
      # rootCAData: ( base64 encoded PEM file )
      # startTLS: true                                    # Use ldap:// instead of ldaps:// protocol

      # The DN and password for an application service account. The connector uses
      # these credentials to search for users and groups. Not required if the LDAP
      # 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: uid=serviceaccount,cn=users,dc=example,dc=com
      bindPW: password

      # User search maps a username and password entered by a user to a LDAP entry.
      userSearch:
        # BaseDN to start the search from. It will translate to the query
        # "(&(objectClass=person)(uid=<username>))".
        baseDN: cn=users,dc=example,dc=com
        # Optional filter to apply when searching the directory.
        filter: "(objectClass=person)"
        # username attribute used for comparing user entries. This will be translated
        # and combined with the other filter as "(<attr>=<username>)".
        username: uid
        # The following three fields are direct mappings of attributes on the user entry.
        # String representation of the user.
        idAttr: uid
        # Required. Attribute to map to Email.
        emailAttr: mail
        # Maps to display name of users. No default value.
        nameAttr: name

      # Group search queries for groups given a user entry.
      groupSearch:
        # BaseDN to start the search from. It will translate to the query
        # "(&(objectClass=group)(member=<user uid>))".
        baseDN: cn=groups,dc=freeipa,dc=example,dc=com
        # Optional filter to apply when searching the directory.
        filter: "(objectClass=group)"
        # Represents group name.
        nameAttr: name
        # Following list contains field pairs that 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.
        userMatchers:
        - userAttr: uid
          groupAttr: member
  staticClients:
  - name: Loft
    id: loft                                              # Define a preferred $DEX_CLIENT_ID
    secret: XXXXXXXXXXXXXX                                # Define a preferred $DEX_CLIENT_SECRET (can be any secret key)
    redirectURIs:
    - 'https://loft.YOUR_COMPANY.tld/auth/oidc/callback'     # Loft URL + /auth/oidc/callback
  oauth2:
    skipApprovalScreen: true
  web:
    http: 0.0.0.0:5556
  storage:
    type: kubernetes
    config:
      inCluster: true
```

Then run the following command to deploy Dex:

```
helm install dex dex --repo https://charts.dexidp.io \
  --create-namespace --namespace dex \
  -f dex-config.yaml \
  --wait
```

Log in to your Loft dashboard at `https://loft.YOUR_COMPANY.tld`, complete your profile, and then navigate to **Admin > Config** and adjust your Loft configuration as seen below:

```
audit:
  enabled: true
  oidc:
    issuerUrl: https://dex.YOUR_COMPANY.tld  # use $DEX_HOSTMANE as configured in your `dex-config.yaml` file
    clientId: "loft"  # use the preferred $DEX_CLIENT_ID you configured in your `dex-config.yaml` file                         
    clientSecret: "loft" # use the preferred $DEX_CLIENT_SECRET you configured in your `dex-config.yaml` file
    redirectURI: https://loftcluster.tolustar.com/auth/oidc/callback # use the `redirectURI` configured in your `dex-config.yaml` file
```

Open an incognito browser window, and access your Loft domain (`https://loft.YOUR_COMPANY.tld`). You should see a page similar to the image below.

![Loft Login with SSO](https://loft.sh/blog/images/content/kubernetes-ldap-5.png)

As you’ll notice, you have a new button titled **Sign in with SSO**. Clicking the button will redirect you to `https://dex.YOUR_COMPANY.tld` with a page similar to the image below.

![Dex login page](https://loft.sh/blog/images/content/kubernetes-ldap-6.png)

Input the username and password of any of the users in your LDAP directory and complete the developer’s profile. Your Loft account will recognize the developer. It’s important to note that any user or group in your LDAP directory matching the filter configured in your `dex-config.yaml` can visit `https://loft.YOUR_COMPANY.tld` to authenticate and complete their profile.
However, if you visit the clusters page, the developer won’t have access to any cluster yet.

![No access to cluster](https://loft.sh/blog/images/content/kubernetes-ldap-7.png)

#### Granting Developers Access to a Cluster

Log in to your Loft dashboard using your admin credentials. Go to the `Users` page, and you should see a page similar to the image below.

![SSO Users](https://loft.sh/blog/images/content/kubernetes-ldap-8.png)

The developer you authenticated via SSO should appear on the Users page. Now, click **Clusters** on the left menu, then **Cluster Access**, and, finally, the **Create Cluster Access** button.

![Configure access to cluster](https://loft.sh/blog/images/content/kubernetes-ldap-9.png)

Fill in the necessary details and click **Create**.

![Adding user to a cluster](https://loft.sh/blog/images/content/kubernetes-ldap-10.png)

Return to the **Clusters** page in the incognito browser window, and you should see the developer now has access to the cluster.

![Developer Access to Cluseter](https://loft.sh/blog/images/content/kubernetes-ldap-11.png)

The developer should run the following command whenever they want to use any of the clusters accessible to them:

```
loft use cluster [NAME_OF_CLUSTER]
```

The developer can view how to connect to a cluster by visiting the **Clusters** page in their Loft Dashboard, hovering their mouse on the cluster they want to use,  and clicking **connect** as seen below:




![kubectl connection info](https://loft.sh/blog/images/content/kubernetes-ldap-13.png)

The developer's `Kube config` will be automatically updated, allowing the developer to access the cluster via `kubectl`.

## Conclusion

Integrating SSO into your Kubernetes cluster makes it easier to invite new developers into your cluster in a secure manner. Furthermore, it removes the burden of provisioning a new set of credentials for developers on your cluster as you can use the same credentials from an identity provider or active directory service to authenticate developers.

You also learned how to integrate an LDAP Directory Service with your Kubernetes cluster, which allows developers in your directory service to authenticate to your cluster with their existing credentials from the directory.

Loft is a platform that supercharges your Kubernetes cluster management with enterprise-grade access control and cost monitoring, and empowers your team through self-service environment provisioning, such as virtual clusters and preconfigured namespaces. For more information visit [Loft](https://loft.sh).

*Photo by [Tim Foster](https://unsplash.com/@timberfoster?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*