Objectiu:
Fa uns anys vaig fer una sèrie d’entrades sobre com instal·lar Shibboleth 2.x. Això ha quedat una mica desfasat i, a la vegada, els protocols han evolucionat. La idea d’aquesta entrada és actualitzar el procediment d’instal·lació de la versió 3.x de l’IDP de Shibboleth i disposar d’un SP que autentiqui via IDP + LDAP i obtingui atributs de l’IDP.
Procediment:
El procediment no és senzill i hi ha moltes coses que poden anar, i segur que aniran, malament. A la vegada, ara que no ens llegeixen els de Shibboleth… no he vist mai documentació més feixuga i pesada de seguir. La veritat és que no sé quin és el seu usuari tipus, però si no es disposa de tres doctorats i dos màsters es fa molt difícil seguir el que ells consideren un howto bàsic. Dit això, preparem-nos per embrutar-nos i som-hi…
Ingredients necessaris:
Per fer aquesta instal·lació necessitarem:
- Dues màquines virtuals, una per a l’IDP3 (amb Debian 9/10/11, jetty9 i nginx) i una per a l’SP (amb una aplicació web sobre Apache + PHP, aquesta amb Debian 9/10/11).
- Les dues màquines amb sistema operatiu sense entorn gràfic i amb els paquets estàndard del sistema únicament (això és opcional, però recomanable en entorns de producció).
- La màquina de l’IDP s’anomenarà idp3.dom.lan amb IP 192.168.0.1/24
- La màquina de l’SP s’anomenarà sp1.dom.lan amb IP 192.168.0.2/24
- La configuració de l’arxiu /etc/hosts reflectirà aquestes dues anteriors configuracions de noms. Això ho farà portable en aquest exemple. En un entorn de producció real aquesta configuració la faríem, probablement al servidor DNS de l’empresa.
Aquest és un exemple de l’arxiu /etc/hosts (vàlid per a les dues màquines):
$ pico /etc/hosts 127.0.0.1 localhost 192.168.0.1 idp3.dom.lan sp1.dom.lan The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters
Primer pas: Instal·lar jetty9 a la màquina virtual de l’IDP
Com a servidor d’aplicacions utilitzarem jetty9. La versió 9.4 està disponible als repositoris de Debian. Això instal·larà també java en la versió openjdk, necessari per a l’execució del servidor:
$ apt install jetty9 libslf4j-java liblogback-java $ pico /etc/environment JAVA_HOME=/usr/lib/jvm/default-java $ pico /etc/systemd/system/multi-user.target.wants/jetty9.service ... [Service] ... ReadWritePaths=/opt/shibboleth-idp/metadata ReadWritePaths=/opt/shibboleth-idp/logs ... $ systemctl daemon-reload
Podem verificar que la instal·lació s’ha dut a terme correctament accedint a la pàgina web: http://idp3.dom.lan:8080. Hauríem de veure la pàgina per defecte de jetty9.
En la configuració de jetty9 afegirem els següents canvis:
$ pico /etc/jetty9/start.d/start.ini --module=http-forwarded $ service jetty9 restart
Segon pas: Instal·lar nginx i configurar proxy SSL/TLS cap a jetty9
Com a servidor proxy de peticions al jetty9 i, a la vegada, com a TLS/SSL terminator utilitzarem nginx. De nou, la instal·lació és senzilla ja que els paquets necessaris estan disponibles per a Debian:
$ apt install nginx
Podem verificar que la instal·lació ha anat correctament accedint a la pàgina: http://idp3.dom.lan. Hauríem de veure la pàgina per defecte de nginx.
En aquest punt és important notar que jetty9 escolta al port 8080 i que nginx escolta en el port 80. Cap dels dos està configurat encara per acceptar peticions segures mitjançant SSL/TLS al port 443.
Ens interessa que totes les peticions SSL/TLS es facin a l’nginx i que aquest les reenviï al jetty9. La configuració, doncs, per a nginx serà la següent (l’arxiu no existeix i s’ha de crear):
$ pico /etc/nginx/conf.d/default.conf upstream idp3 { server localhost:8080; } server { listen 0.0.0.0:80; listen 0.0.0.0:443 ssl; server_name idp3.dom.lan; if ($ssl_protocol = "") { rewrite ^ https://$server_name$request_uri? permanent; } ssl_certificate /etc/nginx/ssl/dom.lan.crt; ssl_certificate_key /etc/nginx/ssl/dom.lan.key; location / { proxy_pass http://idp3; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; proxy_redirect off; proxy_buffering off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Scheme $scheme; } }
Finalment reiniciem nginx:
$ service nginx restart
IMPORTANT: En aquesta configuració es defineixen les rutes dels arxius de certificat i clau que s’utilitzaran per a la configuració SSL/TLS. S’han utilitzat certificats wildcard *.dom.lan perquè simplifiquen la gestió de certificats. Aquests arxius es poden obtenir via openssl, via Let’s Encrypt, o a partir de certificats vàlids de l’empresa. Sigui com sigui aquests són els certificats que veurà el client quan connecti a idp3.dom.lan.
En aquest punt podem accedir a https://idp3.dom.lan i hauríem de veure la pàgina d’inici de jetty9 mitjançant una connexió segura SSL/TLS.
Tercer pas: Instal·lar Shibboleth IDP3/4 amb autenticació LDAP
En aquest punt estem preparats per instal·lar Shibboleth IDP. L’arxiu amb el programari necessari el descarregarem de la pàgina de Shibboleth. En aquest tutorial aquest és l’arxiu que s’ha utilitzat.
La instal·lació s’executa amb l’arxiu bin/install.sh. A continuació s’ha marcat en negreta els camps entrats a les preguntes de l’instal·lador. La instal·lació de la versió 4.x de l’IDP és exactament igual.
$ wget https://shibboleth.net/downloads/identity-provider/3.4.6/shibboleth-identity-provider-3.4.6.tar.gz $ tar zxf shibboleth-identity-provider-3.4.6.tar.gz $ cd shibboleth-identity-provider-3.4.6/ $ bin/install.sh Source (Distribution) Directory (press to accept default): [/root/shibboleth-identity-provider-3.4.6] Installation Directory: [/opt/shibboleth-idp] Hostname: [localhost.localdomain] idp3.dom.lan SAML EntityID: [https://idp3.dom.lan/idp/shibboleth] Attribute Scope: [localdomain] dom.lan Backchannel PKCS12 Password: ***** Re-enter password: ***** Cookie Encryption Key Password: ***** Re-enter password: ***** Warning: /opt/shibboleth-idp/bin does not exist. Warning: /opt/shibboleth-idp/edit-webapp does not exist. Warning: /opt/shibboleth-idp/dist does not exist. Warning: /opt/shibboleth-idp/doc does not exist. Warning: /opt/shibboleth-idp/system does not exist. Generating Signing Key, CN = idp3.dom.lan URI = https://idp3.dom.lan/idp/shibboleth ... ...done Creating Encryption Key, CN = idp3.dom.lan URI = https://idp3.dom.lan/idp/shibboleth ... ...done Creating Backchannel keystore, CN = idp3.dom.lan URI = https://idp3.dom.lan/idp/shibboleth ... ...done Creating cookie encryption key files... ...done Rebuilding /opt/shibboleth-idp/war/idp.war ... ...done BUILD SUCCESSFUL Total time: 35 seconds $ cd /opt $ chown jetty:jetty -R shibboleth-idp
Fet això, podem indicar a jetty9 on es troba la nova aplicació a desplegar:
$ pico /var/lib/jetty9/webapps/idp.xml <Configure class="org.eclipse.jetty.webapp.WebAppContext"> <Set name="war">/opt/shibboleth-idp/war/idp.war</Set> <Set name="contextPath">/idp</Set> <Set name="extractWAR">false</Set> <Set name="copyWebDir">false</Set> <Set name="copyWebInf">true</Set> <Set name="persistTempDirectory">false</Set> </Configure>
Les metadades inicials configurades per l’instal·lador de l’IDP posen la data de validesa a la data actual, cosa que no és correcte. S’ha de modificar l’arxiu /opt/shibboleth-idp/metadata/idp-metadata.xml i actualitzar aquest valor:
$ pico /opt/shibboleth-idp/metadata/idp-metadata.xml ... <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:req-attr="urn:oasis:names:tc:SAML:protocol:ext:req-attr" validUntil="2039-11-13T15:47:11.916Z" entityID="https://idp3.dom.lan/idp/shibboleth"> ...
Finalment per acabar la configuració inicial de Shibboleth IDP, configurarem l’LDAP com a backend d’autenticació. Aquesta versió de Shibboleth IDP ho porta gairebé tot fet. Només queda editar l’arxiu /opt/shibboleth-idp/conf/ldap.properties. A continuació només es mostren els valors que s’han modificat de l’arxiu original:
$ pico /opt/shibboleth-idp/conf/ldap.properties idp.authn.LDAP.authenticator = bindSearchAuthenticator idp.authn.LDAP.ldapURL = ldap://ldap.dom.lan idp.authn.LDAP.useStartTLS = true idp.authn.LDAP.useSSL = false idp.authn.LDAP.sslConfig = jvmTrust idp.authn.LDAP.returnAttributes = uid,mail,givenName,sn idp.authn.LDAP.baseDN = dc=dom,dc=lan idp.authn.LDAP.subtreeSearch = true idp.authn.LDAP.userFilter = (uid={user}) idp.authn.LDAP.bindDN = cn=ldapuser,cn=users,dc=dom,dc=lan idp.authn.LDAP.bindDNCredential = bindpassword
OPCIONAL: En l’arxiu /opt/shibboleth-idp/conf/access-control.xml podem afegir el rang d’adreces des d’on connectem per verificar l’estat (status) del servidor IDP.
$ pico /opt/shibboleth-idp/conf/access-control.xml ... <entry key="AccessByIPAddress"> <bean id="AccessByIPAddress" parent="shibboleth.IPRangeAccessControl" p:allowedRanges="<{ {'127.0.0.1/32', '::1/128', '192.168.0.0/16'} }" /> </entry> ...
En aquest punt podem reiniciar jetty9 i l’aplicació es desplegarà:
$ service jetty9 restart
Hauríem de poder accedir a l’adreça https://idp3.dom.lan/idp i veure una pantalla de Shibboleth IDP informant-nos que actualment no hi ha serveis configurats. Això és normal. A la vegada la pàgina https://idp3.dom.lan/idp/status ens mostrarà l’estat de l’IDP.
Quart pas: Instal·lar un SP
Amb l’IDP llest és el moment de configurar un SP (Service Provider) que contacti amb l’IDP per autenticar usuaris via LDAP.
En la màquina per a l’SP instal·larem apache2, php i el mòdul shib2 (tot i no especificar que volem l’apache2 aquest s’instal·larà igual perquè es tracta d’una dependència de php):
$ apt install libapache2-mod-php libapache2-mod-shib
A continuació deshabilitarem la pàgina per defecte i instal·larem la nova amb una aplicació php que autentiqui amb l’IDP de Shibboleth:
$ a2dissite 000-default.conf $ pico /etc/apache2/sites-available/sp1.conf <VirtualHost *:80> ServerName https://sp1.dom.lan:443 UseCanonicalName on DocumentRoot /var/www/html <Directory /var/www/html> AuthType shibboleth ShibRequireSession On require shibboleth </Directory> ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost> $ a2ensite sp1.conf $ service apache2 restart
Com es pot veure a la configuració l’accés sempre es farà via https utilitzant SSL/TLS. En aquest exemple el nostre SSL/TLS terminator és l’nginx que hem configurat en la màquina IDP. A continuació actualitzem la configuració per afegir aquest nou servei web:
$ cat /etc/nginx/conf.d/default.conf upstream idp3 { server localhost:8080; } upstream sp1 { server 192.168.0.2:80; } server { listen 0.0.0.0:80; listen 0.0.0.0:443 ssl; server_name idp3.dom.lan; if ($ssl_protocol = "") { rewrite ^ https://$server_name$request_uri? permanent; } ssl on; ssl_certificate /etc/nginx/ssl/dom.lan.crt; ssl_certificate_key /etc/nginx/ssl/dom.lan.key; location / { proxy_pass http://idp3; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; proxy_redirect off; proxy_buffering off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Scheme $scheme; } } server { listen 0.0.0.0:80; listen 0.0.0.0:443 ssl; server_name sp1.dom.lan; if ($ssl_protocol = "") { rewrite ^ https://$server_name$request_uri? permanent; } ssl_certificate /etc/nginx/ssl/dom.lan.crt; ssl_certificate_key /etc/nginx/ssl/dom.lan.key; location / { proxy_pass http://sp1; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; proxy_redirect off; proxy_buffering off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Scheme $scheme; } }
A continuació configurarem el mòdul shib2. Els arxius de configuració es troben a la carpeta /etc/shibboleth. Primer crearem el certificat i la clau necessaris per a la comunicació amb l’IDP.
$ shib-keygen -f -u _shibd -h sp1.dom.lan -y 10 -e https://sp1.dom.lan/shibboleth -o /etc/shibboleth/
A continuació modificarem l’arxiu /etc/shibboleth/shibboleth.xml tot especificant l’adreça de l’IDP (només es mostren els canvis específics):
$ pico /etc/shibboleth/shibboleth2.xml ... <ApplicationDefaults entityID="https://sp1.dom.lan/shibboleth" /> ... <SSO entityID="https://idp3.dom.lan/idp/shibboleth" discoveryProtocol="SAMLDS" discoveryURL="https://ds.example.org/DS/WAYF"> SAML2 </SSO> ... <MetadataProvider type="XML" url="https://idp3.dom.lan/idp/shibboleth" backingFilePath="idp-metadata.xml" reloadInterval="7200"> </MetadataProvider> ... <CredentialResolver type="File" key="sp-key.pem" certificate="sp-cert.pem"/> ...
És important notar que ja no utilitzem el protocol depreciat SAML1. En aquest punt es poden reiniciar els serveis shibd i apache2:
$ service shibd restart && service apache2 restart
Afegir l’SP a l’IDP
A continuació s’ha de configurar l’IDP, de nou, per a afegir aquest nou SP com a servei disponible a l’autenticació. Això, en la màquina de l’IDP, ho farem modificant l’arxiu /opt/shibboleth-idp/conf/metadata-providers.xml:
$ cat /opt/shibboleth-idp/conf/metadata-providers.xml ... <MetadataProvider id="sp1" xsi:type="FileBackedHTTPMetadataProvider" backingFile="%{idp.home}/metadata/sp1-metadata.xml" metadataURL="https://sp1.dom.lan/Shibboleth.sso/Metadata"> </MetadataProvider> ... $ service jetty9 restart
Si tot ha anat bé ja podem accedir a l’adreça https://sp1.dom.lan i hauríem de ser redirigits a l’IDP per autenticar-nos. Després d’entrar el nom d’usuari i la contrasenya hauríem de ser redirigits a la pàgina d’inici d’apache2, tal i com hem configurat.
Cinquè pas: Pas d’atributs bàsics
Fins aquest punt tenim un IDP 3 configurat i un SP que pot autenticar recursos. En aquesta secció configurarem l’IDP perque enviï atributs a l’SP. En concret enviarem 4 atributs: uid, givenname, sn i mail retornats pel servidor LDAP.
A la màquina on hi ha l’SP configurem el següent:
$ pico /etc/shibboleth/attribute-map.xml ... <Attribute name="urn:mace:dir:attribute-def:uid" id="uid" /> <Attribute name="urn:oid:0.9.2342.19200300.100.1.1" id="uid" /> <Attribute name="urn:mace:dir:attribute-def:givenName" id="givenName" /> <Attribute name="urn:oid:2.5.4.42" id="givenName" /> <Attribute name="urn:mace:dir:attribute-def:sn" id="sn" /> <Attribute name="urn:oid:2.5.4.4" id="sn" /> <Attribute name="urn:mace:dir:attribute-def:mail" id="mail" /> <Attribute name="urn:oid:0.9.2342.19200300.100.1.3" id="mail" /> ...
I, finalment, a la configuració de l’IDP canviarem l’arxiu /opt/shibboleth-idp/attribute-resolver.xml per afegir el mapeig d’atributs obtinguts de l’LDAP:
$ cd /opt/shibboleth-idp/conf $ mv attribute-resolver.xml attribute-resolver-original.xml $ mv attribute-resolver-ldap.xml attribute-resolver.xml $ pico /opt/shibboleth-idp/conf/attribute-resolver.xml ... <AttributeDefinition id="givenName" xsi:type="Simple"> <InputDataConnector ref="myLDAP" attributeNames="givenName"/> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:givenName" encodeType="false" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:2.5.4.42" friendlyName="givenName" encodeType="false" /> </AttributeDefinition> <AttributeDefinition id="sn" xsi:type="Simple"> <InputDataConnector ref="myLDAP" attributeNames="sn"/> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:sn" encodeType="false" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:2.5.4.4" friendlyName="sn" encodeType="false" /> </AttributeDefinition> ... ### Eliminar la línia <trustFile="%{idp.attribute.resolver.LDAP.trustCertificates}", nosaltres fem serveir jvmTrust i no tenim el certificat del servidor LDAP.
Els atributs mail i uid ja són a la configuració per defecte, pel que no cal afegir-los. Ara que hem definit quins són els atributs que volem passar hem d’establir les regles de a qui els volem passar. Això ho farem modificant l’arxiu /opt/shibboleth-idp/attribute-filter.xml:
$ pico /opt/shibboleth-idp/conf/attribute-filter.xml ... <AttributeFilterPolicy id="sp1"> <PolicyRequirementRule xsi:type="OR"> <Rule xsi:type="Requester" value="https://sp1.uda.ad" /> <Rule xsi:type="Requester" value="https://sp1.uda.ad/shibboleth" /> </PolicyRequirementRule> <AttributeRule attributeID="uid"> <PermitValueRule xsi:type="ANY" /> </AttributeRule> <AttributeRule attributeID="sn"> <PermitValueRule xsi:type="ANY" /> </AttributeRule> <AttributeRule attributeID="givenName"> <PermitValueRule xsi:type="ANY" /> </AttributeRule> <AttributeRule attributeID="mail"> <PermitValueRule xsi:type="ANY" /> </AttributeRule> </AttributeFilterPolicy> ...
En aquest punt està tot llest. Podem reiniciar jetty9 i, després d’autenticar-nos de nou al servei, podem accedir a la pàgina https://sp1.dom.lan/Shibboleth.sso/Session i veure si els atributs s’han passat correctament. Altrament, també podem mostrar la variable $_SERVER de php i veure quines variables ha creat shibboleth.
Pas opcional: Deshabilitar el consentiment de pas d’atributs
En l’entorn local que estem configurant potser no és necessari el consentiment dels usuaris en el pas d’atributs entre IDP i SP. Això és pot deshabilitar a l’IDP modificant l’arxiu /opt/shibboleth-idp/conf/relying-party.xml:
$ pico /opt/shibboleth-idp/conf/relying-party.xml ### Eliminar p:postAuthenticationFlows="attribute-release" allà on aparegui
Finalment, com sempre, reiniciar el servidor jetty9.