TLDR; If you end up on this page, you probably realize that configuring domain-level Forward Auth from Traefik in K3S with Authentik 2024.2.2 is not easy. I show here how to do it.

Authentik is a popular identity provider which can be used in various authentication and authorisation flows. You can get more information about it on https://goauthentik.io/. There is a big list of possible integrations with known services, such as Portainer and Jenkins.

The other feature of Authentik is more interesting. It provides a capability to protect web-services from unauthorized access, even if these services do not support it originally. This is achieved by intercepting incoming requests and testing if they contain a proper http-header. In case no such header is found, the request is redirected to a login page of Authentik.

It sounds great and doable. There are plenty of resources describing the configuration process. Unfortunately, I did not find a solution for K3S with Traefik. Getting it work cost me a lot of time and patience. The official documentation does not describe this case. So I would like to share this knowledge.

To be on the same page, here are the prerequisites:
1. Your instance of Authentik is deployed at https://authentik.domain1
2. The service you would like to protect is deployed in a K3S and available at https://service.domain2
3. A future Authentik Outpost will be accessible at https://outpost.domain3.
Please note, that domain1, domain2 and domain3 do not need to be different. They all can use 1 domain and be deployed in the same K3S cluster.

First of all, we install an Authentik Outpost using a helm chart from https://artifacthub.io/packages/helm/goauthentik/authentik-remote-cluster. The installation process ends with a kube-config. This config is reqiured to create an outpost integration in Authentik.

Later, you can use the integration to create an outpost for an app of your choice. The created outpost will automatically trigger a new ghcr.io/goauthentik/proxy deployment in the cluster which will connect to Authentik.

Here is a catch! The outpost needs to be exposed at https://outpost.domain3. There is a ping endpoint which must be testable via:
curl -i https://outpost.domain3/outpost.goauthentik.io/ping. A correct response contains HTTP/2 204. One way to expose the outpost is using Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: outpost-ingress
spec:
  ingressClassName: traefik
  rules:
  - host: outpost.domain3
    http:
      paths:
      - backend:
          service:
            name: ak-outpost
            port:
              number: 9000
        path: /
        pathType: Prefix

Finally, the target web-service needs to be re-configured to use the following Traefik Middleware with the exposed outpost:

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: authentik
spec:
  forwardAuth:
    address: https://outpost.domain3/outpost.goauthentik.io/auth/traefik
    trustForwardHeader: true
    authResponseHeaders:
      - X-authentik-username
      - X-authentik-groups
      - X-authentik-email
      - X-authentik-name
      - X-authentik-uid
      - X-authentik-jwt
      - X-authentik-meta-jwks
      - X-authentik-meta-outpost
      - X-authentik-meta-provider
      - X-authentik-meta-app
      - X-authentik-meta-version

The target web-service can be exposed via:

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whoami-ingress
  annotations:
    traefik.ingress.kubernetes.io/router.middlewares: default-authentik@kubernetescrd
spec:
  ingressClassName: traefik
  rules:
    - host: https://service.domain2
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: whoami
                port:
                  number: 80

This configuration works for domain-level forward auth, where an auth cookie is set for the whole domain2 and all services within the domain, e.g. service2.domain2.

Single app forward auth is also possible, but needs changes to Ingress. Let me know if you are interested.