3 OIDC Configurations with Traefik Enterprise, from Basic to Advanced
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: “[email protected]”
tlschallenge: {}
authSources:
oidcSource:
oidc:
issuer: “https://idp.example.com”
clientID: “client-id”
clientSecret: “client-secret”
This static config file does the following:
- Enables EntryPoints on ports 80 and 443, with a redirect from plain HTTP to HTTPS
- Enables the dashboard
- Enables the KubernetesCRD provider for service discovery through Kubernetes Custom Resources
- Creates a certificate resolver for automatic TLS certificate creation & renewal through Let’s Encrypt.
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”] == “[email protected]”
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.