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).
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
.