Authenticate with Dex

Dex is an OpenID Connect identity hub. Dex can be used to expose a consistent OpenID Connect interface to your applications while allowing your users to authenticate using their existing credentials from various back-ends, including LDAP, SAML, and other OIDC providers. Using an identity hub like Dex has the advantage of allowing you to change your authentication back-ends without affecting the rest of the system. You can also use Dex for authentication to the Kubernetes API server itself; for example, to allow LDAP logins to work with kubectl. This is outside the scope of this document, but you can read more about it here.

In this guide we will see how to authenticate users with your application via an OIDC flow that uses Dex as an identity provider. This guide is just an example to get you started and does not cover all aspects of a complete setup, like setting up a domain and SSL certificates.

Setup

This guide assumes that you have deployed Gloo to the gloo-system namespace and that the glooctl command line utility is installed on your machine. glooctl provides several convenient functions to view, manipulate, and debug Gloo resources; in particular, it is worth mentioning the following command, which we will use each time we need to retrieve the URL of the Gloo Gateway that is running inside your cluster:

glooctl proxy url

Deploy sample application

The sample petclinic application deploys a MySql server. If you are using minikube v1.5 to run this guide, this service is likely to crash due a minikube issue. To get around this, you can start minikube with the following flag:

minikube start --docker-opt="default-ulimit=nofile=102400:102400" 

Let’s deploy a sample web application that we will use to demonstrate these features:

kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo/v0.8.4/example/petclinic/petclinic.yaml

Creating a Virtual Service

Now we can create a Virtual Service that routes all requests (note the / prefix) to the petclinic service.

apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: petclinic
  namespace: gloo-system
spec:
  virtualHost:
    domains:
    - '*'
    routes:
    - matcher:
        prefix: /
      routeAction:
        single:
          kube:
            ref:
              name: petclinic
              namespace: default
            port: 80

To verify that the Virtual Service has been accepted by Gloo, let’s port-forward the Gateway Proxy service so that it is reachable from your machine at localhost:8080:

kubectl -n gloo-system port-forward svc/gateway-proxy-v2 8080:80

If you open your browser and navigate to localhost:8080 you should see the following page (you might need to wait a minute for the containers to start):

Pet Clinic app homepage

Securing the Virtual Service

As we just saw, we were able to reach our application without having to provide any credentials. This is because by default Gloo allows any request on routes that do not specify authentication configuration. Let’s change this behavior. We will update the Virtual Service so that each request to the sample application is authenticated using an OpenID Connect flow.

Install Dex

To implement the authentication flow, we need an OpenID Connect provider to be running in your cluster. To this end, we will deploy the Dex identity service, as it easy to install and configure.

Let’s start by defining a dex-values.yaml Helm values file with some bootstrap configuration for Dex:

cat > dex-values.yaml <<EOF
config:
  # The base path of dex and the external name of the OpenID Connect service.
  # This is the canonical URL that all clients MUST use to refer to dex. If a
  # path is provided, dex's HTTP service will listen at a non-root URL.
  issuer: http://dex.gloo-system.svc.cluster.local:32000

  # Instead of reading from an external storage, use this list of clients.
  staticClients:
  - id: gloo
    redirectURIs:
    - 'http://localhost:8080/callback'
    name: 'GlooApp'
    secret: secretvalue
  
  # A static list of passwords to login the end user. By identifying here, dex
  # won't look in its underlying storage for passwords.
  staticPasswords:
  - email: "admin@example.com"
    # bcrypt hash of the string "password"
    hash: "\$2a\$10\$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
    username: "admin"
    userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
EOF

This configures Dex with a static users. Notice how we choose a client secret with value secretvalue for the client named gloo. Gloo will need to provide this secret when connecting to Dex in order to confirm its identity.

Using this configuration, we can deploy Dex to our cluster using Helm:

helm install --name dex --namespace gloo-system stable/dex -f dex-values.yaml

Make the client secret accessible to Gloo

To be able to act as our OIDC client, Gloo needs to have access to the client secret we defined in the Dex configuration, so that it can use it to identify itself with the Dex authorization server. Gloo expects the client secret to be stored in a specific format inside of a Kubernetes Secret.

Let’s create the secret and name it oauth:


glooctl create secret oauth --client-secret secretvalue oauth

apiVersion: v1
kind: Secret
type: Opaque
metadata:
  annotations:
    resource_kind: '*v1.Secret'
  name: oauth
  namespace: gloo-system
data:
  # The value is a base64 encoding of the following YAML:
  # config:
  #   client_secret: secretvalue
  # Gloo expects OAuth client secrets in this format.
  extension: Y29uZmlnOgogIGNsaWVudF9zZWNyZXQ6IHNlY3JldHZhbHVlCg==

Create an AuthConfig

The auth configuration format shown on this page was introduced with Gloo Enterprise, release 0.20.1. If you are using an earlier version, please refer to this page to see which configuration formats are supported by each version.

Now that all the necessary resources are in place we can create the AuthConfig resource that we will use to secure our Virtual Service.

apiVersion: enterprise.gloo.solo.io/v1
kind: AuthConfig
metadata:
  name: oidc-dex
  namespace: gloo-system
spec:
  configs:
  - oauth:
      app_url: http://localhost:8080/
      callback_path: /callback
      client_id: gloo
      client_secret_ref:
        name: oauth
        namespace: gloo-system
      issuer_url: http://dex.gloo-system.svc.cluster.local:32000/
      scopes:
      - email

The above configuration instructs Gloo to use its extauth OIDC module to authenticate the incoming request. Notice how the configuration references the client secret we created earlier and compare the configuration values with the ones we used to bootstrap Dex.

Update the Virtual Service

Once the AuthConfig has been created, we can use it to secure our Virtual Service:

apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: petclinic
  namespace: gloo-system
spec:
  virtualHost:
    domains:
    - '*'
    routes:
    - matcher:
        prefix: /
      routeAction:
        single:
          kube:
            ref:
              name: petclinic
              namespace: default
            port: 80
    virtualHostPlugins:
      extauth:
        config_ref:
          name: oidc-dex
          namespace: gloo-system

Testing our configuration

The OIDC flow redirects the client (in this case, your browser) to a login page hosted by Dex. Since Dex is running in your cluster and is not publicly reachable, we need some additional configuration to make our example work. Please note that this is just a workaround to reduce the amount of configuration necessary for this example to work.

  1. Port-forward the Dex service so that it is reachable from your machine at localhost:32000:

    kubectl -n gloo-system port-forward svc/dex 32000:32000 & 
    portForwardPid1=$! # Store the port-forward pid so we can kill the process later
  2. Add an entry to the /etc/hosts file on your machine, mapping the dex.gloo-system.svc.cluster.local hostname to your localhost (the loopback IP address 127.0.0.1).

    echo "127.0.0.1 dex.gloo-system.svc.cluster.local" | sudo tee -a /etc/hosts
  3. Port-forward the Gloo Gateway Proxy service so that it is reachable from your machine at localhost:8080:

    kubectl -n gloo-system port-forward svc/gateway-proxy-v2 8080:80 &
    portForwardPid2=$! # Store the port-forward pid so we can kill the process later
    

Now we are ready to test our complete setup! Open you browser and navigate to localhost:8080. You should see the following login page:

Dex login page

If you login as the admin@example.com user with the password password, Gloo should redirect you to the main page of our sample application!

Pet Clinic app homepage

Cleanup

You can clean up the resources created in this guide by running:

sudo sed '/127.0.0.1 dex.gloo-system.svc.cluster.local/d' /etc/hosts # remove line from hosts file
kill $portForwardPid1
kill $portForwardPid2
rm dex-values.yaml
helm delete --purge dex
kubectl delete -n gloo-system secret oauth dex-grpc-ca  dex-grpc-client-tls  dex-grpc-server-tls  dex-web-server-ca  dex-web-server-tls
kubectl delete virtualservice -n gloo-system petclinic
kubectl delete authconfig -n gloo-system oidc-dex
kubectl delete -f https://raw.githubusercontent.com/solo-io/gloo/v0.8.4/example/petclinic/petclinic.yaml