There is an easy way to use base-auth for “securing” ingress access in Traefik, but, well - it’s base auth. It’s not that secure nor easy / nice to use in browsers, especially on mobile devices. So for long time, I was thinking how to integrate Traefik Ingress with an SSO (Single Sign-On) solution.

Prerequisites

You will need some kind of SSO provider. I’m using Forgejo (a fork of Gitea). I didn’t need anything fancy, and I already had Gitea working, which had the OAuth2 provider out of the box. Probably something dedicated would be better, like Authelia, Keycloak, Authentik, which have more features. But for my purposes (a single user for most of the services), the limited functionality of Forgejo is sufficient and simple to use.

For the purpose of this article, I’ll assume you are using Forgejo, but you can use any SSO provider, even Google (though there will be some slightly different configuration needed for traefik). For OAuth2 providers, we need following values:

  • auth URL (https://forgejo/login/oauth/authorize)
  • token URL (https://forgejo/login/oauth/access_token)
  • user URL (https://forgejo/api/v1/user)
  • client ID
  • client secret
  • secret (a random string)

To obtain the client ID and secret from Forgejo we need to create new Application in User / Settings. Specify a meaning full Application Name and use the destination URL with /_oauth suffix as the Redirect URI (otherwise, it will not work).

Forgejo settings

Base application

Simply deploy anything with a service and ingress to protect. Even a bare Nginx server is enough to start with.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  labels:
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: whoami
  labels:
    app: whoami
spec:
  ports:
    - name: http
      port: 80
  selector:
    app: whoami

Traefik middleware

Now that we have SSO configured and an application to protect, it’s time to connect the pieces. For this purpose, we’ll use traefik-forward-auth middleware. Let’s start by preparing the secrets. Encode the client ID, client secret and secret in base64 and use them in a secret resource:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: Secret
metadata:
  name: traefik-forward-auth-secrets
  labels:
    app: traefik-forward-auth
type: Opaque
data:
  oauth-client-id: base64-string
  oauth-client-secret: base64-string
  auth-secret: base64-string

Next, deploy forward-auth resources:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
apiVersion: apps/v1
kind: Deployment
metadata:
  name: traefik-forward-auth
  labels:
    app: traefik-forward-auth
spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik-forward-auth
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: traefik-forward-auth
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - image: thomseddon/traefik-forward-auth:2
          name: traefik-forward-auth
          resources:
            limits:
              cpu: 100m
              memory: 128Mi
          ports:
            - containerPort: 4181
              protocol: TCP
          env:
            # Set DOMAIN if you want to limit users from one domain, since in article we use Forgejo and want to allow
            # all users it can be unset
            # - name: DOMAIN
            #   value: "email.domain.com"
            # INSECURE_COOKIE is required unless using https entrypoint
            # - name: INSECURE_COOKIE
            #   value: "true"
            - name: DEFAULT_PROVIDER
              value: "generic-oauth"
            - name: PROVIDERS_GENERIC_OAUTH_AUTH_URL
              value: "https://forgejo/login/oauth/authorize"
            - name: PROVIDERS_GENERIC_OAUTH_TOKEN_URL
              value: "https://forgejo/login/oauth/access_token"
            - name: PROVIDERS_GENERIC_OAUTH_USER_URL
              value: "https://forgejo/api/v1/user"
            - name: PROVIDERS_GENERIC_OAUTH_CLIENT_ID
              valueFrom:
                secretKeyRef:
                  name: traefik-forward-auth-secrets
                  key: oauth-client-id
            - name: PROVIDERS_GENERIC_OAUTH_CLIENT_SECRET
              valueFrom:
                secretKeyRef:
                  name: traefik-forward-auth-secrets
                  key: oauth-client-secret
            - name: PROVIDERS_GENERIC_OAUTH_TOKEN_STYLE
              value: "query"
            - name: PROVIDERS_GENERIC_OAUTH_SCOPE
              value: openid,profile
            - name: SECRET
              valueFrom:
                secretKeyRef:
                  name: traefik-forward-auth-secrets
                  key: auth-secret

---
apiVersion: v1
kind: Service
metadata:
  name: traefik-forward-auth
  labels:
    app: traefik-forward-auth
spec:
  type: ClusterIP
  selector:
    app: traefik-forward-auth
  ports:
    - name: auth-http
      port: 4181
      targetPort: 4181

---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: traefik-forward-auth
spec:
  forwardAuth:
    address: http://traefik-forward-auth.test.svc.cluster.local:4181
    authResponseHeaders:
      - X-Forwarded-User

Finally, create an ingress resource that points to our application and uses the middleware we created earlier. Note that the order in traefik.ingress.kubernetes.io/router.middlewares matters, and if you are using middleware for HTTP-to-HTTPS redirection - it has to be listed first. The name test-traefik-forward-auth@kubernetescrd is generated using namespace name test - dash - middleware name traefik-forward-auth.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
    traefik.ingress.kubernetes.io/router.middlewares: default-https@kubernetescrd,test-traefik-forward-auth@kubernetescrd
  name: whoami
spec:
  rules:
    - host: test.example.com
      http:
        paths:
          - backend:
              service:
                name: whoami
                port:
                  number: 80
            path: /
            pathType: Prefix
  tls:
    - hosts:
        - test.example.com
      secretName: whoami-tls

Summary

That’s it! We have now secured access to test.example.com with Forgejo SSO. To logout just go to test.example.com/_oauth/logout.