...
Sous debian, il faut modifier /etc/systemd/system/multi-user.target.wants/tomcat9.service pour y insérer le paramétrage suivant :
| Bloc de code | ||||
|---|---|---|---|---|
| ||||
ReadWritePaths=/etc/cas/config/keystore.jwks |
...
En ne demandant pas la MFA pour un client donné (au premier appel) mais en la demandant lors de la validation du ticket, nous récupérions une erreur de type invalid_authentication_context côté du service lors de la validation du ticket.
...
Cette revalidation, du fait que le client doit ou non passer par la (une) MFA (ré-exécution du script groovy) lors de la validation du ticket, est apparue entre la 6.4.6.6 et la 6.6.9
| Info |
|---|
À noter que clientIpAddress, comme l'ensemble des attributs issus de authentication, provient du TGC qui est calculé au moment de l'authentification initale CAS : aussi clientIpAddress correspond à l'IP de l'utilisateur au moment de l'authentification et non pas au moment de l'accès au service demandé. Dans un certain nombre de scénarios, ce fonctionnement est à garder à l'esprit et prendre en compte et risque malheureusement de provoquer des scénarios de connexion incohérents vis-à-vis de l'utilisateur. Exemple :
Le problème est que l'usager est donc passé 2 fois par la MFA, la seconde est de trop mais les éléments connus dans le script groovy ne permettent pas de savoir que l'utilisateur doit maintenant être considéré comme étant dans l'établissement. |
Ticket Registry
Ticket Registry Redis à éviter en CAS 6.6.9
...
On peut espérer que le DefaultTicketRegistryCleaner appelle le ticketRegistry Redis pour supprimer les TGTs stockés dans ces clefs CAS_PRINCIPAL:uid ... mais force est de constater que ça ne semble pas être le cas sur notre installation.
Comme on avait pu le constater avec le JPA Ticket Registry (cf notre retour de 2019 à ce propos) le fonctionnement du Ticket Registry semble avoir été pensé pour déléguer les tâches d'expiration des données au système de stockage (registry) choisi.
Jusque-là le Redis Ticket Registry fonctionnait bien ainsi, mais l'apparition de ces entrées CAS_PRINCIPAL:uid (sorte de rustine ayant pour but de retrouver rapidement les tickets d'un utilisateur donné) vient casser ce système et il semble aventureux de fonctionner ainsi.
| Info |
|---|
| Après lecture du code (DefaultTicketRegistryCleaner et RedisTicketRegistry dans CAS), ce que l'on en conclue ici, c'est que Redis supprime naturellement les entrées du type CAS_TICKET:TGT-numero en fonction du TTL positionné ; aussi lorsque le DefaultTicketRegistryCleaner passe, les tickets déjà supprimés par Redis lui-même ne sont pas listés/pris en compte par la procédure de CAS qui, de fait, ne les supprime pas des sets listés dans CAS_PRINCIPAL:uid ; en fonctionnant ainsi, l'ensemble des serialisation des TGT (avec l'ensemble des attributs utilisateurs associés) vont donc perdurer dans les CAS_PRINCIPAL:uid sans jamais être nettoyés pour consommer très rapidement l'ensemble de la RAM disponible (le graphe nous montre une consommation d'environ 1GB de RAM jour sur une période estivale de congés) - on peut considérer ce pb comme un bug. Provenant de toute manière d'une implémentation contournant une "limitation" Redis sur la recherche par index, il semble ici plus simple de ne pas s'obstiner sur cette option de ticket registry Redis, dans cette version 6.6 en tout cas. |
Notre CAS avec esup-otp utilisant dès à présent pour certaines données une base MongoDB, on s'est naturellement tourné vers le Ticket Registry sous MongoDB.
Passage au Ticket Registry MongoDB
...
Aussi, les tickets sont supprimés par CAS lui-même via DefaultTicketRegistryCleaner en fonction de ce expireAt.
Et "au pire", ils sont supprimés par le mécanisme interne de MongoDB via le expireAfterSeconds 15 jours après le expireAt ... soit 15 jours et 8 heures après pour les TGT non remember-me et 29 (14+15) jours après pour les remember-me.
Cf la documentation MongoDB à ce sujet vis-à-vis de expireAfterSeconds.
Coût du nettoyage de tickets via DefaultTicketRegistryCleaner
Aussi, avec cette configuration ("par défaut"), DefaultTicketRegistryCleaner a la charge de supprimer tous les tickets expirés de la base. En soit, on peut penser que ce type d'opérations n'est pas gourmande : on supprime tous les tickets dont le expireAt est plus vieux que la date actuelle ; on peut penser que cela se fait en une seule requête. Malheureusement l'implémentation proposée ici est réalisée dans une logique de NoSQL générique : DefaultTicketRegistryCleaner récupère tous les tickets, les "lit" (et donc les décode si ils sont encodés, ce qui est le cas normalement si vous suivez les recommandations de CAS), sélectionne les tickets qu'il estime expirés pour enfin les supprimer. Cette implémentation générique est très coûteuse, d'autant que par défaut cette procédure de suppression de tickets par le DefaultTicketRegistry est appelée toutes les 2 minutes ( cas.ticket.registry.cleaner.schedule.repeat-interval=PT2M ). Ainsi, lors d'une période creuse sur notre CAS, on note une consommation d'environ 30 secondes de CPU à chaque nettoyage de tickets (toutes les 2 minutes) résultant du déchiffrement de l'ensemble des tickets.
Une "optimisation" élémentaire est simplement d'augmenter le cas.ticket.registry.cleaner.schedule.repeat-interval à 1 heure par exemple (PT1H).
A l'autre extrême on peut aussi imaginer laisser le soin à MongoDB de s'occuper lui-même de supprimer les tickets expirés, d'autant que c'est tout l'intérêt de ce type de base et de la fonctionnalité d'index avec un expireAfterSeconds (ttl) de positionné et que c'est ce qui pose problème à l'implémentation via Redis avec l'introduction de l'entrée CAS_PRINCIPAL:uid correspondant à plusieurs TGT et ne pouvant de fait pas avoir un TTL propre.
...