Passer au contenu principal

Nginx CAS Authentication and SSO : Protection de toute application derrière Nginx avec Casdoor

· Lecture de 12 minutes
Yang Luo
Casdoor Maintainer

Nginx open-source ne dispose pas de module CAS, donc « authentification CAS pour nginx » signifie en pratique l'une des deux configurations suivantes. Soit nginx interroge un service d'authentification pour chaque requête via son module auth_request, et Casdoor répond (directement sur /api/forward-auth, ou via casdoor-forward-auth ou OAuth2 Proxy), soit l'application derrière nginx est elle-même un client CAS et communique avec les endpoints du serveur CAS de Casdoor. Si vous pouvez recompiler nginx, le module nginx de Casdoor exécute la connexion à l'intérieur de nginx, et si vous préférez ne pas utiliser nginx, le proxy de site de Casdoor peut se placer devant l'application. Dans tous les cas, la page de connexion est celle de Casdoor, de sorte qu'une seule session SSO, un seul jeu d'autorisations et une seule politique MFA couvrent toutes les applications.

Les options en un coup d'œil​

OptionOù s'exécute la logique de connexionCe dont nginx a besoinProtocole vers CasdoorQui est autoriséChoisissez-la quand
Casdoor forward authCasdoor, /api/forward-authauth_requestCookie de session Casdoor sur le domaine de l'appAutorisations de l'application, vérifiées au moins une fois par minutenginx est déjà en place et vous ne voulez aucun service supplémentaire
casdoor-forward-authUn petit service Goauth_requestCode d'autorisation OIDCRôles ou groupes Casdoor, par emplacementUn cookie de session partagé par plusieurs sous-domaines
OAuth2 ProxyOAuth2 Proxyauth_requestOIDCallowed_groups, domaines d'e-mailVous utilisez déjà OAuth2 Proxy
casdoor-nginx-modulenginx lui-mêmeUn module compiléOIDC avec PKCE, jetons vérifiés localementTout utilisateur connecté de l'appAucun processus supplémentaire, plus des jetons bearer pour les API
Client CAS dans l'appL'applicationRien, elle ne fait que proxifierCAS 1.0, 2.0, 3.0Ce que l'app fait avec les attributsL'app prend déjà en charge CAS
Proxy de site CasdoorCasdoor sur les ports 80 et 443Pas de nginxSession CasdoorAutorisations de l'applicationRien d'autre n'est placé devant l'app

Les trois premières options diffèrent uniquement par le service qui répond à la sous-requête de nginx.

Pourquoi il n'y a pas de « module nginx CAS »​

CAS est une conversation entre une application et le serveur CAS : l'application redirige le navigateur vers le /login du serveur avec une URL service, reçoit un ticket de service en retour sur cette URL, et valide le ticket par un appel de serveur à serveur. Apache httpd dispose du module mod_auth_cas d'Apereo pour effectuer cela au niveau du serveur web, mais nginx ne propose aucun module CAS parmi ses modules officiels. Pour nginx, le composant standard est ngx_http_auth_request_module : avant de servir une requête, nginx envoie une sous-requête vers une URL choisie. Une réponse 2xx autorise la requête, 401 ou 403 la refuse avec ce code, et tout autre code est une erreur.

Pour l'utilisateur, une connexion CAS et une connexion OIDC semblent identiques : une redirection vers la page de connexion Casdoor puis retour. Ainsi, pour une application sans prise en charge CAS, la solution pratique consiste à utiliser OIDC au niveau de la couche nginx. Informations générales sur le protocole : Qu'est-ce que CAS.

Le module auth_request n'est pas construit par défaut : ajoutez --with-http_auth_request_module lors de la compilation, ou vérifiez nginx -V sur une version packagée de nginx.

Option 1 : Forward auth de Casdoor​

Casdoor peut répondre directement à la sous-requête de nginx. Vous ajoutez un Site dans Casdoor dont le Domain correspond au domaine de l'application et dont l'Casdoor app est l'application dans laquelle les utilisateurs se connectent, et vous ajoutez https://app.example.com/caswaf-handler aux Redirect URLs de cette application. Le host et le port peuvent rester vides, car le trafic ne passe pas par Casdoor.

Comme auth_request ne peut pas suivre les redirections, Casdoor répond à une sous-requête non authentifiée par 401 et place l'URL de connexion dans Location. nginx transforme cela en une réponse 302 :

server {
server_name app.example.com;

proxy_buffer_size 16k;
proxy_buffers 8 16k;

location = /caswaf-handler {
proxy_pass http://casdoor:8000/api/forward-auth;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
}

location = /casdoor-auth {
internal;
proxy_pass http://casdoor:8000/api/forward-auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-Method $request_method;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

location / {
auth_request /casdoor-auth;
auth_request_set $casdoor_redirect $upstream_http_location;
auth_request_set $casdoor_user $upstream_http_x_forwarded_user;
auth_request_set $casdoor_email $upstream_http_x_forwarded_email;
auth_request_set $casdoor_groups $upstream_http_x_forwarded_groups;
error_page 401 =302 $casdoor_redirect;

proxy_set_header X-Forwarded-User $casdoor_user;
proxy_set_header X-Forwarded-Email $casdoor_email;
proxy_set_header X-Forwarded-Groups $casdoor_groups;
proxy_pass http://app:3000;
}
}

Le backend reçoit l'utilisateur dans X-Forwarded-User, X-Forwarded-Email et X-Forwarded-Groups. L'accès suit une permission sur l'application, vérifiée au moins une fois par minute, afin qu'un utilisateur désactivé perde rapidement l'accès. Les Public paths du site (webhooks, contrôles de santé) passent sans connexion.

Consultez Utiliser votre propre reverse proxy pour la référence et Ajouter une connexion à une application sans OIDC pour un guide pas à pas avec Grafana. L'application ne doit être accessible que via nginx, sinon n'importe qui peut envoyer l'en-tête X-Forwarded-User lui-même.

Option 2 : casdoor-forward-auth ou OAuth2 Proxy​

Si vous préférez ne pas créer de site Casdoor pour chaque application, exécutez un petit service qui effectue le flux OIDC contre Casdoor et répond aux sous-requêtes.

casdoor-forward-auth est le service autonome de Casdoor conçu pour cela (il a remplacé traefik-casdoor-auth). Il conserve l'utilisateur dans un cookie signé, sans état côté serveur. Pour nginx, il expose /verify, qui répond par un code 200 avec les en-têtes d'identité ou un code 401, et nginx redirige vers son /login en cas de 401 :

server {
server_name app.example.com;

location = /_casdoor_verify {
internal;
proxy_pass http://casdoor-forward-auth:9999/verify?groups=built-in/dev;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}

location @casdoor_login {
return 302 https://auth.example.com/login?rd=$scheme://$http_host$request_uri;
}

location / {
auth_request /_casdoor_verify;
error_page 401 = @casdoor_login;

auth_request_set $casdoor_user $upstream_http_x_forwarded_user;
auth_request_set $casdoor_roles $upstream_http_x_forwarded_roles;
proxy_set_header X-Forwarded-User $casdoor_user;
proxy_set_header X-Forwarded-Roles $casdoor_roles;
proxy_pass http://app:8080;
}
}

Les paramètres de requête roles et groups sur l'URL de vérification restreignent chaque application aux détenteurs d'un rôle Casdoor ou aux membres d'un groupe ; les utilisateurs connectés ne possédant pas ces attributs reçoivent un code 403. Les rôles et groupes sont lus lors de la connexion et conservés pour la session, donc les modifications prennent effet après une nouvelle connexion de l'utilisateur. Définir COOKIE_DOMAIN=example.com permet à une seule connexion de couvrir tous les sous-domaines. Le guide Traefik couvre ce même service pour Traefik.

OAuth2 Proxy fonctionne de la même manière avec provider = "oidc" et oidc_issuer_url défini sur votre URL Casdoor. Son point de terminaison /oauth2/auth renvoie 202 ou 401, les en-têtes utilisateur et email nécessitent --set-xauthrequest, et la gestion des en-têtes transférés dans son exemple nginx nécessite --reverse-proxy. Deux paramètres Casdoor sont importants : utilisez le format de jeton JWT-Standard, sinon les utilisateurs ayant une email non vérifiée seront rejetés, et conservez la portée profile afin que les groupes et rôles soient renvoyés. Consultez OAuth2 Proxy avec Casdoor et le guide de la version communautaire nginx.

Option 3 : le module nginx de Casdoor​

casdoor-nginx-module gère la connexion directement dans nginx, sans processus séparé. Les navigateurs sans session sont redirigés vers la page de connexion de Casdoor ; le module échange le code avec PKCE, vérifie la signature du jeton d'accès ainsi que les champs exp, aud et le type par rapport au certificat de Casdoor, et conserve l'utilisateur dans un cookie signé. Les clients API peuvent envoyer Authorization: Bearer avec un jeton d'accès Casdoor, vérifié localement sans appel à Casdoor.

Il nécessite nginx 1.22 ou plus récent et OpenSSL 1.1.1 ou plus récent. Vous pouvez le compiler en tant que module dynamique par rapport au nginx que vous exécutez déjà :

./configure --with-compat --with-http_ssl_module --add-dynamic-module=/path/to/casdoor-nginx-module
make modules
cp objs/ngx_http_casdoor_module.so /etc/nginx/modules/
load_module modules/ngx_http_casdoor_module.so;

events {}

http {
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/nginx/app.crt;
ssl_certificate_key /etc/nginx/app.key;

casdoor_endpoint https://door.example.com;
casdoor_client_id <client-id>;
casdoor_client_secret <client-secret>;
casdoor_certificate /etc/nginx/casdoor.pem;
casdoor_redirect_uri https://app.example.com/casdoor/callback;
casdoor_token_location /_casdoor/token;

location = /_casdoor/token {
internal;
proxy_pass https://door.example.com/api/login/oauth/access_token;
proxy_ssl_server_name on;
}

location = /casdoor/callback {
casdoor_callback;
}

location / {
casdoor on;
proxy_set_header X-User $casdoor_user;
proxy_pass http://127.0.0.1:3000;
}
}
}

Remplacez les espaces réservés par les valeurs de l'application. La session n'est pas revérifiée auprès de Casdoor avant son expiration, donc la désactivation d'un utilisateur prend effet lorsque la session se termine ; casdoor_session_timeout raccourcit cette fenêtre. Le module n'effectue aucune vérification de rôle ou de groupe. Sur NGINX Plus, consultez plutôt le guide NGINX Plus.

Option 4 : l'application est un client CAS​

Si l'application parle déjà CAS, comme une application Java avec le client Apereo CAS, nginx agit uniquement comme proxy et Casdoor est le serveur CAS. Chaque application Casdoor possède des points de terminaison CAS sous /cas/<organization>/<application> : /login et /logout, /validate pour CAS 1.0, /serviceValidate, /proxyValidate et /proxy pour 2.0, /p3/serviceValidate et /p3/proxyValidate pour 3.0, et /samlValidate pour la validation SAML 1.1.

L'application envoie le navigateur vers /login?service=..., Casdoor redirige en retour avec ticket=ST-..., et l'application valide ce ticket :

curl "https://door.example.com/cas/my-org/my-app/p3/serviceValidate?ticket=ST-1234567890&service=https%3A%2F%2Fapp.example.com%2F"

Une réponse réussie est un élément cas:serviceResponse contenant cas:user et des attributs tels que l'email, le nom affiché et memberOf ; ajoutez format=json pour obtenir du JSON. Voici les pièges courants :

  • Chaque URL service doit être listée dans les Redirect URLs de l'application, et le paramètre service utilisé lors de la validation doit correspondre exactement à celui pour lequel le ticket a été émis.
  • Les tickets ne fonctionnent qu'une seule fois et expirent après 5 minutes.
  • Les tickets sont conservés en mémoire, donc avec plusieurs répliques de Casdoor, la validation doit atteindre la réplique qui a émis le ticket (sessions collantes).
  • Derrière nginx, transmettez Host et X-Forwarded-Proto afin que l'application construise la même URL service que celle vue par le navigateur.

La configuration, y compris les filtres du client Java pour chaque version de CAS, est décrite dans Connecter un client CAS.

Option 5 : laissez Casdoor agir comme reverse proxy​

Lorsque rien d'autre ne se trouve devant l'application, Casdoor peut la servir directement : son proxy de site écoute sur gatewayHttpPort et gatewayHttpsPort (80 et 443 par défaut) dès qu'un site existe. Définissez le champ Host du site sur le backend, choisissez un certificat, définissez Casdoor app, et pointez le DNS vers Casdoor. La connexion, les permissions et les en-têtes se comportent comme dans l'option 1. Consultez Sites.

Ajout de MFA​

MFA ne nécessite aucun changement dans nginx ou dans l'application. Casdoor demande le second facteur sur sa propre page de connexion avant d'émettre un code d'autorisation OAuth ou un ticket de service CAS, donc toutes les options ci-dessus en bénéficient. Les utilisateurs peuvent utiliser une application TOTP, SMS, email ou RADIUS, avec des codes de récupération, et les administrateurs peuvent exiger MFA pour une organisation via les éléments MFA. Cela répond également à « CAS 2FA » : avec Casdoor comme serveur CAS, le second facteur fait partie de la connexion CAS.

MFA est appliqué à la connexion, pas par requête : une session déjà détenue par nginx ou un service forward-auth dure jusqu'à son expiration, et le champ MFA remember time de l'organisation permet aux utilisateurs d'ignorer le second facteur sur un appareil de confiance pendant un certain temps. Consultez le guide MFA et Qu'est-ce que MFA.

Une fois connecté à Casdoor, les utilisateurs accèdent à d'autres applications qui les renvoient vers Casdoor sans nouvelle demande de mot de passe ; avec Auto signin, Casdoor ignore également le sélecteur de compte. Consultez single sign-on et Single logout.

Lequel choisir​

  • Pas de connexion dans l'application et nginx déjà en place : forward auth de Casdoor.
  • Une connexion unique sur de nombreux sous-domaines, ou des vérifications de rôle par emplacement : casdoor-forward-auth (ou OAuth2 Proxy si vous l'utilisez déjà).
  • Vous configurez nginx vous-même et protégez également les API avec des jetons bearer : casdoor-nginx-module.
  • L'application prend en charge CAS : les endpoints CAS de Casdoor, nginx comme proxy simple.
  • Pas encore de proxy : le site proxy de Casdoor.

Références​