3 OIDC Configurations with Traefik Enterprise | Traefik Labs

Distributed systems require a strategic approach to authentication and authorization. As the number of components grows, controlling access to services becomes more and more complex. To alleviate this complexity, many organizations adopt the API gateway model, where security is standardized at a single point of ingress into a network.

In this blog post, we’ll walk through three configurations in which Traefik Enterprise is used to enforce access control via OpenID Connect (OIDC) — a solid choice for any authentication strategy. Each configuration example includes instructions, code, and video tutorials.

Setting up your environment

Installing Traefik Enterprise in Kubernetes

First, we’ll need to install Traefik Enterprise. For the sake of this blog post, we’ll assume everything is running in Kubernetes.

We’ll use Helm to install Traefik Enterprise:

helm repo add traefik https://helm.traefik.io/traefikee
helm repo update
helm upgrade --install traefikee traefik/traefikee \
  --namespace traefikee --create-namespace \
  --file values.yaml

We’ll also create a Secret that will store our license key.

kubectl create secret generic $CLUSTERNAME-license --from-literal=license="$TRAEFIKEE_LICENSE" -n traefikee

Finally, let’s create a ConfigMap storing our static configuration with kubectl apply -f static.yaml.

# static.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: default-static-config
  namespace: traefikee
data:
  static.yaml: |
    entryPoints:
      web:
        address: “:80”
        http:
          redirections:
            entryPoint:
              to: “websecure”
              scheme: “https”
      websecure:
        address: “:443”
    api:
      dashboard: true
    providers:
      kubernetesCRD: {}
    certificatesResolvers:
      default:
        acme:
          email: “admin@example.com”
          tlschallenge: {}
    authSources:
      oidcSource:
        oidc:
          issuer: “https://idp.example.com”
          clientID: “client-id”
          clientSecret: “client-secret”

This static config file does the following:

In addition to the above, we also declare an OIDC authSource. In Traefik Enterprise, authSources point to identity provider servers in order to delegate authentication and authorization. For OIDC, we define the IdP server’s URL, client ID, and client secret.

You can watch a more detailed installation video here.

Deploying whoami as a test service

Next, we’ll deploy Traefik’s whoami application as a standard Kubernetes Deployment and Service with kubectl apply -f whoami.yaml.

# whoami.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: whoami
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: whoami
  namespace: whoami
spec:
  replicas: 3
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - name: whoami
        image: traefik/whoami
        imagePullPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: whoami
  namespace: whoami
  labels:
    app: whoami
spec:
  type: ClusterIP
  ports:
  - port: 80
    name: whoami
  selector:
    app: whoami

To expose this application outside of our cluster, we’ll need to deploy an IngressRoute resource defining a routing rule with kubectl apply -f ingress.yaml:

# ingress.yaml
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: whoami
  namespace: whoami
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`whoami.example.com`)
      services:
        - name: whoami
          port: 80
  tls:
    certResolver: default

In this IngressRoute definition, we do the following:

  • Expose this application on the websecure entryPoint (port 443) only
  • Match any incoming requests with whoami.example.com in the Host header to the whoami Service
  • Enable automatic TLS termination through the default certResolver.

We should now be able to access our application at https://whoami.example.com.

OIDC configuration #1: authentication

Now that our application is accessible outside the cluster, we can add an OIDC middleware to limit access to authenticated users.

This middleware defines two main things. First, it references the authSource that we declared above in our static configuration. Second, it specifies the redirectUrl used to return to the application after the IdP has handled authentication. To attach this middleware to our application route, we’ll update our IngressRoute to reference it.

# ingress.yaml
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: whoami
  namespace: whoami
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`whoami.example.com`)
      services:
        - name: whoami
          port: 80
      middlewares:
        - name: oidc-authn
  tls:
    certResolver: default
# OIDC authN
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: oidc-authn
  namespace: whoami
spec:
  plugin:
    oidcAuth:
      source: oidcSource
      redirectUrl: "/callback"

With this middleware in place, users will be redirected to the IdP login page before being able to access the application.

OIDC configuration #2: authentication + authorization

So far, we’ve been able to expose our application and limit access only to authenticated users. However, it’s often useful to further limit access to a certain subset of those users that have the required permissions.

For example, suppose we want to expose this application only to members of an admin group within our IdP. To do this, we’ll add a claims option to our middleware. This option allows a number of different logical functions to check the contents of a given claim against a specified value. In this example, we check if the groups claim associated with a given user contains the admin group. This allows us to enforce authorization on top of the authentication introduced in the previous example.

We can also optionally include the forwardHeaders option to extract those claim values into a custom header to forward to the backend service.

# ingress.yaml
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: whoami
  namespace: whoami
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`whoami.example.com`)
      services:
        - name: whoami
          port: 80
      middlewares:
        - name: oidc-authz
  tls:
    certResolver: default
# OIDC authN + authZ
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: oidc-authz
  namespace: whoami
spec:
  plugin:
    oidcAuth:
      source: oidcSource
      redirectUrl: /callback
      claims: Contains(`groups`, `admin`)
      forwardHeaders:
        X-Traefik-Groups: groups

OIDC configuration #3: authentication + advanced authorization with Open Policy Agent (OPA) middleware

The claims functions can be combined with boolean operators to form complex logical statements. However, in some scenarios, it can be preferable to handle authentication and authorization in separate middlewares.

In this final example, we combine our existing OIDC middleware with an Open Policy Agent (OPA) middleware. Doing so allows us to leverage the full flexibility of the OPA Rego policy language.

In this OPA middleware, we embed our Rego policy directly in the body of the middleware. The policy decodes the JWT output by the OIDC middleware and checks the claims for group, username, and email. While the logic in this example is fairly straightforward, you can envision scenarios with much more complex business logic that can be handled with OPA.

# ingress.yaml
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: whoami
  namespace: whoami
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`whoami.example.com`)
      services:
        - name: whoami
          port: 80
      middlewares:
        - name: oidc-authn
        - name: opa
  tls:
    certResolver: default
# OIDC authN
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: oidc-authn
  namespace: whoami
spec:
  plugin:
    oidcAuth:
      source: oidcSource
      redirectUrl: "/callback"
# OPA
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: opa
  namespace: whoami
spec:
  plugin:
    opa:
      allow: data.example.authz.allow
      forwardHeaders:
        Group: data.example.authz.group
      policy: |
        package example.authz
        default allow = false
        default group = “”
        auth := split(input.headers.Authorization, “ “)
        jwtDecode := io.jwt.decode(auth[1])
        payload := jwtDecode[1]
        allow {
          payload[“email”] == “user@example.com”
          payload[“preferred_username”] == “user”
          payload[“groups”][_] == “admin”
        }

Conclusion

In this post, we’ve walked through how to deploy Traefik Enterprise as a unified ingress controller and API gateway in Kubernetes. We’ve exposed a sample application through Traefik’s dynamic routing rules, and added OIDC for user authentication. We’ve also explored how to add authorization using the OIDC middleware and the OPA middleware.

Using OIDC with Traefik Enterprise allows us to leverage an industry-standard authentication approach while consolidating access control at the ingress/API gateway. Following this model allows security teams to easily enforce IAM standards while regaining developer productivity at the same time.

Secure, manage, & scale all your APIs. Want to simplify API management and security? Request a demo today and see Traefik Enterprise in action.Request a demo

This is a companion discussion topic for the original entry at https://traefik.io/blog/3-oidc-configurations-with-traefik-enterprise/