Grouper
Introduzione
Grouper e' un promettentissimo progetto di Internet2 per la gestione e la delega della gestione dei gruppi. Grouper e' veramente completo: legge la base dati utenti da ldap o da un db relazionale, crea i gruppi (da linea di comando, tramite web service o con interfaccia web) e poi li immette (provision) in ldap.
Cosa funziona gia'
- creare i gruppi da ui e da shell;
- il client web service funziona in una caso banale;
- ho chiesto alla ml per avere due tipi di gruppi con provisioning diverso;
- provision con schema posixGroup (vedi gli hooks per creare il gidnumber) -- rifatto dopo la migrazione ad oracle grazie all'aiuto di Chris Hyzer
Cosa manca da capire
Funzionamento dei privilegi di delega.
Cosa manca da fare
- passare a oracle (fatto);
- shibbolettizzare grouper;
- aggiungere anche le objectClass samba;
- modificare le acl di ldap per
- permettere a grouper di inserire i gruppi con privilegi minori di admin (fatto salvo l'aggiunta dello ou)
- nascondere i gruppi
- importare i gruppi presenti su domain e ahab;
- modificare il delta per non sovrascrivere gli attributi utenti impostati da grouper (variante: modificare il raccoglitore per prendere questi dati da grouper);
- creare gli script di "inventario" per ricreare eventualmente i gruppi;
- inizializzare i privilegi
Cosa ho fatto
Le istruzioni che ho seguito sono:
- https://spaces.internet2.edu/display/Grouper/Grouper+Hosted+on+a+Cloud+Server+%282%29
- https://spaces.internet2.edu/display/GrouperWG/Grouper+Product
Il diagramma grouper ed ldap illustra quali sono i componenti di grouper e come si relazionano con ldap e eventuali db relazionali.
Grouper mantiene i gruppi in un db locale che adesso e' oracle su oracle10g.dmz-int.unimo.it.
Ogni componente di grouper (le api, il tool di provisioning, la ui, il web service) e' in un pacchetto diverso: l'unico fondamentale sono le api.
Per usare gsh è meglio installare rlwrap poi:
sudo apt-get install rlwrap rlwrap gsh.sh
Come connettere ldap con grouper
Le api dialogano in lettura con lo ldap tramite le impostazioni in grouper.api/conf/sources.xml.
Lo stesso sources.xml viene incluso nelle ui. Se si cambia sources.xml, ricordarsi di lanciare
cd /opt/grouper.ui ant war
per la ui e spostare il war risultante nella cartella webapps di tomcat.
Come connettere grouper con ldap
Il provisioning (cioe' la scrittura dei gruppi in ldap) la fa il componente ldapcng. ldappcng deve essere scompattato sopra grouper.api (i file di configurazione di ldappng devono finire nella cartella conf delle api, i jar nella cartella lib/custom).
I file di rilievo sono:
- ldappc-internal.xml: bootstrap di velocity, probabilmente per le sostituzioni;
- ldappc.properties: file di definizioni per vt-ldap per il provisioning verso i server ldap; serve al file ldappcng.xml;
- ldappc-service.xml: definisce il servizo ldappcng (in cui inietta il file di proprietà ldappc.properties) e definisce il grouper.AttributeResolver (sempre con il file di proprietà);
- ldappcng.xml: i template dei gruppi, sembra un file php, in cui fare le sostituzioni dei valori che provengono dal file successivo; genera lo SPML;
- ldappc-resolver.xml: il resolver dei valori citati nel file precedente. Si tratta della stessa sintassi (e dello stesso programma) dell'attribute resolver di shibboleth;
- ldappc-ldap.xml: il file di configurazione per vt-ldap.
ldappc-resolver.xml
In questo file ho modificato la definizione del cn per i cn composti. Infatti i cn di grouper sono del tipo: cn=unimore:sia:test e con il default:
<resolver:AttributeDefinition id="cn" xsi:type="ad:Simple" sourceAttributeID="extension"> <resolver:Dependency ref="GroupDataConnector" /> </resolver:AttributeDefinition>
vengono tradotti in ldap con dei valori multipli come:
cn=unimore cn=unimore:sia cn=unimore:sia:test
Questi valori multipli confondono openldap quando c'e' da genera il dn.
La definizione corretta di cn e':
<resolver:AttributeDefinition id="cn" xsi:type="ad:Simple" > <resolver:Dependency ref="cn-extension" /> <resolver:Dependency ref="cn-name" /> </resolver:AttributeDefinition> <resolver:AttributeDefinition id="cn-extension" xsi:type="ad:Simple" sourceAttributeID="extension"> <resolver:Dependency ref="GroupDataConnector" /> </resolver:AttributeDefinition> <resolver:AttributeDefinition id="cn-name" xsi:type="ad:Simple" sourceAttributeID="name"> <resolver:Dependency ref="GroupDataConnector" /> </resolver:AttributeDefinition>
In questo modo la definizione del dn e del cn sono allineati su sourceAttributeID="name". In piu' nella entry del gruppo il cn compare a valori multipli.
Se un valore e' sufficiente, allora basta cambiare:
<resolver:AttributeDefinition id="cn" xsi:type="ad:Simple" sourceAttributeID="name"> <resolver:Dependency ref="GroupDataConnector" /> </resolver:AttributeDefinition>
Per avere la lista dei membri dei gruppi, accertarsi che il source per members-jdbc punti a una datasource corretta. Per un motivo che non mi e' chiaro solo il server ldap funziona (avrei immaginato che il default, jdbc, cioe' il database di grouper andasse bene).
<resolver:AttributeDefinition id="members-jdbc" xsi:type="grouper:Member" sourceAttributeID="members"> <resolver:Dependency ref="GroupDataConnector" /> <grouper:Attribute id="id" source="unimore" /> </resolver:AttributeDefinition>
Per avere un provisioning diverso per due tipi di gruppi (ad esempio groupOfNames e PosixGroup), bisogna definire due groupMapping in ldappc-resolver.xml, con un filtro tipo:
<resolver:DataConnector id="GroupDataConnector" xsi:type="grouper:GroupDataConnector"> <grouper:GroupFilter xsi:type="grouper:OR"> <grouper:GroupFilter xsi:type="grouper:StemName" name="unimore:plains" scope="SUB" /> <grouper:GroupFilter xsi:type="grouper:StemName" name="etc" scope="SUB" /> </grouper:GroupFilter> <grouper:Attribute id="members" /> <grouper:Attribute id="groups" /> </resolver:DataConnector>
e
<resolver:DataConnector id="SambaGroupDataConnector" xsi:type="grouper:GroupDataConnector"> <grouper:GroupFilter xsi:type="grouper:StemName" name="unimore:domains" scope="SUB" /> <grouper:Attribute id="members" /> <grouper:Attribute id="groups" /> </resolver:DataConnector>
Dopo bisogna ricordarsi di includere entrambi i DataConnector in tutte le definizioni di attributi, tipo:
<resolver:AttributeDefinition id="description" xsi:type="ad:Simple"> <resolver:Dependency ref="GroupDataConnector" /> <resolver:Dependency ref="SambaGroupDataConnector" /> </resolver:AttributeDefinition>
Bisogna duplicare la definizione dell'objectClass e del dn. Nell'objectClass bisogna aver cura di introdurre almeno un objectClass per ogni tipo gruppo che non sia presente nell'altro gruppo. Questa objectClass permette a grouper di capire la differenza tra i gruppi tramite la chiave 'identifiyngAttribute' in ldappcng.xml.
Da notare:
<resolver:AttributeDefinition id="hasMember" xsi:type="grouper:Member" sourceAttributeID="members"> <resolver:Dependency ref="GroupDataConnector" /> <resolver:Dependency ref="SambaGroupDataConnector" /> <grouper:Attribute id="name" source="unimore" /> </resolver:AttributeDefinition>
<resolver:AttributeDefinition id="members-jdbc" xsi:type="grouper:Member" sourceAttributeID="members"> <resolver:Dependency ref="GroupDataConnector" /> <resolver:Dependency ref="SambaGroupDataConnector" /> <grouper:Attribute id="id" source="unimore" /> </resolver:AttributeDefinition>
Nella prima definizione 'name' coincide con il cn dei membri, per creare delle entry tipo:
hasMember: tizio hasMember: caio
Nel secondo 'id' e' il dn per delle entry tipo:
member: uid=tizio,ou=people,dc=unimore,dc=it member: uid=caio,ou=people,dc=unimore,dc=it
Nel file ldappcng.xml bisogna creare una definizione per ciascun gruppo, ad esempio:
<object id="group" authoritative="true"> <identifier ref="group-dn" baseId="${groupsOU}"> <identifyingAttribute name="objectClass" value="groupOfNames" /> </identifier> <attribute name="objectClass" ref="group-objectclass-eduMember" /> <attribute name="cn" /> <attribute name="description" /> <attribute name="hasMember" ref="hasMember" /> <attribute name="isMemberOf" ref="groupIsMemberOf" /> <references name="member" emptyValue="" > <reference ref="members-jdbc" toObject="member" /> <reference ref="members-g:gsa" toObject="group" /> </references> </object> <object id="samba-group" authoritative="true"> <identifier ref="group-samba-dn" baseId="${groupsOU}"> <identifyingAttribute name="objectClass" value="unimoreGroup" /> </identifier> <attribute name="objectClass" ref="group-objectclass-unimoreGroup" /> <attribute name="cn" /> <attribute name="description" /> <attribute name="gidNumber" /> <attribute name="sambaGroupType" /> <attribute name="sambaSID" /> <attribute name="isMemberOf" ref="groupIsMemberOf" /> <attribute name="memberUid" ref="hasMember" /> <attribute name="hasMember" ref="hasMember" /> <references name="member" emptyValue="" > <reference ref="members-jdbc" toObject="member" /> <reference ref="members-g:gsa" toObject="group" /> </references> </object>
Gli attributi gidNumber, sambaGroupType e sambaSID sono definiti nel ldappc-resolver.xml come:
<resolver:AttributeDefinition id="gidNumber" xsi:type="ad:Simple"> <resolver:Dependency ref="SambaGroupDataConnector" /> </resolver:AttributeDefinition>
Sincronizzare un gruppo:
bin/gsh.sh -ldappcng -sync etc:sysadmingroup
sincronizzare un utente:
bin/gsh.sh -ldappcng -sync malvezzi
Attributi virtuali
Servono per visualizzare nella ui i subjects in modo diverso che con i semplici campi id (uid) name (cn) o description (description), ma ad esempio con "${subject.cn} ${subject.description}".
Modificare lo sources.xml nella sezione della sorgente dati di interesse (unimore -- nel nostro caso) e aggiungere:
<init-param> <param-name>subjectVirtualAttribute_1_displayName0</param-name> <param-value>${subject.name}, ${subject.description}</param-value> </init-param>
Questo attributo si può testare in gsh con subject.getAttributeValue("displayName0")
Poi bisogna editare il file /opt/grouper.ui/conf/resources/grouper/media.properties nelle due sezioni (molti distanti tra loro) oppure il file /opt/grouper.ui/conf/grouper-ui.properties nella versione 2.2.1:
subject.display.default=displayName0 [...] #grouperUi.subjectImg.sourceId.0 = pennperson #grouperUi.subjectImg.image.0 = user.png ## screen EL has "subject" as an object #grouperUi.subjectImg.screenEl.0 = ${subject.description}
grouperUi.subjectImg.sourceId.0 = unimore grouperUi.subjectImg.image.0 = user.png # screen EL has "subject" as an object grouperUi.subjectImg.screenEl.0 = ${subject.getAttributeValue("displayName0")}
La prima server per la ui, la seconda per la lite-ui.
Ho anche provato con una modifica nelle dinamic tiles (sempre in media.properties):
unimore.subject.view=/WEB-INF/jsp/custom/unimoreSubjectView.jsp
con il seguente file /opt/grouper.ui/webapp/WEB-INF/jsp/custom/unimoreSubjectView.jsp:
<%@include file="/WEB-INF/jsp/include.jsp"%> <tiles:importAttribute ignore="true"/><c:set var="attrKey" value="*subject.display.${viewObject.source.id}"/><c:if test="${empty mediaMap[attrKey]}"><c:set var="attrKey" value="subject.display.default"/></c:if> <c:if test="${viewObject.isGroup}"><img <grouper:tooltip key="group.icon.tooltip"/> src="grouper/images/group.gif" class="groupIcon" alt="Group" /></c:if><c:if test="${empty inLink}"><span class="<c:out value="${viewObject.subjectType}"/>Subject"></c:if><c:out value="${viewObject['cn']}" />[<c:out value="${viewObject['description']" /> ] <c:if test="${empty inLink}"></c:if>
ma sembra che venga ignorato.
Shibboleth
Per disabilitare l'autenticazione gestita da tomcat per grouper, modificare il file: /opt/grouper.ui/web-xml-merge-tags.xml commentare le righe:
<tag name="security-constraint" key="url-pattern"/> <tag name="login-config" type="single"/> <tag name="security-role" key="role-name"/>
Difficolta'
La difficolte' (risolte) del provisioning sono:
- problema di accessi (acl) su ldap;
- conflitti di schema con openldap;
- aggiunta di attributi calcolati durante la generazione dei gruppi (gidNumber -> vedi usare gli hook)
- Provisioning diverso per stem diversi: ad esempio lo stem etc:sysadmingroup non deve avere gli attributi posix, mentre lo stem it.unimore si'.
Import/export di dati
Si possono creare gruppi in grouper con:
- la shell manualmente uno a uno
- la interfaccia grafica
- i web service (esiste anche un client java gia' pronto)
- il servizio import (non l'ho guardato)
- la shell con il comando -xmlimport.
Upgrade di versione
Istruzioni sul sito di grouper
grouper.api
Ricordarsi gli hook: copiare il build xml ed eseguire ant.
grouper.ui
Modificare il file build.properties.
Copiare dalla versione precedenti il file conf/grouper-ui.properties (contiene il riferimento al logo) ed il file conf/grouperText/grouper.text.en.us.base.properties (nome dell'istituzione, chiave institutionName).
Copiare il logo unimore_logo.png in webapp/grouperExternal/public/assets/images/
Andare in ./dist/grouper/WEB_INF/web.xml ed eliminare tutte le sezioni: security-constraint, login-config, and security-role (altrimenti c'è sempre un errore 403). Meglio ancora fare come scritto nella sezione "Shibboleth".
ant war
grouper.ws
Modifcare build properties in grouper-ws
#grouper base dir grouper.dir=/opt/grouper
poi ricreare il jar:
cd grouper-ws sudo JAVA_HOME=/usr/lib/jvm/java-6-sun ant
poi modificare il soft link
grouper-2.5
grouper-2.5 è un container docker. Ho seguito il setup manuale maturity level 1 (cioé con il dockerfile).
In /opt/grouperContainer ci sono tre contanier: grouper-daemon, grouper-ui e grouper-ws.
docker pull i2incommon/grouper:4.12.0 sudo docker build -t grouper-4.12.0 /opt/grouperContainer sudo ./grouperUiDockerRun.sh sudo docker exec -it grouper-ui /bin/bash sudo docker ps --all sudo docker container stop grouper-ui sudo docker rm -f grouper-ui docker system df docker system prune
Si possono copiare file dai container alla VM con:
sudo docker cp grouper-ui:/log4j.tar
Per i log verificare che l'utente tomcat abbia lo stesso uid nel container e nella VM:
$ sudo groupadd -r -g 994 tomcat $ sudo useradd -r -u 996 -g 994 tomcat
Altre istruzioni soprattutto per i ws: https://wiki.unimore.it/index.php/Setup_grouper25#Web_Services
Aggiornare lo schema del db, vedi punto 32.
Verifica la versione in: Home -> Miscellaneous -> Configure.