Multi-Factor Authentication for Kubernetes with DUO Security

 

You’ve deployed Kubernetes and someone says “This requires privileged access, that means multi-factor authentication!” It makes sense. You don’t want a phishing email to compromise your cluster. A recent Google study showed that upwards of 90% of phishing attacks are stopped with multi-factor authentication. If your’e a cluster admin and you were to get phished having a second factor can make a real difference. One of the most popular multi-factor solutions is from DUO Security (now owned by Cisco).

In this post we’re going to use Orchestra with Active Directory login as a starting point for integrating DUO. The user’s experience will be to:

  1. Attempt to access the Orchestra login portal
  2. Get challenged for their Active Directory username and password
  3. After providing their credentials, be promoted by DUO to present a second factor (usually a push)
  4. Get into the login portal

We’re detailing this process in a blog post for a couple of reasons. The first is that its a really great integration! The other is it shows off how to customize any of the Orchestra portals for your needs. We’ll start with the customization part.

Orchestra Customization

Orchestra is an application built on OpenUnison. OpenUnison is our identity management project. It provides SSO, LDAP Virtual Directory services, web services, etc. Orchestra is a set of configurations for OpenUnison that builds an application that you interact with. Tremolo Security maintains updated images of these applications for your convenience. We built these images based on our experience and with numerous opinions which may not line up with your requirements, for instance maybe you need a multi-factor authentication solution! We wanted to make it easy to customize Orchestra and OpenUnison in a cloud-native way.

Lets start with what we need to customize Orchestra:

  • Git
  • Java / OpenJDK 8
  • Maven

Thats it. In a production environment, you’ll want to integrate this into a pipeline of some kind where the input is your git repository and the output is a container that’s ready to run (pushed into your container registry of choice). When we make updates to Orchestra and do a build, we’re relying on Maven to take our base OpenUnison web application and layer on top your customizations to create a new war file that is your application:

Once we have a war file (a  Java web application archive) we need a docker image, enter Jib.  Jib is a project from Google to create containers directly from Java.  We integrate Jib into our project in order to make it easier to generate a container without needing to get specialized build services.  It can be built directly in an unprivileged container (which is exactly how we build the images you deploy directly from the OpenUnison github repo).  Once we generate our war file, we run .a special compile command to maven and generate our container.  Once our container is deployed to a registry we just need to update the OpenUnison CR with the new configuration information and to point to the new image and the OpenUnison operator will take care of the rest.

Integrating DUO

You’ll need four pieces of information from DUO to integrate:

  • Integration Key
  • Secret Key
  • API Host Name
  • A Key

The first 3 items come from adding a Web SDK application to your DUO account.  The A Key is generated by you and never shared with DUO.  Once we have this information, we’re ready to start our integration.

  1. Fork the Orchestra Login Portal for Active Directory
  2. Pull the OpenUnison DUO project from Github, build it and install it into your artifact repo of choice.  If running locally your can also just run
    mvn clean package install

    NOTE: Once 1.0.18 is released this step won’t be necessary any more

We’re now ready to start integrating DUO!  The first thing to do is update pom.xml file to reference the OpenUnison DUO implementation.  Add the following code inside the dependencies block:

<dependency>
  <groupId>com.tremolosecurity.unison</groupId>
  <artifactId>unison-auth-duo</artifactId>
  <version>1.0.17</version>
</dependency>

Next, copy the src/main/webapp/auth< directory from the unison-auth-duo project into src/main/webapp in to your fork of  openunison-k8s-login-activedirectory.  These are the files that DUO uses to either push the code to you or collect your code for multi-factor.  Also create a file called src/main/webapp/WEB-INF/auth_mechs/100-duo.xml with the content:

<mechanism name="duo">
        <uri>/auth/duo</uri>
        <className>com.tremolosecurity.proxy.auth.DuoSecLogin</className>
        <init>

        </init>
        <params />
</mechanism>

This tells OpenUnison that DUO is available for authentication.

Finally, edit src/main/webapp/WEB-INF/auth_chains/20-enterprise_idp.xml and insert the below code AFTER the first closing authMech tag:

<authMech>
    <name>duo</name>
    <required>required</required>
    <params>
      <param name="duoIntegrationKey" value="#[DUO_INTEGRATION_KEY]"/>
      <param name="duoSecretKey" value="#[DUO_SECRET_KEY]"/>
      <param name="duoApiHostName" value="#[DUO_API_HOST]"/>
      <param name="duoAKey" value="#[DUO_A_KEY]"/>
      <param name="userNameAttribute" value="uid"/>
    </params>
 </authMech>

This is where the magic happens.  This authentication mechanism generates the signatures needed, redirects you to a JSP page that the user interacts with and generates the push.  Once the user has authenticated, the page posts the results back to OpenUnison to validate.  The userNameAttribute is the name of the attribute that is sent to DUO to identify the user.  The rest of the parameters are supplied to OpenUnison via a Kubernetes secret at run time.

With our edits made, we can build.  I’ll assume for the sake of simplicity we’re doing a local build.  From inside of the openunison-k8s-login-activedirectory fork, run:

$ mvn clean package
$ mvn compile jib:dockerBuild
$ docker tag openunison-k8s-login-activedirectory:1.0.17 docker.io/myrepo/ou-duo:1.0.17
$ docker push docker.io/myrepo/ou-duo:1.0.17

The first command builds our war file.  The second creates a docker image with the tag openunison-k8s-login-activedirectory:1.0.17.  The third command tags it for dockerhub and the final command pushes the image.  If using a different image registry, update accordingly.

Deployment

First, we’ll assume you’ve deployed the Orchestra Login Portal for Active Directory.  There are two configuration points:

  • The orchestra-secrets-source secret that stores sensitive information
  • The orchestra OpenUnison CR

The secret key and A key should be considered secret, so we’ll add them to our source secret by creating a patch.  Base64 encode both values and use the below template to create a patch:

data:
  DUO_SECRET_KEY: base_64_data
  DUO_A_KEY: base_64_data

Then run the patch

kubectl patch secret orchestra-secrets-source -p "$( cat /path/to/secret-patch.yaml)

Finally, update the orchestra openunison cr:

spec:
.
.
.
  image: docker.io/registry/opeonunison-duo:1.0.17
.
.
.
  non_secret_data:
  - name: DUO_INTEGRATION_KEY
    value: XXXXXXXXXXXXXXXXXX
  - name: DUO_API_HOST
    value: api-hist.duosecurity.com
.
.
.
  secret_data:
  - DUO_SECRET_KEY
  - DUO_A_KEY

Once you save this CR the operator will take over and start rolling out the new image with updated secret data.  If everything was done correctly, you’ll now need to have both your password and a DUO key to start accessing your cluster!

Lights, Camera, ACTION!

Here’s about a 12 minute video where we goo through the entire process from start to finish:

Ongoing Maintenance

Here’s the million dollar question: how do you upgrade this customized solution? When Trmeolo Security releases a new version of OpenUnison just update the version in your pom.xml file, rebuild and redeploy the container based on your own pipeline processes. We release new versions 2-3 times a year at least, updating libraries with each release. Keep an eye on our twitter feed for updates on the latest releases at @tremolosecurity.

Comments