Obsah

Shibboleth IdP

Stiahneme a rozbalíme inštalačný balíček:

wget https://shibboleth.net/downloads/identity-provider/3.4.1/shibboleth-identity-provider-3.4.1.tar.gz
tar -xzf shibboleth-identity-provider-3.4.1.tar.gz

Spustíme inštaláciu:

./shibboleth-identity-provider-3.4.1/bin/install.sh

Výstup:

Source (Distribution) Directory (press <enter> to accept default): [/root/shibboleth-identity-provider-3.4.1]

Installation Directory: [/opt/shibboleth-idp]

Hostname: [demoidp.sanet.sk]

SAML EntityID: [https://demoidp.sanet.sk/idp/shibboleth]

Attribute Scope: [sanet.sk]

Backchannel PKCS12 Password: <Heslo3>
Re-enter password: <Heslo3>
Cookie Encryption Key Password: <Heslo4>
Re-enter password: <Heslo4>
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 = demoidp.sanet.sk URI = https://demoidp.sanet.sk/idp/shibboleth ...
...done
Creating Encryption Key, CN = demoidp.sanet.sk URI = https://demoidp.sanet.sk/idp/shibboleth ...
...done
Creating Backchannel keystore, CN = demoidp.sanet.sk URI = https://demoidp.sanet.sk/idp/shibboleth ...
...done
Creating cookie encryption key files...
...done
Rebuilding /opt/shibboleth-idp/war/idp.war ...
...done

BUILD SUCCESSFUL
Total time: 3 minutes 8 seconds

Presunieme inštalačný adresár do /opt (zatial neviem načo) a nastavíme opravnenia:

mv shibboleth-identity-provider-3.4.1 /opt/
chown -R idp:idp /opt/shibboleth-id*

Reštartujeme Jetty, servlet by sa mal nahrať:

systemctl restart jetty

Stav IdP môžeme zobraziť príkazom:

/opt/shibboleth-idp/bin/status.sh

Funkčnost je možné overiť aj zobrazením stránky v prehliadači: https://demoidp.sanet.sk/idp

Konfigurácia IdP

Prepnúť sa na používateľa idp:

su - idp

Aby Shibboleth poznal cestu k Jave aj pod neprivilegovaným učtom musíme ju špecifikovať:

JAVA_HOME=/usr/java/latest /opt/shibboleth-idp/bin/reload-service.sh -id shibboleth.ReloadableAccessControlService

conf/idp.properties

V konfigurácii idp nastavíme premennú idp.cookie.secure na true (odkomentovať a zmeniť na true):

vim /opt/shibboleth-idp/conf/idp.properties
idp.cookie.secure = true

a reštartujeme jetty (ako root)

systemctl restart jetty

conf/access-control.xml

Tu je možné obmedziť prístup k stránkam so špeciálnou funkcionalitou (pre adminov):

vim /opt/shibboleth-idp/conf/access-control.xml
<util:map id="shibboleth.AccessControlPolicies">
 
        <entry key="AccessByIPAddress">
            <bean id="AccessByIPAddress" parent="shibboleth.IPRangeAccessControl"
                p:allowedRanges="#{ {'127.0.0.1/32', '194.160.44.13/32'} }" />
        </entry>
...

Znovunačitame konfigurácie (pokiaľ robíme pod userom idp musíme stále zadávať JAVA_HOME):

JAVA_HOME=/usr/java/latest /opt/shibboleth-idp/bin/reload-service.sh -id shibboleth.ReloadableAccessControlService

Teraz by malo byť možné z vybraných IP zobraziť napríklad info o stave idp: https://demoidp.sanet.sk/idp/status

conf/ldap.properties

Zaitaľ sa mi nepodarilo nakonfigurovať IdP tak aby mu stačilo spojenie s LDAP bez SSL. Možno je to bug: http://shibboleth.net/pipermail/users/2015-August/023536.html

Pre použitie SSL treba stiahnuť ssl certifikat z LDAP servera:

openssl s_client -showcerts -connect io.fpv.umb.sk:636 </dev/null 2>/dev/null|openssl x509 -outform PEM > /opt/shibboleth-idp/credentials/ldap-server.crt
vim /opt/shibboleth-idp/conf/ldap.properties
idp.authn.LDAP.authenticator                   = bindSearchAuthenticator

idp.authn.LDAP.ldapURL                         = ldaps://ldap.umb.sk:636
idp.authn.LDAP.useStartTLS                     = false
idp.authn.LDAP.useSSL                          = true

idp.authn.LDAP.connectTimeout                  = PT3S
idp.authn.LDAP.responseTimeout                 = PT3S

idp.authn.LDAP.sslConfig                       = certificateTrust
idp.authn.LDAP.trustCertificates               = %{idp.home}/credentials/ldap-server.crt

idp.authn.LDAP.baseDN                          = ou=users,dc=ldap,dc=umb,dc=sk
idp.authn.LDAP.subtreeSearch                   = true
idp.authn.LDAP.userFilter                      = (uid={user})
idp.authn.LDAP.bindDN                          = cn=demoidp,ou=system,ou=users,dc=ldap,dc=umb,dc=sk
idp.authn.LDAP.bindDNCredential                = ------- heslo pre ldap usera -------

idp.attribute.resolver.LDAP.ldapURL             = %{idp.authn.LDAP.ldapURL}
idp.attribute.resolver.LDAP.connectTimeout      = %{idp.authn.LDAP.connectTimeout:PT3S}
idp.attribute.resolver.LDAP.responseTimeout     = %{idp.authn.LDAP.responseTimeout:PT3S}
idp.attribute.resolver.LDAP.baseDN              = %{idp.authn.LDAP.baseDN:undefined}
idp.attribute.resolver.LDAP.bindDN              = %{idp.authn.LDAP.bindDN:undefined}
idp.attribute.resolver.LDAP.bindDNCredential    = %{idp.authn.LDAP.bindDNCredential:undefined}
idp.attribute.resolver.LDAP.useStartTLS         = %{idp.authn.LDAP.useStartTLS:true}
idp.attribute.resolver.LDAP.trustCertificates   = %{idp.authn.LDAP.trustCertificates:undefined}
idp.attribute.resolver.LDAP.searchFilter        = (uid=$resolutionContext.principal)

conf/metadata-providers.xml

vim /opt/shibboleth-idp/conf/metadata-providers.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file is an EXAMPLE metadata configuration file. -->
 
<MetadataProvider id="ShibbolethMetadata" xsi:type="ChainingMetadataProvider"
    xmlns="urn:mace:shibboleth:2.0:metadata"
    xmlns:resource="urn:mace:shibboleth:2.0:resource"
    xmlns:security="urn:mace:shibboleth:2.0:security"
    xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd
                        urn:mace:shibboleth:2.0:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd
                        urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd
                        urn:oasis:names:tc:SAML:2.0:metadata http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd">
 
    <!-- MEtadata federacie - maxRefreshDelay urcuje ako casto sa budu metadata aktualizovat -->
    <MetadataProvider
        id="safeid-metadata"
        xsi:type="FileBackedHTTPMetadataProvider"
        backingFile="%{idp.home}/metadata/safeid.xml"
        metadataURL="https://www.safeid.sk/metadata/metadata.safeid.sk.xml"
        maxRefreshDelay="PT2H">
        <MetadataFilter xsi:type="SignatureValidation" requireSignedRoot="true"
            certificateFile="%{idp.home}/credentials/SAFEID_metadata_signer.pem" />
    </MetadataProvider>
</MetadataProvider>

Stiahnuť verejný kľúč pre kontrolu podpisu metadát:

cd /opt/shibboleth-idp/credentials
wget https://www.safeid.sk/metadata/cert/SAFEID_metadata_signer.pem

Reštart služby pre načítanie metadát:

JAVA_HOME=/usr/java/latest /opt/shibboleth-idp/bin/reload-service.sh -id shibboleth.MetadataResolverService

Kontrola, či sa metadata stiahli:

ls -l /opt/shibboleth-idp/metadata/

Vo výpise by sa mal nachádzať súbor safeid.xml s aktuálnym časom vytvorenia.

conf/attribute-resolver.xml

cd /opt/shibboleth-idp/conf
mv attribute-resolver-ldap.xml attribute-resolver.xml
TODO: Doplnit alebo nalinkovat obsah suboru attribute-resolver.xml

conf/attribute-filter.xml

Tu definujeme, ktoré atribúty budeme uvolnovať a komu.

vim /opt/shibboleth-idp/conf/attribute-filter.xml
TODO: Doplnit alebo nalinkovat obsah suboru attribute-filter.xml

metadata/idp-metadata.xml

Tu treba doplniť metadata.

vim /opt/shibboleth-idp/metadata/idp-metadata.xml
TODO: Doplnit alebo nalinkovat obsah suboru idp-metadata.xml

Po reštarte jetty budú metadata dostupné na adrese https://demoisp.sanet.sk/idp/shibboleth. Metadata je potrebné validovať vo validátore: https://mdr.safeid.sk/saml-validator/

Persistentný identifikátor / eduPersonTargetedID

Identifikátor sa generuje pre každý pár používateľ-služba a slúži na ochranu súkromia používatela (služba zároveň vie, že sa jedná o rovnakého používateľa). Kedže sa pre každú sluzbu generuje iný identifikátor a tento identifikátor je nahodny reťazec, nie je jednoducho možne pre prevádzkovateľa služby používateľa identifikovať ale zároven to umožäuje zo strany služby ukladať rôzne personalizované nastavenia atď. V tom je hlavný rozdiel oproti identifikátoru eduPersonPrinciaplName, ktorý má spravidla hodnotu login@domena.

Podpora pre túto funkcionalitu nie je povinná, ale je doporučovaná. Podobný efekt je možné dosiahnuť používaním generovaného čísla namiesto loginu používateľa v eduPersonPrinciaplName. Samorejme to stráca zmysel ak spolu s týmto identifikátorom posielame ďalšie údaje ako meno, mail atď.

Navyše niektoré z konfigurašnych krokov, ktoré budú potrebné pre jeho spravádzkovanie bude potrebné aj tak urobiž kvôli ukladaniu súhlasov s poskytovaním atribútov.

Pre ukladanie údajov je odporúčané použiť databázu MySQL (nie MariaDB).

MySQL

Nainštalovať repozitár od Oracle - MySQL server:

yum install https://repo.mysql.com//mysql80-community-release-el7-1.noarch.rpm
yum update
yum install mysql-community-server

Spustiť server:

systemctl enable mysqld
systemctl startmysqld

Po spustení server vygeneruje heslo pre roota, ktoré sa nachádza v logu:

cat /var/log/mysqld.log | grep password

Spustíme procedúru pre zabezpečenie databazy, v rámci ktorej zmeníme heslo pre roota (pozor, mysql8 má štandardne aktívne dosť prísne požiadavky na zložitosť hesla).

Aby sa nám s DB ľahšie pracovalo môžeme uložiť prihlasovacie údaje do súboru .my.cnf:

vim /root/.my.cnf

obsah súboru:

[client]
user=root
password="4T............1I"

Nastavíme oprávnenia pre súbor:

chmod go-rwx .my.cnf

Prihlásime sa do cmd clienta a spustíme SQL príkazy pre vytvorenie databázy a používateľa (nezabudneme na silné heslo):

mysql
SET NAMES 'utf8';
SET CHARACTER SET utf8;
CHARSET utf8;
CREATE DATABASE IF NOT EXISTS shibboleth CHARACTER SET=utf8;
CREATE USER 'shibboleth'@'localhost' IDENTIFIED BY 'jeGYjU....hFf7S';
GRANT ALL ON shibboleth.* TO 'shibboleth'@'localhost';
FLUSH PRIVILEGES;

Vytvoríme novú tabuľku v datábaze shibboleth:

USE shibboleth;
CREATE TABLE IF NOT EXISTS `shibpid` (
  `localEntity` VARCHAR(255) NOT NULL,
  `peerEntity` VARCHAR(255) NOT NULL,
  `principalName` VARCHAR(255) NOT NULL DEFAULT '',
  `localId` VARCHAR(255) NOT NULL,
  `persistentId` VARCHAR(50) NOT NULL,
  `peerProvidedId` VARCHAR(255) DEFAULT NULL,
  `creationDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `deactivationDate` TIMESTAMP NULL DEFAULT NULL,
  PRIMARY KEY (localEntity, peerEntity, persistentId)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Doinštalujeme knižnice do Jetty:

http://tux.rainside.sk/apache//commons/dbcp/binaries/commons-dbcp2-2.5.0-bin.tar.gz http://tux.rainside.sk/apache//commons/pool/binaries/commons-pool2-2.6.0-bin.tar.gz https://search.maven.org/remotecontent?filepath=commons-logging/commons-logging-api/1.1/commons-logging-api-1.1.jar https://cdn.mysql.com//Downloads/Connector-J/mysql-connector-java-8.0.13.tar.gz

TODO: Momentalne to bezi so starsimi kniznicami:

https://search.maven.org/remotecontent?filepath=org/apache/commons/commons-dbcp2/2.1.1/commons-dbcp2-2.1.1.jar https://search.maven.org/remotecontent?filepath=org/apache/commons/commons-pool2/2.4.2/commons-pool2-2.4.2.jar

Keďže MySQL je v aktuálnej verzii 8, driver musí byť tieť “najnovší” mysql-connector-java-8.0.13.tar.gz.

Archívy treba porozbaľovať a potom skopírovať súbory:

cp commons-dbcp2-2.5.0/commons-dbcp2-2.5.0.jar /opt/jetty/lib/ext/
cp commons-pool2-2.6.0/commons-pool2-2.6.0.jar /opt/jetty/lib/ext/
cp commons-logging-api-1.1.jar /opt/jetty/lib/ext/
cp mysql-connector-java-8.0.13/mysql-connector-java-8.0.13.jar /opt/jetty/lib/ext/
chown idp:idp /opt/jetty/lib/ext/*

Konfigurácia IdP

vim /opt/shibboleth-idp/conf/attribute-resolver.xml

Doplniť nový atribút:

<AttributeDefinition id="eduPersonTargetedID" xsi:type="SAML2NameID" nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
    <InputDataConnector ref="myStoredId" attributeNames="storedId"/>
    <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" encodeType="false"/>
    <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" friendlyName="eduPersonTargetedID" encodeType="false"/>
</AttributeDefinition>

A nový data konektor:

<DataConnector id="myStoredId"
    xsi:type="StoredId"
    sourceAttributeID="uid"
    generatedAttributeID="storedId"
    salt="<Zadat vygenerovany SALT>"
    queryTimeout="0">
    <InputAttributeDefinition ref="uid"/>
    <BeanManagedConnection>shibboleth.MySQLDataSource</BeanManagedConnection>
</DataConnector>

Konfiguračný súbor uložíme.

Vygenerujeme salt pomocou príkazu:

openssl rand -base64 36 2>/dev/null

Vystup vyzerá napríklad takto:

LdudV6JOJDzQOaF/4a75mApxsIFf7LVbjdgG3ME3hh7saPJw

Dodefinujeme potrebné beany v global.xml

vim /opt/shibboleth-idp/conf/global.xml
<bean id="shibboleth.MySQLDataSource"
    class="org.apache.commons.dbcp2.BasicDataSource"
    p:driverClassName="com.mysql.jdbc.Driver"
    p:url="jdbc:mysql://localhost:3306/shibboleth"
    p:username="shibboleth"
    p:password="<heslo pre MySQL pouzivatela shibboleth>" />
 
<bean id="shibboleth.JPAStorageService"
    class="org.opensaml.storage.impl.JPAStorageService"
    p:cleanupInterval="%{idp.storage.cleanupInterval:PT10M}"
    c:factory-ref="shibboleth.JPAStorageService.entityManagerFactory" />
 
<bean id="shibboleth.JPAStorageService.entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="packagesToScan" value="org.opensaml.storage.impl"/>
    <property name="dataSource" ref="shibboleth.MySQLDataSource"/>
    <property name="jpaVendorAdapter" ref="shibboleth.JPAStorageService.JPAVendorAdapter"/>
    <property name="jpaDialect">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
    </property>
</bean>
 
<bean id="shibboleth.JPAStorageService.JPAVendorAdapter"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
    p:generateDdl="true"
    p:database="MYSQL"
    p:databasePlatform="org.hibernate.dialect.MySQL5Dialect" />

Nový atribút zaradíme aj do konfigurácie filtrov attribute-filter.xml. Keďže sa jedná o anonymný atribút, môžeme ho sprístupnovať všetkým.

vim /opt/shibboleth-idp/conf/attribute-filter.xml
<AttributeRule attributeID="eduPersonTargetedID">
  <PermitValueRule xsi:type="ANY" />
</AttributeRule>

Upravíme súbor saml-nameid.properties:

vi /opt/shibboleth-idp/conf/saml-nameid.properties

Doplníme potrebné atribúty (odkazy na beany a salt):

idp.persistentId.generator = shibboleth.StoredPersistentIdGenerator
idp.persistentId.dataSource = shibboleth.MySQLDataSource
idp.persistentId.sourceAttribute = uid
idp.persistentId.salt = LdudV6JOJDzQOaF/4a75mApxsIFf7LVbjdgG3ME3hh7saPJw

Ďalej upravíme konfiguráciu v saml-nameid.xml

vim /opt/shibboleth-idp/conf/saml-nameid.xml

V sôbore odkomentujeme tento riadok:

<ref bean="shibboleth.SAML2PersistentGenerator" />

Ďalej upravíme súbor:

vim /opt/shibboleth-idp/conf/idp.properties

V súbore doplníme riadok:

idp.consent.StorageService = shibboleth.JPAStorageService

Ešte ostáva upraviť súbor subject-c14n.xml:

vim /opt/shibboleth-idp/conf/c14n/subject-c14n.xml

V súbore odkomentujeme riadok:

<ref bean="c14n/SAML2Persistent" />

V metadatach budeme oznamovaž, že IdP podporuje persistentný identifikátor:

vim /opt/shibboleth-idp/metadata/idp-metadata.xml

Do elementu IDPSSODescriptor pridáme element (napríklad za element <ArtifactResolutionService …>):

<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>

Vygenerujeme nový WAR súbor a reľtartujeme Jetty:

/opt/shibboleth-idp/bin/build.sh

systemctl restart jetty

Úprava prihlasovacej stránky

TODO