├── README.md ├── cert-manager-issuer.yml ├── cert-manager.yml ├── dex.yml ├── forward-auth.png ├── forward-auth.yml ├── github-credentials.yml ├── microbot.yml ├── traefik-crds.yml ├── traefik.yml └── whoami.yml /README.md: -------------------------------------------------------------------------------- 1 | # Traefik-Forward-Auth Example Setup 2 | 3 | This repo shows an example of a full-cycle setup using Traefik, Traefik-Forward-Auth, and Dex to put Github authentication in front of an arbitrary Kubernetes web service. 4 | 5 | This is not a specific turnkey solution, but rather an interactive example to adapt to your own needs. Notably if your authentication provider supports OpenID Connect natively (or if you are using Google Authentication), you can entirely skip Dex and have traefik-forward-auth talk to the provider directly. 6 | 7 | ## The Components 8 | 9 | * [Traefik](https://github.com/containous/traefik) - A proxy server that also serves as a Kubernetes Ingress Controller. 10 | * [Traefik-Forward-Auth](https://github.com/thomseddon/traefik-forward-auth) - An adapter between the forward-auth protocol and OpenID Connect. 11 | * [Dex](https://github.com/dexidp/dex) - An OpenID Connect server that supports numerous authentication backends, in this case Github OAuth. 12 | * [Cert-Manager](https://github.com/jetstack/cert-manager) - Playing a supporting role, creating and managing TLS certificates. 13 | * Whoami and Microbot - Two example applications, one using the normal Ingress resource, the other the custom IngressRoute. 14 | 15 | Each of these is set up in a YAML manifest, with annotations in line. 16 | 17 | ## Request Flow 18 | 19 | ![HTTP request flow](forward-auth.png) 20 | 21 | ## Questions? 22 | 23 | You can find me as `@coderanger` on the Kubernetes Slack. 24 | 25 | ## License 26 | 27 | Except for the cert-manager installation manifest which is provided under the original Apache license: 28 | 29 | To the extent possible under law, I waive all copyright and related or neighboring rights to this work. 30 | -------------------------------------------------------------------------------- /cert-manager-issuer.yml: -------------------------------------------------------------------------------- 1 | # A global selfsigned issuer, used for settings up CA certs. 2 | apiVersion: cert-manager.io/v1alpha2 3 | kind: ClusterIssuer 4 | metadata: 5 | name: selfsigned 6 | spec: 7 | selfSigned: {} 8 | --- 9 | # Create the CA cert for the local-ca issuer. 10 | apiVersion: cert-manager.io/v1alpha2 11 | kind: Certificate 12 | metadata: 13 | name: local-ca 14 | namespace: cert-manager 15 | spec: 16 | secretName: local-ca 17 | commonName: local-ca 18 | isCA: true 19 | issuerRef: 20 | kind: ClusterIssuer 21 | name: selfsigned 22 | usages: 23 | - any 24 | --- 25 | # A global issuer using the local CA keypair. 26 | apiVersion: cert-manager.io/v1alpha2 27 | kind: ClusterIssuer 28 | metadata: 29 | name: local-ca 30 | spec: 31 | ca: 32 | secretName: local-ca 33 | -------------------------------------------------------------------------------- /dex.yml: -------------------------------------------------------------------------------- 1 | # A namespace for Dex to live in. 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: dex 6 | --- 7 | # Run Dex. 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: dex 12 | namespace: dex 13 | spec: 14 | # In a real scenario, consider the uptime requirements of things behind Dex authentication to decide how many replicas you need. 15 | # If also using it for Kubernetes authentication, you may want to use a DaemonSet instead for maximum redundancy. 16 | replicas: 1 17 | selector: 18 | matchLabels: 19 | app: dex 20 | template: 21 | metadata: 22 | labels: 23 | app: dex 24 | spec: 25 | # Give it Kubernetes API access for session data storage. 26 | serviceAccountName: dex 27 | containers: 28 | - name: dex 29 | image: quay.io/dexidp/dex:v2.24.0 30 | command: [/usr/local/bin/dex, serve, /cfg/config.yaml] 31 | ports: 32 | - containerPort: 5556 33 | envFrom: 34 | - secretRef: 35 | name: github-credentials 36 | volumeMounts: 37 | - name: config 38 | mountPath: /cfg 39 | - name: tls 40 | mountPath: /tls 41 | volumes: 42 | - name: config 43 | configMap: 44 | name: dex 45 | - name: tls 46 | secret: 47 | secretName: dex-tls 48 | --- 49 | # Dex configuration file. 50 | kind: ConfigMap 51 | apiVersion: v1 52 | metadata: 53 | name: dex 54 | namespace: dex 55 | data: 56 | config.yaml: | 57 | # This issuer URI must exactly match the one configured in traefik-forward-auth. 58 | issuer: https://dex.dex:5556/ 59 | # WARNING THIS WILL NOT WORK FOR YOU. Pending https://github.com/dexidp/dex/pull/1722. 60 | # Until then you need to use a public-facing URL for `issuer` instead. 61 | publicURL: https://dex.lvh.me/ 62 | 63 | # Store session data in Kubernetes custom objects so we don't need a database. 64 | storage: 65 | type: kubernetes 66 | config: 67 | inCluster: true 68 | 69 | web: 70 | https: 0.0.0.0:5556 71 | tlsCert: /tls/tls.crt 72 | tlsKey: /tls/tls.key 73 | 74 | # Here is where you would configure your authentication backend. 75 | # See https://github.com/dexidp/dex#connectors for a list of supported connectors. 76 | connectors: 77 | - type: github 78 | id: github 79 | name: GitHub 80 | config: 81 | clientID: $GITHUB_CLIENT_ID 82 | clientSecret: $GITHUB_CLIENT_SECRET 83 | redirectURI: https://dex.lvh.me/callback 84 | 85 | oauth2: 86 | skipApprovalScreen: true 87 | 88 | # This is internal client config for traefik-forward-auth. 89 | staticClients: 90 | - name: Traefik Forward Auth 91 | id: forward-auth 92 | # An arbitrary secret value, make sure you change this if using this example 93 | # for real, and then put the matching value in forward-auth.yml too. 94 | secret: 6bbc3f2a6d55a3917e068dff5b07a476 95 | redirectURIs: 96 | - http://auth.lvh.me/_oauth 97 | - https://auth.lvh.me/_oauth 98 | --- 99 | # TLS certificate for Dex itself, from local certificate authority. 100 | apiVersion: cert-manager.io/v1alpha2 101 | kind: Certificate 102 | metadata: 103 | name: dex-tls 104 | namespace: dex 105 | spec: 106 | secretName: dex-tls 107 | issuerRef: 108 | kind: ClusterIssuer 109 | name: local-ca 110 | dnsNames: 111 | - dex 112 | - dex.dex 113 | - dex.dex.svc.cluster.local 114 | --- 115 | # A service for both internal use and for the Ingress. 116 | apiVersion: v1 117 | kind: Service 118 | metadata: 119 | name: dex 120 | namespace: dex 121 | spec: 122 | ports: 123 | # Use `https` as the port name so Traefik knows to use that for talking to the pod. 124 | - name: https 125 | port: 5556 126 | selector: 127 | app: dex 128 | --- 129 | # Expose Dex to the internet for OAuth initiation and callbacks. 130 | apiVersion: networking.k8s.io/v1beta1 131 | kind: Ingress 132 | metadata: 133 | name: dex 134 | namespace: dex 135 | spec: 136 | rules: 137 | - host: dex.lvh.me 138 | http: 139 | paths: 140 | - path: / 141 | backend: 142 | serviceName: dex 143 | servicePort: https 144 | --- 145 | # A service account so Dex can connect to the Kubernetes API to store session management data. 146 | apiVersion: v1 147 | kind: ServiceAccount 148 | metadata: 149 | name: dex 150 | namespace: dex 151 | --- 152 | # An RBAC role for use with the above service account. 153 | apiVersion: rbac.authorization.k8s.io/v1beta1 154 | kind: ClusterRole 155 | metadata: 156 | name: dex 157 | rules: 158 | # Read-write access to all of Dex's custom objects, used for session management. 159 | - apiGroups: [dex.coreos.com] # API group created by dex 160 | resources: ["*"] 161 | verbs: ["*"] 162 | # Access to install the CRDs for Dex's custom objects. 163 | - apiGroups: [apiextensions.k8s.io] 164 | resources: [customresourcedefinitions] 165 | verbs: [create] 166 | --- 167 | # Bind the service account and the RBAC role together. 168 | apiVersion: rbac.authorization.k8s.io/v1beta1 169 | kind: ClusterRoleBinding 170 | metadata: 171 | name: dex 172 | roleRef: 173 | apiGroup: rbac.authorization.k8s.io 174 | kind: ClusterRole 175 | name: dex 176 | subjects: 177 | - kind: ServiceAccount 178 | name: dex 179 | namespace: dex 180 | -------------------------------------------------------------------------------- /forward-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderanger/traefik-forward-auth-dex/da8fd51cd49c3c22b4746c9918bb06c9bf7def8b/forward-auth.png -------------------------------------------------------------------------------- /forward-auth.yml: -------------------------------------------------------------------------------- 1 | # Run forward-auth. 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: forward-auth 6 | namespace: traefik 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: forward-auth 11 | template: 12 | metadata: 13 | labels: 14 | app: forward-auth 15 | spec: 16 | containers: 17 | - name: forward-auth 18 | image: thomseddon/traefik-forward-auth:latest 19 | ports: 20 | - containerPort: 4181 21 | args: 22 | # Random value used for encrypting cookies, pick a new one. 23 | - --secret=bc989147a1d2ff433933b39b40b9b1d0 24 | # The only hard rule of auth-host mode is that the auth-host must be a subdomain of cookie-domain. 25 | - --auth-host=auth.lvh.me 26 | - --cookie-domain=lvh.me 27 | # If using either Google auth or a provider that supports OIDC directly, change these values. 28 | - --default-provider=oidc 29 | - --providers.oidc.issuer-url=https://dex.dex:5556/ 30 | - --providers.oidc.client-id=forward-auth 31 | - --providers.oidc.client-secret=6bbc3f2a6d55a3917e068dff5b07a476 # Must match Dex's staticClient. 32 | # Insert the local CA cert into the trust store. 33 | volumeMounts: 34 | - name: tls 35 | mountPath: /etc/ssl/certs/dex.crt 36 | subPath: ca.crt 37 | volumes: 38 | - name: tls 39 | secret: 40 | secretName: forward-auth-tls 41 | --- 42 | # Service for Traefik to talk to Forward-Auth. 43 | apiVersion: v1 44 | kind: Service 45 | metadata: 46 | name: forward-auth 47 | namespace: traefik 48 | spec: 49 | selector: 50 | app: forward-auth 51 | ports: 52 | - port: 4181 53 | --- 54 | # Expose forward-auth to the internet for OAuth initialization and callbacks. 55 | apiVersion: networking.k8s.io/v1beta1 56 | kind: Ingress 57 | metadata: 58 | name: forward-auth 59 | namespace: traefik 60 | annotations: 61 | # While possibly unexpected, this is required for the OAuth callback to process correctly. 62 | # It will never actually return a 200 so it's mostly just a protocol issue. 63 | traefik.ingress.kubernetes.io/router.middlewares: traefik-forward-auth-dex@kubernetescrd 64 | spec: 65 | rules: 66 | - host: auth.lvh.me 67 | http: 68 | paths: 69 | - path: / 70 | backend: 71 | serviceName: forward-auth 72 | servicePort: 4181 73 | --- 74 | # Register a middleware with Traefik to use this forward-auth instance. 75 | apiVersion: traefik.containo.us/v1alpha1 76 | kind: Middleware 77 | metadata: 78 | name: forward-auth-dex 79 | namespace: traefik 80 | spec: 81 | forwardAuth: 82 | address: http://forward-auth.traefik:4181 83 | authResponseHeaders: 84 | - X-Forwarded-User 85 | --- 86 | # Traefik-forward-auth itself does not yet support TLS, but we still need to grab the ca.crt for talking to Dex. 87 | apiVersion: cert-manager.io/v1alpha2 88 | kind: Certificate 89 | metadata: 90 | name: forward-auth-tls 91 | namespace: traefik 92 | spec: 93 | secretName: forward-auth-tls 94 | issuerRef: 95 | kind: ClusterIssuer 96 | name: local-ca 97 | dnsNames: 98 | - unused 99 | -------------------------------------------------------------------------------- /github-credentials.yml: -------------------------------------------------------------------------------- 1 | # Github OAuth app credentials. 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: github-credentials 6 | namespace: dex 7 | stringData: 8 | # To try this yourself, you will need to register your own app. 9 | GITHUB_CLIENT_ID: xxxxx 10 | GITHUB_CLIENT_SECRET: xxxxx 11 | -------------------------------------------------------------------------------- /microbot.yml: -------------------------------------------------------------------------------- 1 | # Example app using the microbot image. 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: microbot 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: microbot 10 | template: 11 | metadata: 12 | labels: 13 | app: microbot 14 | spec: 15 | containers: 16 | - name: microbot 17 | image: dontrebootme/microbot:v1 18 | ports: 19 | - containerPort: 80 20 | --- 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: microbot 25 | spec: 26 | selector: 27 | app: microbot 28 | ports: 29 | - port: 80 30 | --- 31 | # Example of configuring forward-auth middleware on an Ingress. 32 | apiVersion: networking.k8s.io/v1beta1 33 | kind: Ingress 34 | metadata: 35 | name: microbot 36 | annotations: 37 | traefik.ingress.kubernetes.io/router.middlewares: traefik-forward-auth-dex@kubernetescrd 38 | spec: 39 | rules: 40 | - host: microbot.lvh.me 41 | http: 42 | paths: 43 | - path: / 44 | backend: 45 | serviceName: microbot 46 | servicePort: 80 47 | -------------------------------------------------------------------------------- /traefik-crds.yml: -------------------------------------------------------------------------------- 1 | # Custom resource definitions for Traefik. 2 | apiVersion: apiextensions.k8s.io/v1beta1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: ingressroutes.traefik.containo.us 6 | spec: 7 | group: traefik.containo.us 8 | version: v1alpha1 9 | names: 10 | kind: IngressRoute 11 | plural: ingressroutes 12 | singular: ingressroute 13 | scope: Namespaced 14 | --- 15 | apiVersion: apiextensions.k8s.io/v1beta1 16 | kind: CustomResourceDefinition 17 | metadata: 18 | name: ingressroutetcps.traefik.containo.us 19 | spec: 20 | group: traefik.containo.us 21 | version: v1alpha1 22 | names: 23 | kind: IngressRouteTCP 24 | plural: ingressroutetcps 25 | singular: ingressroutetcp 26 | scope: Namespaced 27 | --- 28 | apiVersion: apiextensions.k8s.io/v1beta1 29 | kind: CustomResourceDefinition 30 | metadata: 31 | name: ingressrouteudps.traefik.containo.us 32 | spec: 33 | group: traefik.containo.us 34 | version: v1alpha1 35 | names: 36 | kind: IngressRouteUDP 37 | plural: ingressrouteudps 38 | singular: ingressrouteudp 39 | scope: Namespaced 40 | --- 41 | apiVersion: apiextensions.k8s.io/v1beta1 42 | kind: CustomResourceDefinition 43 | metadata: 44 | name: middlewares.traefik.containo.us 45 | spec: 46 | group: traefik.containo.us 47 | version: v1alpha1 48 | names: 49 | kind: Middleware 50 | plural: middlewares 51 | singular: middleware 52 | scope: Namespaced 53 | --- 54 | apiVersion: apiextensions.k8s.io/v1beta1 55 | kind: CustomResourceDefinition 56 | metadata: 57 | name: tlsoptions.traefik.containo.us 58 | spec: 59 | group: traefik.containo.us 60 | version: v1alpha1 61 | names: 62 | kind: TLSOption 63 | plural: tlsoptions 64 | singular: tlsoption 65 | scope: Namespaced 66 | --- 67 | apiVersion: apiextensions.k8s.io/v1beta1 68 | kind: CustomResourceDefinition 69 | metadata: 70 | name: tlsstores.traefik.containo.us 71 | spec: 72 | group: traefik.containo.us 73 | version: v1alpha1 74 | names: 75 | kind: TLSStore 76 | plural: tlsstores 77 | singular: tlsstore 78 | scope: Namespaced 79 | --- 80 | apiVersion: apiextensions.k8s.io/v1beta1 81 | kind: CustomResourceDefinition 82 | metadata: 83 | name: traefikservices.traefik.containo.us 84 | spec: 85 | group: traefik.containo.us 86 | version: v1alpha1 87 | names: 88 | kind: TraefikService 89 | plural: traefikservices 90 | singular: traefikservice 91 | scope: Namespaced 92 | -------------------------------------------------------------------------------- /traefik.yml: -------------------------------------------------------------------------------- 1 | # Namespace for Traefik to live in. 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: traefik 6 | --- 7 | # A service account so Traefik can connect to the Kubernetes API to discover routes. 8 | apiVersion: v1 9 | kind: ServiceAccount 10 | metadata: 11 | name: traefik 12 | namespace: traefik 13 | --- 14 | # An RBAC role for use with the above service account. 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRole 17 | metadata: 18 | name: traefik 19 | rules: 20 | # Allow read-only access to services/endpoints for service discovery, and secrets for TLS keys. 21 | - apiGroups: [""] 22 | resources: [services, endpoints, secrets] 23 | verbs: [get, list, watch] 24 | # Allow read-only access to ingresses for dynamic discovery there too. 25 | - apiGroups: [extensions] 26 | resources: [ingresses] 27 | verbs: [get, list, watch] 28 | # Allow writing to the ingress status data to update the external IP field. 29 | - apiGroups: [extensions] 30 | resources: [ingresses/status] 31 | verbs: [update] 32 | # Allow read-only access to all Traefik custom resources. 33 | - apiGroups: [traefik.containo.us] 34 | resources: ["*"] 35 | verbs: [get, list, watch] 36 | --- 37 | # Bind the service account and the RBAC role together. 38 | kind: ClusterRoleBinding 39 | apiVersion: rbac.authorization.k8s.io/v1 40 | metadata: 41 | name: traefik 42 | roleRef: 43 | apiGroup: rbac.authorization.k8s.io 44 | kind: ClusterRole 45 | name: traefik 46 | subjects: 47 | - kind: ServiceAccount 48 | name: traefik 49 | namespace: traefik 50 | --- 51 | # A service to expose Traefik to the outside world. 52 | apiVersion: v1 53 | kind: Service 54 | metadata: 55 | name: traefik 56 | namespace: traefik 57 | spec: 58 | # Use a LoadBalancer type to get a single public IP. 59 | type: LoadBalancer 60 | selector: 61 | app: traefik 62 | # Expose both HTTP and HTTPS. 63 | ports: 64 | - port: 80 65 | name: http 66 | targetPort: http 67 | - port: 443 68 | name: https 69 | targetPort: https 70 | --- 71 | # Traefik config file as a ConfigMap, mounted into the container below. 72 | apiVersion: v1 73 | kind: ConfigMap 74 | metadata: 75 | name: traefik-config 76 | namespace: traefik 77 | data: 78 | traefik.toml: | 79 | # Don't talk to the network on startup. 80 | [global] 81 | checkNewVersion = false 82 | sendAnonymousUsage = false 83 | 84 | # Unfortunately Traefik makes it impossible to validate backend certs as it uses the 85 | # pod IP to connect, not a name. Tracked in https://github.com/containous/traefik/issues/4835. 86 | [serversTransport] 87 | insecureSkipVerify = true 88 | 89 | # Internal, HTTP, and HTTPS entrypoints. 90 | [entryPoints.traefik] 91 | address = ":9000" 92 | [entryPoints.http] 93 | address = ":8000" 94 | [entryPoints.https] 95 | address = ":8443" 96 | [entryPoints.https.http.tls] 97 | certResolver = "default" 98 | 99 | # Enable the API and dashboard. 100 | [api] 101 | 102 | # Enable the health check endpoint. 103 | [ping] 104 | 105 | # Discover routes from IngressRoute custom objects. 106 | [providers.kubernetescrd] 107 | 108 | # Discover routes from Ingress objects. 109 | [providers.kubernetesingress] 110 | [providers.kubernetesIngress.ingressEndpoint] 111 | publishedService = "traefik/traefik" 112 | --- 113 | # Run Traefik! 114 | apiVersion: apps/v1 115 | kind: Deployment 116 | metadata: 117 | name: traefik 118 | namespace: traefik 119 | spec: 120 | replicas: 1 # In a real deployment, set this to 2 or more for redundancy. 121 | selector: 122 | matchLabels: 123 | app: traefik 124 | template: 125 | metadata: 126 | labels: 127 | app: traefik 128 | spec: 129 | # Use the service account we set up before. 130 | serviceAccountName: traefik 131 | containers: 132 | - name: traefik 133 | image: traefik:2.2.1 134 | readinessProbe: 135 | httpGet: 136 | path: /ping 137 | port: traefik 138 | failureThreshold: 1 139 | initialDelaySeconds: 10 140 | timeoutSeconds: 2 141 | livenessProbe: 142 | httpGet: 143 | path: /ping 144 | port: traefik 145 | initialDelaySeconds: 10 146 | timeoutSeconds: 2 147 | # Expose all three entrypoints defined in the config. 148 | ports: 149 | - name: traefik 150 | containerPort: 9000 151 | - name: http 152 | containerPort: 8000 153 | - name: https 154 | containerPort: 8443 155 | # Mount in the config file. 156 | volumeMounts: 157 | - name: config 158 | mountPath: /etc/traefik 159 | # Per-container security settings, drop as many permissions as possible. 160 | securityContext: 161 | capabilities: 162 | drop: 163 | - ALL 164 | readOnlyRootFilesystem: true 165 | runAsGroup: 65532 166 | runAsNonRoot: true 167 | runAsUser: 65532 168 | # Pod level security settings. 169 | securityContext: 170 | fsGroup: 65532 171 | # Wait a little longer during shutdown to give time for requests to finish. 172 | terminationGracePeriodSeconds: 60 173 | # If we have multiple replicas, try to spread them between availability zones. 174 | affinity: 175 | podAntiAffinity: 176 | preferredDuringSchedulingIgnoredDuringExecution: 177 | - weight: 100 178 | podAffinityTerm: 179 | topologyKey: topology.kubernetes.io/zone 180 | labelSelector: 181 | matchLabels: 182 | app: traefik 183 | - weight: 1 184 | podAffinityTerm: 185 | topologyKey: kubernetes.io/hostname 186 | labelSelector: 187 | matchLabels: 188 | app: traefik 189 | # Load the configuration file as a volume. 190 | volumes: 191 | - name: config 192 | configMap: 193 | name: traefik-config 194 | --- 195 | # Expose the dasboard. 196 | # THIS HAS NO AUTHENTICATION WHICH IS NOT A GOOD IDEA ON A REAL CLUSTER, ONLY SET UP LIKE THIS FOR DEBUGGING LOCALLY. 197 | apiVersion: traefik.containo.us/v1alpha1 198 | kind: IngressRoute 199 | metadata: 200 | name: dashboard 201 | namespace: traefik 202 | spec: 203 | routes: 204 | - match: Host(`dash.lvh.me`) 205 | kind: Rule 206 | services: 207 | - name: api@internal 208 | kind: TraefikService 209 | -------------------------------------------------------------------------------- /whoami.yml: -------------------------------------------------------------------------------- 1 | # Example app using the whoami image. 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: whoami 6 | labels: 7 | app: whoami 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: whoami 13 | template: 14 | metadata: 15 | labels: 16 | app: whoami 17 | spec: 18 | containers: 19 | - image: containous/whoami 20 | name: whoami 21 | ports: 22 | - containerPort: 80 23 | --- 24 | apiVersion: v1 25 | kind: Service 26 | metadata: 27 | name: whoami 28 | labels: 29 | app: whoami 30 | spec: 31 | ports: 32 | - name: http 33 | port: 80 34 | selector: 35 | app: whoami 36 | --- 37 | # Example of configuring forward-auth middleware on an IngressRoute. 38 | apiVersion: traefik.containo.us/v1alpha1 39 | kind: IngressRoute 40 | metadata: 41 | name: whoami 42 | labels: 43 | app: whoami 44 | spec: 45 | routes: 46 | - match: Host(`whoami.lvh.me`) 47 | kind: Rule 48 | services: 49 | - name: whoami 50 | port: 80 51 | middlewares: 52 | - name: traefik-forward-auth-dex@kubernetescrd 53 | tls: 54 | certresolver: default 55 | --------------------------------------------------------------------------------