oAuth2Proxy with AzureAD and Traefik
- oAuth2Proxy with AzureAD and Traefik
oAuth2Proxy is a nice component for Kubernetes that adds an authentication layer, transparently, on top of your workload.
The deployment of oAuth2Proxy is quite straightforward, via Helm, but when it comes to the integration with the authentication providers, some complexity arises, mainly due to the fact the documentation is not very complete. To be fair, oAuth2Proxy supports a lot of backends, and while some of these are well documented, for some others the documentation is very limited.
Let's talk about the latter category, in particular authentication with Azure AD provider.
This notes will describe how to configure oAuth2Proxy with Azure AD and expose its login endpoint via a Kubernetes ingress resource.
When it comes to Ingress component in Kubernetes, I like Traefik. It's lightweight, supports a lot of features, is pretty flexible and it integrates perfectly with the Kubernetes ecosystem.
How actually the authentication works?
oAuth2Proxy supports 2 mode of working: AuthServer or ReverseProxy. Since in our setup we already have a reverse proxy - Traefik - we will use oAuth2Proxy only as AuthServer. In this way oAuth2Proxy will check if the provided token (if any) is valid otherwise will trigger a redirect to start the authentication process in Azure AD.
See the diagram below:
graph LR; U[User] -->|Request| T(Traefik Ingress) T -->|forward auth| OA2P(oAuth2Proxy) T -->|token is valid| W(Workload) T -->|token is not valid| AZ[Redirect with 30x to Azure AD]
These notes start with the assumption you already have a properly configured Azure AD App Registration.
In addition, we will assume Traefik is already installed in the cluster.
Conventions used in this notes
- We want to configure oAuth2Proxy with the domain:
- Our login endpoint will be:
- Our application endpoint is:
- The app registration we pre-configured in Azure AD give us the following information:
- We generated a
<strong_cookie_secret>via any or the procedures described here.
Although oAuth2Proxy seems to support native 'azuread' as provider, I noticed it works better with the standard 'oidc' provider.
With Helm, you can ship the configuration via
values.yaml file. Use the following configuration:
config: clientID: "<app_id>" clientSecret: "<app_password>" cookieSecret: "<strong_cookie_secret>" configFile: |- cookie_domains = ".example.com" cookie_refresh = "1h" email_domains = "*" footer = "oAuth2 Protected Area" oidc_email_claim = "upn" oidc_issuer_url = "https://login.microsoftonline.com/<tenant_id>/v2.0" provider = "oidc" reverse_proxy = true set_authorization_header = true set_xauthrequest = true silence_ping_logging = true skip_jwt_bearer_tokens = true skip_provider_button = true upstreams = [ "static://202" ] whitelist_domains = ".example.com:*" ingress: enabled: true annotations: kubernetes.io/ingress.allow-http: "false" kubernetes.io/ingress.class: traefik traefik.ingress.kubernetes.io/router.entrypoints: "websecure" traefik.ingress.kubernetes.io/router.tls: "true" traefik.ingress.kubernetes.io/router.tls.options: "default" traefik.ingress.kubernetes.io/router.middlewares: oauth2proxy-headers@kubernetescrd path: / hosts: - login.<common_domain> tls: - hosts: - login.<common_domain>
NOTE: if you check the values.yaml you will see we mention a Traefik middleware (we will create it later):
let's deploy oAuth2Proxy with the usual helm commands:
helm repo add oauth2-proxy https://oauth2-proxy.github.io/manifests helm repo update helm upgrade --install -n oauth2proxy --create-namespace oauth2proxy oauth2-proxy/oauth2-proxy \ --values values.yaml
oAuth2Proxy and Traefik integration
In the diagram above we mention that Traefik will use the Forward Auth mechanism to check whether a request is allowed to be propagated. This in Traefik is achieved via the creation of a Middleware. This approach will additionally require another middleware to properly set/propagate the required headers. In this case we will use the Headers middleware
From a separation of concern perspective, we can safely consider the Headers middleware strictly related to the oAuth2Proxy functionality, so we will deploy that middleware in the oauth2proxy namespace.
When it comes to protect your application, then the other middleware can be part of your application namespace. This is convenient when you want to protect multiple applications with oAuth2.
For sake of simplicity, in this example we will install both middlewares in the
Let's create these 2 middlewares. See the code below
--- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: headers namespace: oauth2proxy spec: headers: sslRedirect: true stsSeconds: 315360000 browserXssFilter: true contentTypeNosniff: true forceSTSHeader: true sslHost: example.com stsIncludeSubdomains: true stsPreload: true frameDeny: true --- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: forwardauth namespace: oauth2proxy spec: forwardAuth: address: "https://login.example.com/?rd=https%3A%2F%2Fmyapp.example.com" trustForwardHeader: true authResponseHeaders: - X-Auth-Request-Access-Token - Authorization - X-Auth-Request-Redirect - X-Auth-Request-Email - X-Auth-Request-User - X-Auth-Request-Groups - X-Auth-Request-Username - Set-Cookie
- As we mentioned above, if you need to protect multiple endpoints, you only need to create multiple
forwardauthmiddlewares, one per each namespace where the endpoint is installed.
- Make sure you keep the urlencoded version for the parameter
addressproperty of the
Your application and oAuth2Proxy
Once we created the middlewares, Traefik will automatically discover them and use the
headers one for the ingress of
Now, to instruct our application to requires authentication we can simply add an annotation on the ingress of our application with:
Traefik uses a special construct to refer to Kubernetes CRD. If you checked above you saw middleware references such as
This name represents the structure
What about creating an Azure AD App Registration?
This topic was out of scope of these notes, but you can find an excellent tutorial in this YouTube video.