AuthService

Introduction

What is the problem?

The traditional way of authenticating users is to include authentication code inside the application. However, in the microservice world, this approach becomes repetitive and prone to error, as similar logic has to be implemented over and over.

There is a need to consolidate authentication in one place. What if we could perform all common authentication logic in a proxy in front of the application and just let the application know the final result? For example, the proxy could use a standardized flow to authenticate users and propagate the authentication’s result to the application in HTTP headers. This allows for the authentication logic to be implemented once and reused across applications.

Cloud-Native Proxies

Modern proxies (e.g., Envoy) allow the user to extend their functionality. One common extension they allow is to call into an external service to ask if a request is allowed or denied. This is especially useful since it allows extending existing proxies of a Service Mesh (e.g., Istio) with the functionality described above. In Envoy, this extension interface is called the envoy.ext_authz filter. So instead of writing our own proxy, we can implement this interface and extend existing proxies, lowering the barrier-to-entry and improving efficiency.

The AuthService

The AuthService is an implementation of Envoy’s ext_authz interface. For every request that reaches Envoy, it will make a request to the AuthService to ask if that request is allowed.

Istio-Gateway Integration

High-Level Overview

After understanding what the AuthService is, we will see a more practical example of how it is integrated in our deployments, using the Istio Gateway. The Istio Gateway is the point where external traffic enters the Istio Service Mesh. It uses an Envoy Proxy, which we configure with an ext_authz filter to talk to the AuthService. Here is a diagram of what this looks like in Kubeflow:

../_images/authservice_kubeflow.png

Installation

Important

This guide assumes that the end user has access to a local clone of Arrikto provided deployments repo.

Configure Authentication

In order to securely deploy our application, we need to configure our OIDC Client (AuthService) and OIDC Provider (Dex) accordingly.

Specifically:

  • Change password of default user
  • Change credentials of OIDC client

By default, Dex is installed with a single static user. To change the default user’s password or create new users, one has to modify Dex’s ConfigMap. To change the password of the default user:

  1. Pick a password for the default user, with handle user, and hash it using bcrypt:

    $ python3 -c 'from passlib.hash import bcrypt; import getpass; print(bcrypt.using(rounds=12, ident="2y").hash(getpass.getpass()))'
    
  2. Edit rok/rok-external-services/dex/base/config-map.yaml and fill the relevant field with the hash of the password you chose:

    ...
      staticPasswords:
      - email: user
        hash: <enter the generated hash here>
    
  3. Generate OIDC Client credentials for the AuthService. These credentials are used by the AuthService to authenticate to Dex. The credentials must be filled in both Dex and AuthService kustomizations:

    $ export OIDC_CLIENT_ID="authservice"
    $ export OIDC_CLIENT_SECRET="$(openssl rand -base64 32)"
    $ envsubst < rok/rok-external-services/dex/base/secret_params.env.in > rok/rok-external-services/dex/base/secret_params.env
    $ envsubst < rok/rok-external-services/authservice/base/secret_params.env.in > rok/rok-external-services/authservice/base/secret_params.env
    

Deploy

Now that we know what the end result should look like, we can plan out our installation. This section is about installing the AuthService at the Istio Gateway and having your application live behind it. That application can be Kubeflow, Rok, etc. In this guide, we assume you have already installed Rok and Istio in your Kubernetes cluster and that both a Gateway and VirtualService exist to expose Rok to the outer world.

Note

You should be able to access your application through the Istio Gateway. For Rok, since we expose it under /rok/, visiting <gateway_url>/rok should show the Rok UI.

For the AuthService you also need to install an OIDC Provider, which is Dex in our case.

  1. Install an OIDC Provider. Dex is a good choice because it’s easy to get started and also supports statically defined users for demo purposes. To install Dex:

    $ kubectl apply -k rok/rok-external-services/dex/overlays/istio
    
  2. Deploy the AuthService. At this point we have everything working, except our application isn’t secure, because all requests are allowed. To mitigate that, will enable the AuthService on the Istio Gateway’s proxy. To install the AuthService:

    $ kubectl apply -k rok/rok-external-services/authservice/base
    

AuthService Configuration

The AuthService is configured through environment variables, defined in a ConfigMap called oidc-authservice-parameters. An exhaustive list of configuration options along with their default values and explanations can be found in the AuthService README.

Istio Gateway Integration

The Istio Gateway runs an Envoy proxy, which we configure with an ext_authz filter pointing to our AuthService. To do that, we use the EnvoyFilter object of Istio. We include an example VirtualService below, with inline comments to explain relevant parts of the configuration:

# EnvoyFilter is an Istio CRD for configuring the underlying Envoy proxy.
# For configuration options, see Istio docs:
# https://istio.io/docs/reference/config/networking/envoy-filter/
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: authn-filter
  namespace: istio-system
spec:
  filters:
  # Filled based on Envoy documentation:
  # https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/ext_authz/v3/ext_authz.proto#envoy-v3-api-msg-extensions-filters-network-ext-authz-v3-extauthz
  - filterConfig:
      httpService:
        authorizationRequest:
          allowedHeaders:
            patterns:
            - exact: cookie
            - exact: X-Auth-Token
        authorizationResponse:
          allowedUpstreamHeaders:
            patterns:
            - exact: kubeflow-userid
        serverUri:
          cluster: outbound|8080||authservice.istio-system.svc.cluster.local
          failureModeAllow: false
          timeout: 10s
          uri: http://authservice.istio-system.svc.cluster.local
      statusOnError:
        code: GatewayTimeout
    # Add the envoy.ext_authz filter
    filterName: envoy.ext_authz
    filterType: HTTP
    insertPosition:
      index: FIRST
    # Only match Gateway proxies
    listenerMatch:
      listenerType: GATEWAY
  # Match proxy pods with the following labels
  workloadLabels:
    istio: ingressgateway