Sécurisez l’accès à vos applications en toute simplicité avec OAuth2 Proxy

Introduction

La multiplication des déploiements d’applications dans le Cloud nous appelle à repenser la façon dont ces dernières sont accessibles et exposées. En effet, il n’est pas concevable sur le plan de la sécurité et de la confidentialité d’ouvrir aux 4 vents l’ensemble des applicatifs que l’on déploie dans le Cloud. Que ce soit des backends d’administration ou des environnements hors-production, il est souvent nécessaire d’en restreindre l’accès à quelques personnes.

Une multitude d’options s’offre alors à nous, avec leurs avantages et leurs inconvénients :

  • Filtrage IP : cette solution a l’avantage d’être simple à mettre en place, mais est complexe à maintenir : certaines personnes vont changer (très) régulièrement d’adresse IP et la whitelist va s’allonger sans cesse car on ne fait que trop rarement de suppressions, représentant alors un risque d’exposition trop importante des applicatifs. De plus, cette solution est très peu adaptée dans un contexte de cloud public, les adresses IP étant allouées de manière dynamique et amenées à changer régulièrement ;
  • VPN : solution efficace mais lourde à mettre en place et contraignante pour les utilisateurs qui doivent installer un client VPN spécifique pour accéder aux applications ;
  • Développements spécifiques : il est en théorie possible de rajouter une page d’authentification devant l'application que l’on veut protéger, mais cela demande du temps de développement et du temps de maintenance lorsqu’il faudra faire les montées de versions des librairies tierces utilisées ;
  • mTLS : tout comme le VPN, solution efficace mais lourde à mettre en place, à maintenir, et contraignante pour les utilisateurs, du fait de l’utilisation de couples de clés privées et certificats TLS au niveau des navigateurs des utilisateurs.

N’y a-t-il pas une autre solution, à la fois simple à mettre en place, à maintenir et peu contraignante pour les utilisateurs ? C’est là qu’intervient OAuth2 Proxy.

OAuth2 Proxy, c’est quoi ?

OAuth2 Proxy est un reverse proxy open-source qui permet de gérer l’authentification auprès de multiples providers. Il peut s’intégrer facilement à une architecture déjà existante car il est autonome et ne nécessite que peu de configuration quant à l’architecture des applications à protéger.

Concrètement, la protection des applications va s’effectuer au niveau du reverse proxy se trouvant devant ces dernières en ajoutant une étape d'autorisation où chaque requête arrivant va devoir être autorisée par OAuth2 Proxy, qui va effectuer une authentification auprès d’un provider tiers. La liste des providers actuellement supportés est assez exhaustive, de Google à Nextcloud en passant par Azure, Keycloak ou encore le standard OpenID Connect.

Le fonctionnement détaillé est le suivant :

  1. L’utilisateur essaye d’accéder à myapp.example.com ;
  2. La requête arrive au niveau du reverse proxy, qui va vérifier si l’utilisateur est bien connecté auprès de OAuth2 Proxy ;
  3. L’utilisateur n’étant pas connecté, le reverse proxy va rediriger l’utilisateur vers le serveur d’authentification, et ce grâce aux informations contenues dans la réponse de OAuth2 Proxy. L’utilisateur est donc redirigé vers auth.example.com et s’authentifie ;
  4. Le serveur d’authentification redirige alors l’utilisateur vers OAuth2 Proxy (oauth.example.com) avec un code permettant à ce dernier de récupérer les access token et id token de l’utilisateur auprès du serveur d’authentification ;
  5. L’Oauth2 Proxy récupère les access token et id token auprès du serveur d’authentification ;
  6. Une fois la connexion validée, OAuth2 Proxy redirige l’utilisateur vers myapp.example.com avec un entête HTTP Set-Cookie permettant à l’utilisateur de stocker un cookie de connexion qui sera envoyé à chaque requête effectuée sur un domaine ou sous-domaine précisé lors de la configuration de OAuth2 Proxy.

Le fonctionnement de OAuth2 Proxy n’ayant plus de secrets pour vous, passons à la pratique !

Mise en place avec l’Ingress NGINX Controller et Keycloak

Dans cet exemple, nous allons nous concentrer sur la mise en place d’OAuth2 Proxy afin de protéger une application hébergée dans un cluster Kubernetes.

Nous utiliserons l’Ingress NGINX Controller ainsi que Keycloak, étant donné que ce sont deux incontournables standards, et que ce sont toutes deux des solutions open source. Cependant, il est tout à fait possible de configurer un reverse proxy Nginx ou Traefik manuellement pour effectuer l’authentification via OAuth2 Proxy.

Prérequis

Avant toute chose, il est nécessaire d’avoir accès à un cluster Kubernetes.

Il convient également d’avoir une application déployée dans ce cluster, sous forme de Deployment par exemple, ainsi qu’un service de type ClusterIP permettant d’y accéder. Nous créerons l’Ingress plus tard.

De la même manière, il est nécessaire d’avoir un Ingress Controller de déployé, ici l’Ingress NGINX Controller, ainsi qu’un serveur d’authentification, ici Keycloak.

Et voilà ! Nous sommes près à déployer OAuth2 Proxy et à le configurer !

Configuration de Keycloak

Nous devons dans un premier temps configurer Keycloak afin de créer un nouveau client OpenID Connect pour l’application OAuth2 Proxy. Ce prérequis est détaillé dans la documentation officielle de OAuth2 Proxy.

Il faut donc créer un nouveau client de type OpenID Connect, que nous appelons oauth2-proxy, en activant l’option Client authentication et en désactivant l’option Direct access grants.

Nous configurons les URL du client comme suit :

Il est ensuite nécessaire de configurer un mapper d’audience dédié pour notre client en allant dans le menu Client scopes de notre client oauth2-proxy, et de sélectionner la portée oauth2-proxy-dedicated.

Depuis cette portée, nous pouvons créer un nouveau mapper de type Audience, avec la configuration suivante :

Le client OpenID Connect est à présent configuré pour fonctionner avec OAuth2 Proxy. Cependant, dans la suite de cet article, nous configurons OAuth2 Proxy pour n’accepter que les utilisateurs ayant le rôle ROLE_USER. Il convient donc de donner ce rôle aux utilisateurs ayant le droit d’accéder aux applications.

Déploiement et configuration de OAuth2 Proxy

Pour déployer simplement et rapidement OAuth2 Proxy, nous utilisons le chart Helm officiel et la commande suivante :

helm install oauth2-proxy oauth2-proxy \
--repo https://oauth2-proxy.github.io/manifests \
--namespace oauth2-proxy \
--create-namespace \
--values values.yaml

La configuration se trouve dans le fichier values.yaml suivant :

config:
  clientID: oauth2-proxy
  clientSecret: the-client-secret
  cookieSecret: the-cookie-secret
  configFile: |
    provider = "keycloak-oidc"
    provider_display_name = "Keycloak"
    redirect_url = "https://oauth.example.com/oauth2/callback"
    oidc_issuer_url = "https://auth.example.com/realms/sso"
    scope = "openid email"
    code_challenge_method = "S256"
    allowed_roles = ["ROLE_USER"]
    whitelist_domains = ["*.example.com"]
    email_domains = ["*.example.com"]
    cookie_domains = [".example.com"]
    cookie_expire = "1h"
    cookie_samesite = "strict"
    session_cookie_minimal = "true"
ingress:
  enabled: true
  className: nginx
  hosts:
  - oauth.example.com
  tls:
  - hosts:
    - oauth.example.com
    secretName: oauth2-proxy-certificate-secret

Tout d’abord, nous spécifions les identifiants du client OpenID dans les variables clientId et clientSecret, ainsi qu’un secret destiné à sécuriser les cookies gérés par OAuth2 Proxy dans la variable cookieSecret.

Ensuite, nous configurons le comportement d’OAuth2 Proxy à l’aide d’un fichier de configuration possédant les champs suivants :

  • provider : le provider OAuth2 à utiliser parmis la liste des providers supportés
  • provider_display_name : le nom du provider affiché dans l’interface d’OAuth2 Proxy
  • redirect_url : l’URL de redirection OAuth2
  • oidc_issuer_url : l’URL de l’émetteur OpenID Connect
  • scope : la portée OAuth2 à utiliser
  • code_challenge_method : la méthode utilisée pour les challenges de code PKCE
  • allowed_roles : les rôles que doivent avoir les utilisateurs dans Keycloak pour pouvoir accéder aux applications derrière OAuth2 Proxy
  • whitelist_domains : les domaines autorisés pour la redirection après authentification
  • email_domains : les domaines des adresses mail pouvant s’authentifier
  • cookie_domains : les domaines sur lesquels le cookie de connexion OAuth2 Proxy peut être envoyé
  • cookie_expire : la durée de validité du cookie de connexion avant expiration
  • cookie_samesite : la politique SameSite du cookie de connexion permettant de se protéger contre des attaques de type CSRF
  • session_cookie_minimal : permet de supprimer les jetons OAuth2 du stockage des cookies de session s’ils ne sont pas nécessaires (permet notamment d’éviter certaines erreurs lors de la connexion en minimisant la taille des requêtes HTTP avec OAuth2 Proxy)


Enfin, nous configurons l’Ingress à créer afin d’exposer OAuth2 Proxy avec le nom de domaine oauth.example.com.

Configuration de l’Ingress afin de protéger notre application

Maintenant que nous avons OAuth2 Proxy déployé et configuré, il ne nous reste plus qu’à mettre en place l’Ingress afin d’exposer notre application de manière sécurisée.

Cela peut se faire grâce au manifest suivant :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    nginx.ingress.kubernetes.io/auth-url: https://oauth.example.com/oauth2/auth
    nginx.ingress.kubernetes.io/auth-signin: https://oauth.example.com/oauth2/start
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - my-app.example.com
    secretName: my-app-certificate-secret
  rules:
  - host: my-app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app
            port:
              number: 8080

Vous aurez sans doute remarqué la simplicité de mise en œuvre. Il suffit en effet d’ajouter deux annotations à notre Ingress pour configurer l’Ingress NGINX Controller et l’interfacer avec OAuth2 Proxy:

  • nginx.ingress.kubernetes.io/auth-url : permet l’utilisation d’un service externe d’authentification ;
  • nginx.ingress.kubernetes.io/auth-signin : permet de spécifier l’URL de la page d’erreur afin de débuter le processus d’authentification.

Et voilà ! Si vous vous rendez maintenant sur my-app.example.com, vous serez redirigé vers le serveur d’authentification. Une fois connecté, vous pourrez accéder à votre application, qui pour rappel, ne gère à aucun moment l’authentification !

Pour aller plus loin

Maintenant que nous avons sécurisé une application avec Oauth2 Proxy, quelles sont les difficultés pour le mettre à l’échelle de plusieurs applications ?

La sécurisation de plusieurs applications portées par des noms de domaines différents pourrait se faire en déployant plusieurs instances de l’Oauth2 Proxy sécurisant chacune un domaine. Cependant, cette solution perdrait l'intérêt d’un outil unique centralisé et obligerait les utilisateurs à s’authentifier à chaque accès à une nouvelle application. C’est pourquoi il est possible de configurer l’Oauth2 Proxy pour répondre sous plusieurs URL - une pour chaque domaine protégé. Il sera alors nécessaire de lui préciser la liste des domaines sur lesquels il doit poser les cookies.

Par ailleurs, il est préférable de ne pas stocker les informations de session dans des cookies côté client mais de les stocker dans un redis dédié. En effet, le stockage côté client peut poser problème lorsque l’on sécurise avec la même instance Oauth2 Proxy plusieurs domaines car le cookie peut dépasser la taille maximale autorisée (c’est notamment le cas avec AzureAD/EntraID et ADFS).

Enfin, la configuration nécessaire pour passer l’étape d’authentification de manière automatisée (pour des tests lancés en CI par exemple) peut être complexe et dépend du provider utilisé.

Conclusion

Nous avons vu dans cet article comment sécuriser simplement et rapidement nos applications grâce à OAuth2 Proxy, et ce sans contraintes pour les utilisateurs et avec un minimum de maintenance côté opérationnel. Plus question donc de mettre en place des solutions telles qu’un filtrage IP, mTLS ou encore un VPN !

Sources