O365 SAML

Innen: KIFÜ Wiki

Az Office365 szolgáltatások SAML azonosítással történő használatához egy új SP-t kell konfigurálni az IdP konfigurációjában, mivel ez az SP nem szerepel a föderációs metaadatok között. A Microsoft által üzemeltetett SP-nek speciális attribútum igényei vannak:

  • perzisztens NameID-t kell küldeni (ImmutableID)
  • kell küldeni egy IDPEmail nevű attribútumot

Kiadott attribútumok

ImmutableID

Az ImmutableID az eduPersonPrincipalName attribútum MD5 hashének UUID formátumra konvertált változata.

IDPEmail

A kiadott e-mail értéknek meg kell egyeznie az Office365 által szolgáltatott e-mail címmel. Ez azt is jelenti, hogy az IdP-től kapott e-mail attribútum domain részét előzetesen validálni kell az Office365-ben.

SimpleSAMLphp

A metadata/saml20-sp-remote.php file-ba kell elhelyezni a következő bejegyzést:

/* 
 * Office 365
 * https://nexus.microsoftonline-p.com/federationmetadata/saml20/federationmetadata.xml
 */
$metadata['urn:federation:MicrosoftOnline'] = array(
  'entityid' => 'urn:federation:MicrosoftOnline',

  // Expose both required attributes 
  'attributes' => array('IDPEmail', 'ImmutableID'),
  'attributes.NameFormat' => "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",

  // Configure attribute mapping and ImmutableID generation
  'authproc' => array(
    31 => array(
      'class' => 'core:PHP',
      'code' => '
        $eppn = $attributes["eduPersonPrincipalName"][0];
        $chunks = str_split(md5($eppn), 4);        
        $attributes["ImmutableID"][0] = vsprintf("%s%s-%s-%s-%s-%s%s%s", $chunks);
      ',
    ),
        36 => array(
      'class' => 'core:AttributeMap',
      'mail' => 'IDPEmail',
    ),
  ),

  // Send ImmutableID as a "persistent" NameID
  'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
  'simplesaml.nameidattribute' => 'ImmutableID',

  'contacts' => array(),
  'metadata-set' => 'saml20-sp-remote',

  'AssertionConsumerService' => array(
    0 => array(
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
      'Location' => 'https://login.microsoftonline.com/login.srf',
      'index' => 0,
      'isDefault' => true,
    ),
    1 => array(
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign',
      'Location' => 'https://login.microsoftonline.com/login.srf',
      'index' => 1,
    ),
    2 => array(
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:PAOS',
      'Location' => 'https://login.microsoftonline.com/login.srf',
      'index' => 2,
    ),
  ),
  'SingleLogoutService' => array(
    0 =>
    array(
    'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
    'Location' => 'https://login.microsoftonline.com/login.srf',
    ),
  ),

  'keys' => array(
    0 => array(
      'encryption' => false,
      'signing' => true,
      'type' => 'X509Certificate',
      'X509Certificate' => 'MIIDYDCCAkigAwIBAgIJALLJPAyvf2sjMA0GCSqGSIb3DQEBBQUAMCkxJzAlBgNV
        BAMTHkxpdmUgSUQgU1RTIFNpZ25pbmcgUHVibGljIEtleTAeFw0xNDA3MTgxOTUz
        NDBaFw0xOTA3MTcxOTUzNDBaMCkxJzAlBgNVBAMTHkxpdmUgSUQgU1RTIFNpZ25p
        bmcgUHVibGljIEtleTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANYD
        KgByFZdqtTnnpF4IfIp4i2XLg2rLIo+mu4DmW9gRLlBJCNc7YESUxpKzuFYaANd8
        fWsDigJZTXbhOQApSpw4xXFnor2vJ1zm94LtqjcVEXTjUml5gAIS4pwuOU3ZfO/0
        eTG0gDYp4a0L/mzzTRsnwe/8WMPIE75Bq2zAyAZ9aePvl3QX7cXYLPfeK4QTgK3B
        5lwe1wWu3y5oQidjcSok8Frf80xzuCYuOa+ZUK3JibpLLCrT4uwiqf+KREDSdc4b
        PPlq0PWI4sQr1tha8yypRSvOH+/MxcfSRSnl6Uc+gm8nVEEWWIu4hhu6NIfG91mM
        UqJuzkgLCi6Gov6JS8UCAwEAAaOBijCBhzAdBgNVHQ4EFgQUnQoq7sI3R8rde4sQ
        s6nGEbJm3LcwWQYDVR0jBFIwUIAUnQoq7sI3R8rde4sQs6nGEbJm3LehLaQrMCkx
        JzAlBgNVBAMTHkxpdmUgSUQgU1RTIFNpZ25pbmcgUHVibGljIEtleYIJALLJPAyv
        f2sjMAsGA1UdDwQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEAf4jaNhKzRG3k+52W
        oM9nnISP7rlWIeWwH6EQGUlF6ozSP/03gYMAdqpdhww5zNwKzi7TQVbDC0pgq/tq
        zHv6JEI0R4B6h7/TJ1pYPxdvIFQrE27RHESltH/m+5UkVnayLqRD3/fi4zf4aEpx
        SDZ73MCR5LanPGqvlAMz29AL3g1ynj+eu7xMfFsM/8+qJaCXuxT5/30eeLEe+PYi
        kA/PhEwp+qkDQWPvdAwEghuUaFvtKAgDZierjpGzHZnYkXTTDTHVe1iP7tsAJH5q
        K3qdcv3UGPyZrjC/lietJcAcnwVoZQ93v2ieGfcKKN+PFN9M59/BkPo62HPoGNNx
        2ZDQaQ==',
    ),
    1 => array(
      'encryption' => false,
      'signing' => true,
      'type' => 'X509Certificate',
      'X509Certificate' => 'MIIDYDCCAkigAwIBAgIJAKLDsqkylLefMA0GCSqGSIb3DQEBBQUAMCkxJzAlBgNV
        BAMTHkxpdmUgSUQgU1RTIFNpZ25pbmcgUHVibGljIEtleTAeFw0xNDEwMTAxODE2
        MTNaFw0xOTEwMDkxODE2MTNaMCkxJzAlBgNVBAMTHkxpdmUgSUQgU1RTIFNpZ25p
        bmcgUHVibGljIEtleTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7A
        3m6uvOxEsX+NlB1hnflaR8DJj597wY3qyh/FX4O6rKvU2leAfINmBWcjEFApCKi9
        p5uIaZpNlDpPQ+R3BaZx+4NhHbOMpeWlpIiZHL61lwbulzurffUPhtzQNHAVzOBk
        ZsOgN9BD/hOleU//d+IXz08ateUb3Ip2vyaodilYQDDi5M9yOhanv1cO1Usjo2xT
        LfiK+TVygu+8bo+/8JHGPRy6pnghng970DRBDkVrKzozlrnmMesdSrtuCnsgyRbE
        XckxaQ8S2nDYyFqBI0PkcBW8+0akdFWW58Os5cGbPFeHi6vtZCR5pWw5pnqtuoip
        rdk9jg1axT3vwu+RVdcCAwEAAaOBijCBhzAdBgNVHQ4EFgQUBjNylGJBvkAY/4yI
        IoD00R6p5hIwWQYDVR0jBFIwUIAUBjNylGJBvkAY/4yIIoD00R6p5hKhLaQrMCkx
        JzAlBgNVBAMTHkxpdmUgSUQgU1RTIFNpZ25pbmcgUHVibGljIEtleYIJAKLDsqky
        lLefMAsGA1UdDwQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEAQGZUlJ3zzJvy1OLd
        tV3NTYHlbVHm3Fty17xqW9Ui8GE8sEWeUdHA6eURNNpNpd+gAGC6Tp+k+cU1LlPw
        Xm7BAATJ/2DjY8tzRc6r6EneQWRkIa8xpbvknXvUml6iFgo2ofOWLaFk6XpQ64MA
        O35wv9XEARNabJ9wJSRSevUigAx2U2GvaorV5PgqHImiKTSrL0K6j8B4OqXWUqP0
        KGf7pCdGlrq2XEl95N2zj8n/scvA9JasImztsVlZ+WxeF+OAMvWQQFc54gC6lwWc
        8kno8vPn3lwxVkTU0o9wcHnOhNi2hzVDV85sz7P9dOZYF73uy1uLshdjCcwlmQ2l
        A9OV9w==',
    ),
  ),
  'saml20.sign.assertion' => true,
   // This metadata does not contain an encryption key,
   // therefore explicitly disabling assertion encryption so it does not depend on global IDP settings.
  'assertion.encryption' => false,
);

ECP (Enhanced Client or Proxy)

Ha szeretnénk elérni az Office365 levelezést IMAP-on keresztül, akkor be kell kapcsolni még az ECP-t is. Ehhez a metadata/saml20-idp-hosted.php file-ba fel kell venni az IdP metaadatai közé egy 'saml20.ecp' elemet true értékkel, illetve az SP metaadatokhoz is hozzá kell adni két beállítást a metadata/saml20-sp-remote.php file-ban:

/* 
 * Office 365
 * https://nexus.microsoftonline-p.com/federationmetadata/saml20/federationmetadata.xml
 */
$metadata['urn:federation:MicrosoftOnline'] = array(
  // ...
  'signature.algorithm'  => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
  'saml20.sign.response' => false,
);

Shibboleth

Egy unicon/shibboleth-idp alapú docker image készítését mutatjuk be az alábbiakban.

Kezdeti IdP konfiguráció

# docker run -it -v $(pwd):/ext-mount --rm unicon/shibboleth-idp init-idp.sh

Please complete the following for your IdP environment:
Hostname: [64ed9b77b493.localdomain]
idp.example.com
SAML EntityID: [https://idp.example.com/idp/shibboleth]
https://idp.example.com:4443/idp/shibboleth
Attribute Scope: [localdomain]
example.com
Backchannel PKCS12 Password: XXXsecretXXX
Re-enter password: XXXsecretXXX
Cookie Encryption Key Password: XXXsecretXXX
Re-enter password: XXXsecretXXX
Warning: /opt/shibboleth-idp-tmp/bin does not exist.
Warning: /opt/shibboleth-idp-tmp/edit-webapp does not exist.
Warning: /opt/shibboleth-idp-tmp/dist does not exist.
Warning: /opt/shibboleth-idp-tmp/doc does not exist.
Warning: /opt/shibboleth-idp-tmp/system does not exist.
Generating Signing Key, CN = idp.example.com URI = https://idp.example.com:4443/idp/shibboleth ...
...done
Creating Encryption Key, CN = idp.example.com URI = https://idp.example.com:4443/idp/shibboleth ...
...done
Creating Backchannel keystore, CN = idp.example.com URI = https://idp.example.com:4443/idp/shibboleth ...
...done
Creating cookie encryption key files...
...done
Rebuilding /opt/shibboleth-idp-tmp/war/idp.war ...
...done

BUILD SUCCESSFUL
Total time: 7 minutes 4 seconds
A basic Shibboleth IdP config and UI has been copied to ./customized-shibboleth-idp/ (assuming the
default volume mapping was used).
Most files, if not being customized can be removed from what was exported/the local Docker image and
baseline files will be used.

Keletkezik egy customized-shibboleth-idp mappa a következő szerkezettel:

  • conf
  • credentials
  • metadata
  • views
  • views/admin
  • views/client-storage
  • views/intercept
  • webapp
  • webapp/css
  • webapp/images

Az alábbi fájlokat kell majd módosítani:

  • conf/attribute-filter.xml
  • conf/attribute-resolver.xml
  • conf/idp.properties
  • conf/ldap.properties
  • conf/metadata-providers.xml
  • conf/relying-party.xml
  • conf/saml-nameid.properties
  • conf/saml-nameid.xml
  • metadata/idp-metadata.xml

Néhány másik fájlt pedig létre kell hozni:

  • credentials/idp-browser.p12 (webserver SSL key/cert)
  • credentials/ldap-server.crt (LDAP server certificate)
  • metadata/federationmetadata.xml (Office 365 federation metadata)

Mivel ebben a példában az IdP-t a 4443-as porton fogjuk elérni, ezért ellenőrizni kell minden Location paramétert a metadata/idp-metadata.xml fájlban, hogy jó helyre mutat-e. Továbbá engedélyezni kell a SingleLogoutService bejegyzéseket (amelyek alapból ki vannak iktatva).

A webszerver tanúsítvány előkészítése

A felhasználók által látogatott oldalakhoz célszerű valamilyen hitelesítés szolgáltató által aláírt tanúsítványt használni. Másoljuk be a webszerver tanúsítványt, a tanúsítvány láncot és a szerver kulcsot PEM formátumban egy mappába, majd egyesítsük őket egyetlen p12-es fáljba az openssl segítségével:

# cat webserver.crt intermediate_ca.crt > cert-chain.txt
# openssl pkcs12 -export -inkey webserver.key -in cert-chain.txt -out idp-browser.p12
Enter Export Password: XXX_secret_XXX
Verifying - Enter Export Password: XXX_secret_XXX

Az előállított idp-browser.p12 fájlt másoljuk be a credentials mappába.

Microsoft SAML SP metadata és föderációs beállítások

Töltsük le a Microsoft SAML2 SP metaadatait a metadata könyvtárba:

# wget https://nexus.microsoftonline-p.com/federationmetadata/saml20/federationmetadata.xml

Készítsünk egy MetadataProvider-t a conf/metadata-providers.xml fájlban:

<MetadataProvider id="MicrosoftOnline"
                  xsi:type="FilesystemMetadataProvider"
                  metadataFile="%{idp.home}/metadata/federationmetadata.xml"/>

A conf/relying-party.xml fájlban néhány speciális beállítást kell eszközölnünk, hogy a föderáció működjön (a shibboleth.RelyingPartyOverrides szekció alatt):

<util:list id="shibboleth.RelyingPartyOverrides">
  <bean parent="RelyingPartyByName" c:relyingPartyIds="urn:federation:MicrosoftOnline">
    <property name="profileConfigurations">
      <list>
        <bean parent="SAML2.SSO"
              p:encryptAssertions="false" />
        <bean parent="SAML2.ECP"
              p:encryptAssertions="false"
              p:signAssertions="true"
              p:signResponses="false"
              p:nameIDFormatPrecedence="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" />
      </list>
    </property>
  </bean>
</util:list>

Az Office 365 megköveteli a SAML2 NameID használatát, amiben ugyanazt az értéket várja, mint ami az ImmutableID attribútumban van. Ezt a conf/saml-nameid.xml fájlban kell beállítani:

<!-- SAML 2 NameID Generation -->
<util:list id="shibboleth.SAML2NameIDGenerators">

  <ref bean="shibboleth.SAML2TransientGenerator" />
  <ref bean="shibboleth.SAML2PersistentGenerator" />

<!-- Persistent ID Generator for all entities except Microsoft -->
<bean parent="shibboleth.SAML2PersistentGenerator">
  <property name="activationCondition">
    <bean parent="shibboleth.Conditions.NOT">
      <constructor-arg>
        <bean parent="shibboleth.Conditions.RelyingPartyId"
              c:candidates="#{{'urn:federation:MicrosoftOnline'}}" />
      </constructor-arg>
    </bean>
  </property>
</bean>

<!-- Persistent ID Generator for Microsoft -->
<bean parent="shibboleth.SAML2AttributeSourcedGenerator"
      p:format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
      p:attributeSourceIds="#{ {'ImmutableID'} }">
  <property name="activationCondition">
    <bean parent="shibboleth.Conditions.RelyingPartyId"
          c:candidates="#{{'urn:federation:MicrosoftOnline'}}" />
  </property>
</bean>

</util:list>

LDAP adatforrás beállítása

Mentsük el az LDAP szerver tanúsítványát PEM formátumban a credentials/ldap-server.crt fájlba.

Az Office 365 két attriútum kiadását kéri: IDPEmail és ImmutableID.

Az IDPEmail értékének meg kell egyeznie a felhasználó Office 365-ös UserPrincipalName attribútumával (ami egyben a hivatalos (elsődleges) felhős email címe).

Az ImmutableID esetünkben az eduPerosnPrincilapName MD5 hash értékének UUID formátumra konvertált változata lesz, amelyet egy javasccript segítségével képezünk.

Cseréljük le a conf/attribute-resolver.xml fájlt a conf/attribute-resolver-full.xml nevűvel.

# mv conf/attribute-resolver-full.xml conf/attribute-resolver.xml

Az attribute-resolver.xml fájlban beállítunk néhány attribútumot és az LDAP adatforrást. Az ImmutableID egy Shibboleth ScriptedAttribute lesz, ami egy javasccript segítségével fogja előállítani az attribútum értékét az eduPersonPrincipalName LDAP attribútumot felhasználva. Az MD5 hash kiszámítása külső segítség nélkül történik, ezért a script kissé hosszú.

<?xml version="1.0" encoding="UTF-8"?>

<AttributeResolver
        xmlns="urn:mace:shibboleth:2.0:resolver"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">

    <!-- ========================================== -->
    <!--      Attribute Definitions                 -->
    <!-- ========================================== -->

    <AttributeDefinition xsi:type="Simple" id="uid">
        <InputDataConnector ref="myLDAP" attributeNames="uid"/>
        <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:uid" encodeType="false" />
        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" encodeType="false" />
    </AttributeDefinition>

    <AttributeDefinition xsi:type="Simple" id="mail">
        <InputDataConnector ref="myLDAP" attributeNames="mail"/>
        <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:mail" encodeType="false" />
        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.3" friendlyName="mail" encodeType="false" />
    </AttributeDefinition>

    <AttributeDefinition xsi:type="Prescoped" id="eduPersonPrincipalName">
        <InputDataConnector ref="myLDAP" attributeNames="eduPersonPrincipalName"/>
        <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
        <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />
    </AttributeDefinition>

    <AttributeDefinition xsi:type="Simple" id="IDPEmail">
        <InputDataConnector ref="myLDAP" attributeNames="mail"/>
        <AttributeEncoder xsi:type="SAML1String" name="IDPEmail" encodeType="false" />
        <AttributeEncoder xsi:type="SAML2String" name="IDPEmail" friendlyName="IDPEmail" encodeType="false" />
    </AttributeDefinition>

    <AttributeDefinition xsi:type="ScriptedAttribute" id="ImmutableID">
        <InputDataConnector ref="myLDAP" attributeNames="eduPersonPrincipalName" />
        <AttributeEncoder xsi:type="SAML1String"
                          name="ImmutableID"
                          encodeType="false" />
        <AttributeEncoder xsi:type="SAML2String"
                          name="ImmutableID"
                          friendlyName="ImmutableID"
                          encodeType="false" />
        <Script><![CDATA[
var MD5 = function(d) {
  var r = M(V(Y(X(d),8*d.length)));
  return r.toLowerCase()
};

function M(d) {
  for(var _,m="0123456789ABCDEF",f="",r=0;r<d.length;r++)_=d.charCodeAt(r),f+=m.charAt(_>>>4&15)+m.charAt(15&_);
  return f
}

function X(d) {
  for(var _=Array(d.length>>2),m=0;m<_.length;m++)_[m]=0;
  for(m=0;m<8*d.length;m+=8)_[m>>5]|=(255&d.charCodeAt(m/8))<<m%32;
  return _
}

function V(d) {
  for(var _="",m=0;m<32*d.length;m+=8)_+=String.fromCharCode(d[m>>5]>>>m%32&255);
  return _
}

function Y(d,_) {
  d[_>>5]|=128<<_%32,d[14+(_+64>>>9<<4)]=_;
  for(var m=1732584193,f=-271733879,r=-1732584194,i=271733878,n=0;n<d.length;n+=16) {
    var h=m,t=f,g=r,e=i;
    f=md5_ii(
      f=md5_ii(
        f=md5_ii(
          f=md5_ii(
            f=md5_hh(
              f=md5_hh(
                f=md5_hh(
                  f=md5_hh(
                    f=md5_gg(
                      f=md5_gg(
                        f=md5_gg(
                          f=md5_gg(
                            f=md5_ff(
                              f=md5_ff(
                                f=md5_ff(
                                  f=md5_ff(
                                    f,r=md5_ff(
                                      r,i=md5_ff(
                                        i,m=md5_ff(
                                          m,f,r,i,d[n+0],7,-680876936
                                        ),f,r,d[n+1],12,-389564586
                                      ),m,f,d[n+2],17,606105819
                                    ),i,m,d[n+3],22,-1044525330
                                  ),r=md5_ff(
                                    r,i=md5_ff(
                                      i,m=md5_ff(
                                        m,f,r,i,d[n+4],7,-176418897
                                      ),f,r,d[n+5],12,1200080426
                                    ),m,f,d[n+6],17,-1473231341
                                  ),i,m,d[n+7],22,-45705983
                                ),r=md5_ff(
                                  r,i=md5_ff(
                                    i,m=md5_ff(
                                      m,f,r,i,d[n+8],7,1770035416
                                    ),f,r,d[n+9],12,-1958414417
                                  ),m,f,d[n+10],17,-42063
                                ),i,m,d[n+11],22,-1990404162
                              ),r=md5_ff(
                                r,i=md5_ff(
                                  i,m=md5_ff(
                                    m,f,r,i,d[n+12],7,1804603682
                                  ),f,r,d[n+13],12,-40341101
                                ),m,f,d[n+14],17,-1502002290
                              ),i,m,d[n+15],22,1236535329
                            ),r=md5_gg(
                              r,i=md5_gg(
                                i,m=md5_gg(
                                  m,f,r,i,d[n+1],5,-165796510
                                ),f,r,d[n+6],9,-1069501632
                              ),m,f,d[n+11],14,643717713
                            ),i,m,d[n+0],20,-373897302
                          ),r=md5_gg(
                            r,i=md5_gg(
                              i,m=md5_gg(
                                m,f,r,i,d[n+5],5,-701558691
                              ),f,r,d[n+10],9,38016083
                            ),m,f,d[n+15],14,-660478335
                          ),i,m,d[n+4],20,-405537848
                        ),r=md5_gg(
                          r,i=md5_gg(
                            i,m=md5_gg(
                              m,f,r,i,d[n+9],5,568446438
                            ),f,r,d[n+14],9,-1019803690
                          ),m,f,d[n+3],14,-187363961
                        ),i,m,d[n+8],20,1163531501
                      ),r=md5_gg(
                        r,i=md5_gg(
                          i,m=md5_gg(
                            m,f,r,i,d[n+13],5,-1444681467
                          ),f,r,d[n+2],9,-51403784
                        ),m,f,d[n+7],14,1735328473
                      ),i,m,d[n+12],20,-1926607734
                    ),r=md5_hh(
                      r,i=md5_hh(
                        i,m=md5_hh(
                          m,f,r,i,d[n+5],4,-378558
                        ),f,r,d[n+8],11,-2022574463
                      ),m,f,d[n+11],16,1839030562
                    ),i,m,d[n+14],23,-35309556
                  ),r=md5_hh(
                    r,i=md5_hh(
                      i,m=md5_hh(
                        m,f,r,i,d[n+1],4,-1530992060
                      ),f,r,d[n+4],11,1272893353
                    ),m,f,d[n+7],16,-155497632
                  ),i,m,d[n+10],23,-1094730640
                ),r=md5_hh(
                  r,i=md5_hh(
                    i,m=md5_hh(
                      m,f,r,i,d[n+13],4,681279174
                    ),f,r,d[n+0],11,-358537222
                  ),m,f,d[n+3],16,-722521979
                ),i,m,d[n+6],23,76029189
              ),r=md5_hh(
                r,i=md5_hh(
                  i,m=md5_hh(
                    m,f,r,i,d[n+9],4,-640364487
                  ),f,r,d[n+12],11,-421815835
                ),m,f,d[n+15],16,530742520
              ),i,m,d[n+2],23,-995338651
            ),r=md5_ii(
              r,i=md5_ii(
                i,m=md5_ii(
                  m,f,r,i,d[n+0],6,-198630844
                ),f,r,d[n+7],10,1126891415
              ),m,f,d[n+14],15,-1416354905
            ),i,m,d[n+5],21,-57434055
          ),r=md5_ii(
            r,i=md5_ii(
              i,m=md5_ii(
                m,f,r,i,d[n+12],6,1700485571
              ),f,r,d[n+3],10,-1894986606
            ),m,f,d[n+10],15,-1051523
          ),i,m,d[n+1],21,-2054922799
        ),r=md5_ii(
          r,i=md5_ii(
            i,m=md5_ii(
              m,f,r,i,d[n+8],6,1873313359
            ),f,r,d[n+15],10,-30611744
          ),m,f,d[n+6],15,-1560198380
        ),i,m,d[n+13],21,1309151649
      ),r=md5_ii(
        r,i=md5_ii(
          i,m=md5_ii(
            m,f,r,i,d[n+4],6,-145523070
          ),f,r,d[n+11],10,-1120210379
        ),m,f,d[n+2],15,718787259
      ),i,m,d[n+9],21,-343485551
    ),m=safe_add(m,h),f=safe_add(f,t),r=safe_add(r,g),i=safe_add(i,e)
  }
  return Array(m,f,r,i)
}

function md5_cmn(d,_,m,f,r,i) {
  return safe_add(bit_rol(safe_add(safe_add(_,d),safe_add(f,i)),r),m)
}

function md5_ff(d,_,m,f,r,i,n) {
  return md5_cmn(_&m|~_&f,d,_,r,i,n)
}

function md5_gg(d,_,m,f,r,i,n) {
  return md5_cmn(_&f|m&~f,d,_,r,i,n)
}

function md5_hh(d,_,m,f,r,i,n) {
  return md5_cmn(_^m^f,d,_,r,i,n)
}

function md5_ii(d,_,m,f,r,i,n) {
  return md5_cmn(m^(_|~f),d,_,r,i,n)
}

function safe_add(d,_) {
  var m=(65535&d)+(65535&_);
  return(d>>16)+(_>>16)+(m>>16)<<16|65535&m
}

function bit_rol(d,_) {
  return d<<_|d>>>32-_
}

var UUID = function(s) {
  return s.substring(0,8) + '-' + s.substring(8,12) + '-' + s.substring(12,16) + '-' + s.substring(16,20) + '-' + s.substring(20)
};

ImmutableID.addValue(UUID(MD5(eduPersonPrincipalName.getValues().get(0))));
        ]]></Script>
    </AttributeDefinition>

    <!-- ========================================== -->
    <!--      Data Connectors                       -->
    <!-- ========================================== -->

    <DataConnector id="myLDAP" xsi:type="LDAPDirectory"
        ldapURL="%{idp.attribute.resolver.LDAP.ldapURL}"
        baseDN="%{idp.attribute.resolver.LDAP.baseDN}" 
        principal="%{idp.attribute.resolver.LDAP.bindDN}"
        principalCredential="%{idp.attribute.resolver.LDAP.bindDNCredential}"
        useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS:true}"
        connectTimeout="%{idp.attribute.resolver.LDAP.connectTimeout}"
                trustFile="%{idp.attribute.resolver.LDAP.trustCertificates}"
        responseTimeout="%{idp.attribute.resolver.LDAP.responseTimeout}">
        <FilterTemplate>
            <![CDATA[
                %{idp.attribute.resolver.LDAP.searchFilter}
            ]]>
        </FilterTemplate>
            <ConnectionPool
            minPoolSize="%{idp.pool.LDAP.minSize:3}"
            maxPoolSize="%{idp.pool.LDAP.maxSize:10}"
            blockWaitTime="%{idp.pool.LDAP.blockWaitTime:PT3S}"
            validatePeriodically="%{idp.pool.LDAP.validatePeriodically:true}"
            validateTimerPeriod="%{idp.pool.LDAP.validatePeriod:PT5M}"
            expirationTime="%{idp.pool.LDAP.idleTime:PT10M}"
            failFastInitialize="%{idp.pool.LDAP.failFastInitialize:false}" />
    </DataConnector>

    <DataConnector id="computed" xsi:type="ComputedId"
                   generatedAttributeID="computedId"
                   salt="%{idp.persistentId.salt}"
                   algorithm="%{idp.persistentId.algorithm:SHA}"
                   encoding="%{idp.persistentId.encoding:BASE32}">
        <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}" />
    </DataConnector>

</AttributeResolver>

Ha anonymous LDAP keresést akarunk használni, akkor a principal és principalCredential paramétereket törölni kell a myLDAP DataConnector-ból.

Attribútum kiadás beállítása

Az IdP-nek ki kell adnia az ImmutableID és az IDPEmail attribútumokat a Microsoft-nak. Ezért létre kell hozni egy új AttributeFilterPolicy szabályt a conf/attribute-filter.xml fájlban:

<!-- Office 365 ImmutableID and IDPEmail -->

<AttributeFilterPolicy id="PolicyForWindowsAzureAD">

  <PolicyRequirementRule xsi:type="Requester" value="urn:federation:MicrosoftOnline" />

    <AttributeRule attributeID="IDPEmail">
      <PermitValueRule xsi:type="ANY" />
    </AttributeRule>

    <AttributeRule attributeID="ImmutableID">
      <PermitValueRule xsi:type="ANY" />
    </AttributeRule>

</AttributeFilterPolicy>

A properties fájlok beállításai

conf/idp.properties

idp.session.StorageService = shibboleth.StorageService
idp.session.trackSPSessions = true
idp.session.secondaryServiceIndex = true

conf/ldap.properties

idp.authn.LDAP.ldapURL = ldap://ldap.example.com
idp.authn.LDAP.useStartTLS = true
idp.authn.LDAP.sslConfig = certificateTrust
# idp.authn.LDAP.trustStore = %{idp.home}/credentials/ldap-server.truststore
idp.authn.LDAP.returnAttributes = uid,mail,eduPersonPrincipalName
idp.authn.LDAP.baseDN = ou=People,dc=example,dc=com
idp.authn.LDAP.userFilter = (|(uid={user})(mail={user}))
# idp.authn.LDAP.bindDN = uid=myservice,ou=system
# idp.authn.LDAP.bindDNCredential = myServicePassword
idp.authn.LDAP.dnFormat = uid=%s,ou=People,dc=example,dc=com

conf/saml-nameid.properties

idp.nameid.saml2.default = urn:oasis:names:tc:SAML:2.0:nameid-format:transient
idp.nameid.saml1.default = urn:mace:shibboleth:1.0:nameIdentifier
idp.transientId.generator = shibboleth.CryptoTransientIdGenerator
idp.persistentId.sourceAttribute = eduPersonPrincipalName
idp.persistentId.useUnfilteredAttributes = true
idp.persistentId.salt = XXX_secret_salt_XXX
idp.persistentId.generator = shibboleth.ComputedPersistentIdGenerator

Docker image készítése

Hozzunk létre egy Dockerfile-t ott, ahol a customized-shibboleth-idp mappa található, az alábbi tartalommal:

FROM       unicon/shibboleth-idp
MAINTAINER John Doe <john.doe@example.com>
ADD        customized-shibboleth-idp/ /opt/shibboleth-idp/

Készítsük el az image fájlt:

# docker build -t example-com/idp-shib .

Indítsuk el az új IdP-t. A 4443-as és 8443-as portokat adjuk ki, és ne feledjük az előzőleg az SSL kulcsok számára beállított jelszavakat sem:

# docker run -it --rm \
  -p 4443:4443 \
  -p 8443:8443 \
  --network bridge \
  -e JETTY_BROWSER_SSL_KEYSTORE_PASSWORD=XXX_secret_XXX \
  -e JETTY_BACKCHANNEL_SSL_KEYSTORE_PASSWORD=XXX_secret_XXX \
  example-com/idp-shib

Hibakeresés

A conf/idp.properties fájlban megadható loglevel beállítások részletes hibakeresést tesznek lehetővé.

Variable Default value Description
idp.loglevel.idp INFO Log level for the IdP proper
idp.loglevel.ldap WARN Log level for LDAP events
idp.loglevel.messages INFO Set to DEBUG for protocol message tracing
idp.loglevel.encryption INFO Set to DEBUG to log cleartext versions of encrypted content
idp.loglevel.opensaml INFO Log level for OpenSAML library classes
idp.loglevel.props INFO Set to DEBUG to log runtime properties during startup
idp.loglevel.spring ERROR Log level for Spring Framework (very chatty)
idp.loglevel.container ERROR Log level for Tomcat/Jetty (very chatty)
idp.loglevel.xmlsec INFO Set to DEBUG for low-level XML Signing/Encryption logging

További leírások