How to run OAuth2 Proxy with Traefik in Kubernetes, using Helm and Terraform
Traefik stands out as a top choice for Kubernetes Ingress, particularly within the self-hosted community, where many opt for it as their reverse proxy solution.
Imagine you’ve set up Traefik to expose services like “grafana.mydomain.com” to the world. Suddenly, anyone can access your service, sometimes you’re met with a basic login screen, like in Grafana’s case, or no barrier at all, as with Gatus.
While it’s convenient to access services via fully qualified domain names, we don’t necessarily want the entire internet to access it. That’s where authorization comes into play.
One effective solution is to implement OAuth2 authentication. With this approach, users can authenticate using various accounts like Google, Keycloak, or, as we’ll explore here, GitHub.
In our solution, we’ll both use Traefik and OAuth2 Proxy to achieve this authentication for our services. Traefik’s responsibility it to handle all incoming traffic in our cluster and act as a reverse proxy, while OAuth2 Proxy will make sure only the authenticated users are allowed to our services.
Getting started with Traefik & OAuth2 Proxy
First things first, let’s deploy Traefik and OAuth2 Proxy using Helm and Terraform. Assuming you’ve got everything set up, skip ahead to the configuration section.
resource "helm_release" "traefik" {
name = "traefik"
repository = "https://helm.traefik.io/traefik"
chart = "traefik"
namespace = kubernetes_namespace.traefik.metadata.0.name
max_history = 3
version = "26.1.0"
values = [
"${file("value-files/traefik.yaml")}"
]
}
resource "kubernetes_namespace" "traefik" {
metadata {
name = "traefik"
}
}This will deploy Traefik in the ‘traefik’ namespace in your Kubernetes cluster using Helm.
Now, let’s deploy OAuth2 Proxy similarly, ensuring its resources land in the ‘oauth2’ namespace.
resource "helm_release" "oauth2_proxy" {
name = "oauth2-proxy"
repository = "https://oauth2-proxy.github.io/manifests"
chart = "oauth2-proxy"
namespace = kubernetes_namespace.oauth2.metadata.0.name
max_history = 3
version = "7.1.0"
values = [
"${file("value-files/oauth2.yaml")}"
]
}
resource "kubernetes_namespace" "oauth2" {
metadata {
name = "oauth2"
}
}After basic configuration, you should have both Traefik and OAuth2 Proxy up and running in your cluster. Well done!
Configuring OAuth2 Proxy
Let’s authenticate to services using GitHub accounts. First, create an OAuth application in your GitHub account’s developer settings. Provide necessary details like a name, homepage URL, and authorization callback URL, such as oauthsubdomain.mydomain.com/oauth2/callback.
Upon creation, you’ll receive a ClientID and ClientSecret. Store these securely. In my case, I use Hashicorp Vault to store values, referencing them via Terraform, and injecting them into the OAuth2 configuration through Helm.
resource "helm_release" "oauth2_proxy" {
name = "oauth2-proxy"
repository = "https://oauth2-proxy.github.io/manifests"
chart = "oauth2-proxy"
namespace = kubernetes_namespace.oauth2.metadata.0.name
max_history = 3
version = "7.1.0"
values = [
"${file("value-files/oauth2.yaml")}"
]
set_sensitive {
name = "config.clientSecret"
value = data.vault_kv_secret_v2.oauth_github.data["CLIENT_SECRET"]
}
set_sensitive {
name = "config.clientID"
value = data.vault_kv_secret_v2.oauth_github.data["CLIENT_ID"]
}
}In the end, you should have the following pseudo-configuration in your values file
config:
clientID: 1234
clientSecret: abcd
# Other configuration options...Now, it’s time to configure the OAuth2 Provider. You can copy the following configuration for GitHub into your values.yaml file, or adapt it for any other provider (such as Keycloak, Google, etc)
extraArgs:
github-user: GITHUB_USER
provider: github
scope: "read:org,user:email"
skip-provider-button: true # will be important for redirects later
http-address: 0.0.0.0:4180
reverse-proxy: true # important since we are behind Traefik
cookie-refresh: 1h # adapt this to your preference
real-client-ip-header: X-Forwarded-For
redirect-url: https://OAUTH2SUBDOMAIN.MYDOMAIN.com/oauth2/callback
whitelist-domain: ".MYDOMAIN.com"
cookie-domain: ".MYDOMAIN.com"
email-domain: "*"
upstream: "static://204"Time to also configure the Ingress object for OAuth2, so that we can access the service (Note, I’ll not go into detail about how to configure Traefik to expose your Kubernetes cluster to the outside world. There are some excellent tutorials from TechnoTim for example that use MetalLB & Certificate Manager)
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: traefik
traefik.ingress.kubernetes.io/router.middlewares: traefik-default-headers@kubernetescrd
tls:
- hosts:
- login.MYDOMAIN.com
secretName: CERTIFICATE_NAME
hosts:
- login.MYDOMAIN.com
path: /oauth2
pathType: PrefixNow, we should be able to access OAuth2 Proxy manager by heading to https://login.MYDOMAIN.com/oauth2/start
Configuring Traefik
We also want to protect our other services, and force users to authenticate using their GitHub account. For this, we can leverage Traefik’s forwardAuth middleware.
This middleware checks for every request if the user is authenticated. If this is the case, it responds with 2XX, otherwise, a 401 is returned to the user.
To configure this middleware, we use a Kubernetes Manifest
resource "kubernetes_manifest" "oauth_auth" {
manifest = {
"apiVersion" = "traefik.io/v1alpha1"
"kind" = "Middleware"
"metadata" = {
"name" = "oauth2-forwardauth"
"namespace" = kubernetes_namespace.oauth2.metadata.0.name
}
"spec" = {
"forwardAuth" = {
"address" = "https://login.MYDOMAIN.com/oauth2/"
"trustForwardHeader" = true
"authResponseHeaders" = ["X-Auth-Request-Access-Token", "Authorization"]
}
}
}
}When we apply this middleware to any Traefik Ingress Object, it will forward a call to the OAuth2 Proxy service to check if the user is authenticated. For example, let’s configure this for our Gatus service
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: traefik
traefik.ingress.kubernetes.io/router.middlewares: oauth2-oauth2-forwardauth@kubernetescrd
hosts:
- gatus.MYDOMAIN.com
tls:
- secretName: CERTIFICATE_SECRET_NAME
hosts:
- gatus.MYDOMAIN.comNow, if we hit https://gatus.DOMAIN.com, Traefik in the background checks with OAuth2 if I’m authenticated. If that is the case, I’m forwarded to the Gatus homepage, if not, I’ll be redirected to log in with my GitHub account.
By integrating Traefik and OAuth2 Proxy, you’ve added a layer of security to your Kubernetes cluster while ensuring seamless access to your services.
