Arborescence des pages

Vous regardez une version antérieure (v. /wiki/display/CAS/Retour+de+l%27URN+sur+mise+en+place+de+CAS+6.6.9) de cette page.

afficher les différences afficher l'historique de la page

« Afficher la version précédente Vous regardez la version actuelle de cette page. (v. 19) afficher la version suivante »

Contexte

Eté 2023, l'Université de Rouen Normandie a procédé à une montée de version de son service d'authentification CAS en 6.6.9

Cette page wiki vient en complément de nos pages Retour de l'URN sur mise en place de CAS 6.0.4 et Retour de l'URN sur mise en place de CAS 6.4.1 ; les retours que l'on avait faits sur ces versions 6.x sont a priori toujours d'actualité.

Cette installation est faite sur une debian en utilisant CAS Initializr ; jusque-là notre installation et montée de version s'appuyait sur ce qui était utilisé/embarqué par le Kit installation CAS V5.2 AMU.

On a également profité de cette montée de version pour mettre en place le module OpenID Connect Authentication permettant à CAS de supporter l'authentification OpenIdConnect (oidc) en tant que fournisseur (provider/serveur).

En fin de document, on fait aussi un retour sur un problème rencontré avec le ticket registry sous Redis et cette version de CAS 6.6.9 ; on en profite pour documenter notre rapide passage au ticket registry sous MongoDB.

Installation

Paquets

apt install openjdk-11-jdk-headless tomcat9 curl unzip

CAS Initializr

Pour construire notre CAS, on ajoute dans le .bashrc d'un utilisateur 'cas' :

function getcas() {
  url="https://casinit.herokuapp.com/starter.tgz"
  projectType="cas-overlay"
  dependencies=""
  directory="overlay"
  for arg in $@; do
    case "$arg" in
    --url|-u)
      url=$2
      shift 1
      ;;
    --type|-t)
      projectType=$2
      shift 1
      ;;
    --directory|--dir|-d)
      directory=$2
      shift 1
      ;;
    --casVersion|--cas)
      casVersion="-d casVersion=$2"
      shift 1
      ;;
    --bootVersion|--springBootVersion|--boot)
      bootVersion="-d bootVersion=$2"
      shift 1
      ;;
    --modules|--dependencies|--extensions|-m)
      dependencies="-d dependencies=$2"
      shift 1
      ;;
    *)
      shift
      ;;
    esac
  done
  rm -Rf ./${directory}
  echo -e "Generating project ${projectType} with dependencies ${dependencies}..."
  cmd="curl ${url} -d type=${projectType} -d baseDir=${directory} ${dependencies} ${casVersion} ${bootVersion} | tar -xzvf -"
  echo -e "${cmd}"
  eval "${cmd}"
  ls
}

Puis on créé un fichier urn-generate-cas-overlay.sh : 

#!/bin/bash -x
getcas --cas 6.6.9 --modules support-ldap,support-json-service-registry,support-redis-ticket-registry,support-spnego-webflow,support-trusted-mfa-mongo,support-throttle,support-reports,supp
ort-interrupt-webflow,support-oidc

On lance ce script qui nous construit l'overlay CAS dans le répertoire overlay. Ce répertoire est versionné via git avec pour premier commit "Project created by Apereo CAS Initializr".

On ajuste le build.gradle pour installer notamment les modules CAS d'esup-otp et esup-agimus ; on fixe aussi le look via l'ajout de css et surcharge de templates.

cas@cas:/opt/overlay$ git diff
warning: LF will be replaced by CRLF in build.gradle.
The file will have its original line endings in your working directory
warning: LF will be replaced by CRLF in gradle.properties.
The file will have its original line endings in your working directory
diff --git a/build.gradle b/build.gradle
index bb8758e..6cffd74 100644
--- a/build.gradle
+++ b/build.gradle
@@ -82,6 +82,9 @@ repositories {
         }
         mavenContent { releasesOnly() }
     }
+    maven {
+        url "https://jitpack.io"
+    }
 }
 
 
@@ -273,11 +276,15 @@ dependencies {
     implementation "org.apereo.cas:cas-server-support-json-service-registry"
     implementation "org.apereo.cas:cas-server-support-redis-ticket-registry"
     implementation "org.apereo.cas:cas-server-support-spnego-webflow"
+    implementation files("${projectDir}/lib/jcifs-ext.jar")
     implementation "org.apereo.cas:cas-server-support-trusted-mfa-mongo"
     implementation "org.apereo.cas:cas-server-support-throttle"
     implementation "org.apereo.cas:cas-server-support-reports"
     implementation "org.apereo.cas:cas-server-support-interrupt-webflow"
     implementation "org.apereo.cas:cas-server-support-oidc"
+    implementation "com.github.vbonamy:cas-server-support-agimus-cookie:cas-6.4.x-SNAPSHOT"
+    implementation "com.github.vbonamy:cas-server-support-agimus-logs:cas-6.4.x-SNAPSHOT"
+    implementation "com.github.EsupPortail:esup-otp-cas:6.6.x-SNAPSHOT"
  
 
     if (project.hasProperty("casModules")) {
diff --git a/gradle.properties b/gradle.properties
index e33296a..d875561 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -29,7 +29,7 @@ dockerImagePlatform=amd64:linux
 # Include launch script for executable WAR artifact
 # Setting this to true allows the final web application
 # to be fully executable on its own
-executable=true
+executable=false
 
     
 
@@ -37,7 +37,7 @@ executable=true
 # if the overlay application supports or provides the chosen type.
 # You should set this to blank if you want to deploy to an external container.
 # and want to set up, download and manage the container (i.e. Apache Tomcat) yourself.
-appServer=-tomcat
+appServer=
 
 # Settings to generate keystore
 # used by the build to assist with creating


cas@cas:/opt/overlay$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   build.gradle
	modified:   gradle.properties

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	lib/
	src/main/resources/static/
	src/main/resources/templates/

no changes added to commit (use "git add" and/or "git commit -a")



cas@cas:/opt/overlay$ tree src/main/resources/static/ src/main/resources/templates/ lib/
src/main/resources/static/
??? images
    ??? cas-logo.png
    ??? logo_universite_rouen.svg
src/main/resources/templates/
??? fragments
?   ??? footer.html
?   ??? pmlinks.html
??? layout.html
lib/
??? jcifs-ext.jar

2 directories, 6 files

Puis on commit.

Packaging

Depuis /opt/overlay on lance le packaging via gradle :

./gradlew clean build

Configuration du tomcat

On a choisi ici l'utilisation du tomcat9 fourni par debian, on modifie /var/lib/tomcat9/conf/server.xml pour ne pas loguer les accès (qui seront logués par le apache en frontal), désactiver le connecteur http et activer le connecteur ajp.

Déploiement

rm -rf  /var/lib/tomcat9/webapps/ROOT && unzip /opt/overlay/build/libs/cas.war  -d /var/lib/tomcat9/webapps/ROOT

Configuration du démarrage et démarrage

systemctl enable tomcat9

systemctl restart tomcat9

Installation des autres composants

On installe et configure également les autres paquets/briques nécessaires au bon fonctionnement de notre CAS, à savoir :

  • apache avec ajp, ssl, ...
  • certbot pour automatiser le renouvellement des certificats
  • les briques requises par les modules CAS utilisés : mogodb, redis, etc.
  • les briques esup-otp et dépendances associées que l'on installe sur le même serveur

Ajustement des configurations CAS 6.4.6.3 → 6.6.9

À chaque mise à jour de CAS, son lot de renommage de configurations ... liste non exhaustive pour CAS dans /etc/cas/config/cas.properties :

  • cas.audit.* → cas.audit.engine.*
  • suppression de cas.authn.attributeRepository.ldap[0].useSsl et cas.authn.attributeRepository.ldap[0].providerClass
  • cas.authn.attributerepository.maximum-cache-size → cas.authn.attributerepository.core.maximum-cache-size 
  • cas.authn.mfa.trusted.authentication-context-attribute → cas.authn.mfa.trusted.core.authentication-context-attribute
  • cas.authn.mfa.trusted.device-registration-enabled → cas.authn.mfa.trusted.core.device-registration-enabled
  • suppression de cas.authn.mfa.trusted.expiration=7 et cas.authn.mfa.trusted.timeUnit=DAY

Concernant ce dernier point, les paramétrages d'activation et expiration de l'enregistrement des navigateurs/périphériques clients (devices) comme étant sûres (trusted) sont portés par l'implémentation de la MFA.
Dans le cadre d'esup-otp, dans le fichier esupotp.properties on peut ainsi positionner par exemple :

esupotp.trustedDeviceEnabled=true
esupotp.isDeviceRegistrationRequired=false
esupotp.deviceRegistrationExpirationInDays=7



Les fichiers des déclarations de services dans /etc/cas/services doivent aussi être modifiés : 

find /etc/cas/services/ -name "*json" -exec sed -i 's/org.apereo.cas.services.RegexRegisteredService/org.apereo.cas.services.CasRegisteredService/' {} \;


Pour esup-otp, dans /etc/cas/config/esupotp.properties, suppression de esupotp.byPassServicesIfNoEsupOtpMethodIsActive

OpenIdConnect

Configuration OpenIdConnect sur CAS

Ajout de la configuration suivante dans /etc/cas/config/cas.properties :

##############################
## OPENID CONNECT           ##
##############################
cas.authn.oidc.core.issuer=https://cas.univ-rouen.fr/oidc


Pour que cela fonctionne, CAS doit pouvoir écrire le fichier /etc/cas/config/keystore.jwks au travers de tomcat lors du lancement.

Sous debian, il faut modifier /etc/systemd/system/multi-user.target.wants/tomcat9.service pour y insérer le paramétrage suivant : 

ReadWritePaths=/etc/cas/config/keystore.jwks

On créé le fichier /etc/cas/config/keystore.jwks et on lui donne comme propriétaire l'utilisateur tomcat.

Lors du lancement, on prend en compte les messages du type "The generated key MUST be added to CAS settings" pour compléter la configuration openid.

Un service pourra alors être déclaré de cette façon : 


cat /etc/cas/services/test_oidc-1000.json
{
  "@class" : "org.apereo.cas.services.OidcRegisteredService",
  "clientId": "clientoidctestaconfigurercoteclient",
  "clientSecret": "monsecretaconfigurercoteclient,
  "serviceId" : "https://test-oidc.univ-rouen.fr/secureoidc/redirect_uri",
  "name": "OIDC",
  "id": 1000,
}

Client OpenIdConnect avec Apache2 et mod_auth_openidc

Comment conseillé par les collègues de l'AMU (cf leur présentation à Esup-Days 26) nous avons validé le bon fonctionnement de CAS en tant que provider openidconnect via l'utilisation d'un apache avec mod_auth_openidc.

Ainsi le serveur derrière https://test-oidc.univ-rouen.fr pourra avoir pour configuration Apache : 

ScriptAlias /secureoidc /var/www/printenv.pl
OIDCProviderMetadataURL https://cas.univ-rouen.fr/oidc/.well-known/openid-configuration
OIDCClientID clientoidctestaconfigurercoteclient
OIDCClientSecret monsecretaconfigurercoteclient
OIDCRemoteUserClaim sub
OIDCScope "openid email profile"
OIDCRedirectURI https://test-oidc.univ-rouen.fr/secureoidc/redirect_uri
#LogLevel info auth_openidc:debug
OIDCCryptoPassphrase ppassQuelconquePourChiffrementInterne
<Location /secureoidc>
 AuthType openid-connect
 Require valid-user
</Location>

/var/www/printenv.pl étant le "traditionnel" script utilisé notamment dans les configurations shibboleth et donnant simplement les entêtes HTTP.

#!/usr/bin/perl

print "Content-type: text/plain\n\n";

print "Variables d'environnement :\n\n";

foreach my $key (keys %ENV) {
	printf "$key=$ENV{$key}\n";
}

On y voit alors le REMOTE_USER de positionné, mais aussi des OIDC_CLAIM_* dont OIDC_CLAIM_attributes regroupant l'ensemble des attributs renvoyés (sous forme de json).

Ajustement script groovy déclenchement MFA - récupération de l'IP cliente

Lors de la migration sur ce nouveau CAS, il a fallu ajuster notre script groovy déclenchant la MFA (script donné derrière la configuration cas.authn.mfa.groovy-script.location ).

Notre script tient compte notamment de l'IP du client pour déclencher ou non la MFA.

On récupérait jusque là cette IP par httpRequest.getRemoteAddr() - cependant, et depuis notre mise à jour CAS, ce script est maintenant aussi appelé lors de la validation du ticket par le service CAS. L'IP est alors l'ip du service et non pas du client/usager.

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.

Pour récupérer l'IP du client/usager, il faut donc utiliser dans le script groovy.

def ip = authentication.attributes["clientIpAddress"].get(0)

et non pas (plus, en tout cas pour nous)

def ip = httpRequest.getRemoteAddr()

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

Ticket Registry

Ticket Registry Redis

2 jours après la migration sur CAS 6.6.9, on constate une augmentation de consommation mémoire dans le Redis.
La consommation mémoire va en s'accroissant ; la bascule sur la 6.6.9 a été faite le 18 juillet.

Dans Redis, en plus des traditionnels CAS_TICKET:TGT-numero persistants, on trouve maintenant autant de CAS_PRINCIPAL:uid que d'utilisateurs.
Ces CAS_PRINCIPAL:uid sont des set qui regroupent l'ensemble des TGT des utilisateurs.

On note que le TTL de ces CAS_PRINCIPAL:uid est de 14 jours (on propose le remember-me de 14 jours) et qu'il n'est de fait d'aucune utilité puisqu'il sera allongé à chaque authentification/connexion de l'usager (sur un espace de 14 jours, on peut estimer que la plupart des utilisateurs se connectent au moins une fois).

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.

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

Dans le build.gradle de l'overlay on remplace

org.apereo.cas:cas-server-support-redis-ticket-registry

par 

org.apereo.cas:cas-server-support-mongo-ticket-registry

Puis dans /etc/cas/config/cas.properties on commente les paramètres du ticket-registry redis pour ajouter ceux de mongodb

cas.ticket.registry.mongo.client-uri=mongodb://localhost/cas-ticketregistry
cas.ticket.registry.mongo.crypto.enabled=true
cas.ticket.registry.mongo.crypto.signing.key=clef-generee-au-premier-demarrage
cas.ticket.registry.mongo.crypto.encryption.key=clef-generee-au-premier-demarrage

Un redémarrage CAS suffit à passer sur le nouveau ticket registry, évidemment toutes les sessions sont perdues lors de cette bascule.

MongoDB (et surtout l'implémentation du TicketRegistry avec celui-ci) semble bien convenir :

  • usage de TTL (expireAt) gérés et propores à MongoDB.
  • possibilité de requêter facilement les tickets d'un utilisateur donné, et ce sans rustine (contrairement au ticket registry redis donc)
  • par défaut sous debian, le mongodb a un stockage persistant (file storage sous /var/lib/mongodb/ )

Notes supplémentaires 

CAS positionne donc bien un expireAt (TTL) propre à MongoDB dans les enregistrements, notamment les TGT (collection ticketGrantingTicketsCollection).
Notre installation/configuration faisait que ces TGT étaient, même sans le remember-me d'actif, de 14 jours.

On avait en effet positionné le cas.ticket.tgt.primary.max-time-to-live-in-seconds à 14 jours, pour correspondre à cas.ticket.tgt.remember-me.time-to-kill-in-seconds

Si c'était peut-être une configuration adéquate, ça ne l'est plus (au moins en 6.6.9 et avec le ticket registry mongo) 

On a modifié cas.ticket.tgt.primary.max-time-to-live-in-seconds pour correspondre à 8H20 

On a ainsi les configurations suivantes à ce sujet: 

cas.ticket.tgt.primary.max-time-to-live-in-seconds=30000
cas.ticket.tgt.primary.time-to-kill-in-seconds=28800                                                                                                                                                                                
cas.ticket.tgt.remember-me.enabled=true
cas.ticket.tgt.remember-me.time-to-kill-in-seconds=1209600

Les expireAt (TTL) des TGT sont maintenant positionnés à 8H20 quand le RememberMe n'est pas actionné, et 14 jours sinon. 

On graphe dès à présent quelques métriques mongo via munin et https://github.com/comerford/mongo-munin (dont on a converti les scripts en python3 via 2to3).
D'ici quelques jours, on espère pouvoir ajouter à cette page wiki des graphes montrant que tout va bien sur notre CAS avec le ticket registry sous mongo.

A l'usage, nous ne ressentons (pour l'instant) pas d'impact de performances par rapport au fonctionnement avec Redis.

On note par contre que la demande de suppression des sessions CAS d'un utilisateur (cf le script en place et partagé sur ce même espace wiki) est maintenant très rapide (était très lent précédemment).

  • Aucune étiquette