Obsah

Shibboleth IdP

This section is split in two parts – installation and configuration. Installation itself is trivial, takes minimum time and differences are very simple, for example, entering entity ID, etc. Configuration is, on the other hand, quite complex depending on particular needs such as whether using LDAP or Active Directory, etc.

IdP installation

Shibboleth IdP is installed by downloading source code, extracting it and running a shell script install.sh from within the extracted directory. Checking SHA256 fingerprints is highly recommended.

# Downloading Shibboleth IdP
mkdir -p /opt/src
chown -R idp:idp /opt/src
su idp
cd /opt/src
wget https://shibboleth.net/downloads/identity-provider/3.2.1/shibboleth-identity-provider-3.2.1.tar.gz \
     https://shibboleth.net/downloads/identity-provider/3.2.1/shibboleth-identity-provider-3.2.1.tar.gz.asc \
     https://shibboleth.net/downloads/identity-provider/3.2.1/shibboleth-identity-provider-3.2.1.tar.gz.sha256
 
# Checking SHA256 fingerprints
sha256sum shibboleth-identity-provider-3.2.1.tar.gz && cat shibboleth-identity-provider-3.2.1.tar.gz.sha256
 
# Checking GPG signature
# Importing key 07CEEB8B
gpg --keyserver hkp://keys.gnupg.net --search-keys 07CEEB8B
# Checking the signature
gpg --verify shibboleth-identity-provider-3.2.1.tar.gz.asc
 
#
# Run EXIT command to become 'root' user back again
#
exit

Now, with source code downloaded and checked, installation might proceed.

# Installing Shibboleth (as user root)
cd /opt
tar -xzf src/shibboleth-identity-provider-3.2.1.tar.gz
cd shibboleth-identity-provider-3.2.1/
./bin/install.sh

After running the installation script:

  1. accept source installation directory
  2. fill in server hostname (might be set correctly by default)
  3. accept entityID (or choosing a different one, for example, the one already existing [never change entityID after choosing one])
  4. enter organization scope
  5. enter two passwords asked for (pwgen might help)

The output of the installation script install.sh might look like this:

$ ./bin/install.sh 
Source (Distribution) Directory: [/opt/shibboleth-identity-provider-3.2.1]
 
Installation Directory: [/opt/shibboleth-idp]
 
Hostname: [localhost.localdomain]
whoami-dev.cesnet.cz
SAML EntityID: [https://whoami-dev.cesnet.cz/idp/shibboleth]
 
Attribute Scope: [localdomain]
cesnet.cz
TLS Private Key Password: 
Re-enter password: 
Cookie Encryption Key Password: 
Re-enter password: 
Warning: /opt/shibboleth-idp/bin 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.
Warning: /opt/shibboleth-idp/webapp does not exist.
Generating Signing Key, CN = whoami-dev.cesnet.cz URI = https://whoami-dev.cesnet.cz/idp/shibboleth ...
...done
Creating Encryption Key, CN = whoami-dev.cesnet.cz URI = https://whoami-dev.cesnet.cz/idp/shibboleth ...
...done
Creating TLS keystore, CN = whoami-dev.cesnet.cz URI = https://whoami-dev.cesnet.cz/idp/shibboleth ...
...done
Creating cookie encryption key files...
...done
Rebuilding /opt/shibboleth-idp/war/idp.war ...
...done
 
BUILD SUCCESSFUL
Total time: 1 minute 0 seconds

It is required to change the ownership of /opt/shibboleth-idp/ and /opt/shibboleth-identity-provider-3.*/ directories to the user idp belonging to the group idp.

# Changing the ownership (as root user)
chown -R idp:idp /opt/shibboleth-id*

Restart Jetty to load Shibboleth IdP into the web server:

# Restarting Jetty
/etc/init.d/jetty restart

It is time to check Shibboleth IdP. The following test requires Jetty to listen on HTTP, i.e. port 80. Otherwise -u parameter is mandatory since default value is http://localhost/idp.

# Shibboleth IdP status
cd /opt/shibboleth-idp/
./bin/status.sh

If everything is set correctly, the following output shall be seen:

### Operating Environment Information
operating_system: Linux
operating_system_version: 3.16.0-4-amd64
operating_system_architecture: amd64
jdk_version: 1.8.0_91
available_cores: 1
used_memory: 311 MB
maximum_memory: 958 MB

### Identity Provider Information
idp_version: 3.2.1
start_time: 2016-05-09T15:04:57+02:00
current_time: 2016-05-10T16:53:33+02:00
uptime: 92916009 ms

service: shibboleth.LoggingService
last successful reload attempt: 2016-05-09T13:03:48Z
last reload attempt: 2016-05-09T13:03:48Z

service: shibboleth.ReloadableAccessControlService
last successful reload attempt: 2016-05-09T13:03:55Z
last reload attempt: 2016-05-09T13:03:55Z

service: shibboleth.MetadataResolverService
last successful reload attempt: 2016-05-09T13:03:53Z
last reload attempt: 2016-05-09T13:03:53Z

        metadata source: ShibbolethMetadata
        last refresh attempt: 2016-05-10T14:48:57Z
        last update: 2016-05-10T14:48:57Z

service: shibboleth.RelyingPartyResolverService
last successful reload attempt: 2016-05-09T13:03:53Z
last reload attempt: 2016-05-09T13:03:53Z

service: shibboleth.NameIdentifierGenerationService
last successful reload attempt: 2016-05-09T13:03:52Z
last reload attempt: 2016-05-09T13:03:52Z

service: shibboleth.AttributeResolverService
last successful reload attempt: 2016-05-09T13:03:50Z
last reload attempt: 2016-05-09T13:03:50Z

        DataConnector myStoredId: has never failed

        DataConnector myLDAPgroups: has never failed

        DataConnector staticAttributes: has never failed

        DataConnector myLDAP: has never failed

service: shibboleth.AttributeFilterService
last successful reload attempt: 2016-05-09T13:03:50Z
last reload attempt: 2016-05-09T13:03:50Z

Accessing https://HOSTNAME/idp is also possible, however, no useful information is displayed compared to status.sh script.

IdP configuration

Shibboleth IdP configuration might be very complex depending on particular needs and requirements of an organization. Here, just basic configuration is presented, but anyone should be able to modify it to meet any requirements.

The guide is written in a step by step manner. Skipping parts of this guide might easily lead to misconfiguration.

conf/access-control.xml

In this configuration file, IP ranges (or better just IP addresses) allowed to access the diagnostic page (https://HOSTNAME/idp/status) and other administrative features such as reloading particular configuration (metadata feeds, etc.) are defined.

I recommend to allow just necessary IP addresses such as admin's workstation IP.

# Edit access-control.xml
vi /opt/shibboleth-idp/conf/access-control.xml

In this example, 1.2.3.4/32 IPv4 address and 2001:1:2:3::4/128 IPv6 address are chosen. The addresses must be specified with the address mask.

<!-- Allowing IPv4/IPv6 loopback, 1.2.3.4/32, 2001:1:2:3::4:5/128 -->
p:allowedRanges="#{ {'127.0.0.1/32', '::1/128', '1.2.3.4/32', '2001:1:2:3::4:5/128'} }"

Shibboleth IdP configuration (Access Control List) should be reloaded in order to allow access from defined IP addresses.

# Reloading ACLs
cd /opt/shibboleth-idp
./bin/reload-service.sh -id shibboleth.ReloadableAccessControlService

Accessing the diagnostic page (https://HOSTNAME/idp/status) from the above defined IP addresses will display status information simlarly to status.sh shell script run from console earlier.

conf/ldap.properties

All the information related to LDAP connection are set in conf/ldap.properties configuration file.

# Edit ldap.properties
vi /opt/shibboleth-idp/conf/ldap.properties

The following options are the most important ones:

idp.authn.LDAP.authenticator     = bindSearchAuthenticator
idp.authn.LDAP.ldapURL           = ldaps://ldap1.example.org:636 \
                                   ldaps://ldap2.example.org:636
idp.authn.LDAP.useStartTLS       = false
idp.authn.LDAP.useSSL            = true
idp.authn.LDAP.sslConfig         = certificateTrust
idp.authn.LDAP.trustCertificates = %{idp.home}/credentials/ldap-server.crt
idp.authn.LDAP.baseDN            = ou=people,dc=example,dc=org
idp.authn.LDAP.subtreeSearch     = true
idp.authn.LDAP.bindDN            = uid=shibboleth,ou=users,dc=example,dc=org
idp.authn.LDAP.bindDNCredential  = nejakeheslo

idp.attribute.resolver.LDAP.returnAttributes    = givenName,sn,cn,o,schacHomeOrg,eduPersonPrincipalName,uid,mail

The first variable idp.authn.LDAP.authenticator specifies authentication strategy. If set to bindSearchAuthenticator Shibboleth will bind to LDAP. Default value is anonSearchAuthenticator for anonymous searching.

Option idp.authn.LDAP.ldapURL specifies LDAP server(s) to connect to. In the example above, IdP is set to connect to server ldap1.example.org and ldap2.example.org. Connection is secured by SSL on the standard port 636.

ldapURL comment: There must not be a slash (/) at the end of the ldapURL such as ldaps://ldap1.example.org:636/! In that case, Shibboleth will not start and output an error java.lang.NumberFormatException: For input string: „636/“. to its log file.

Configuration options idp.authn.LDAP.useStartTLS and idp.authn.LDAP.useSSL define SSL usage instead of TLS. The following option idp.authn.LDAP.trustCertificates contains path to a certificate.

Finally, idp.authn.LDAP.baseDN sets so called „base DN“ in an LDAP directory, idp.authn.LDAP.bindDN and idp.authn.LDAP.bindDNCredential define username and password used for accessing LDAP directory when asking for users' attributes.

conf/metadata-providers.xml

Metadata sources are defined in conf/metadata-providers.xml configuration file. In the following example, a production federation eduID.cz is used. Metadata is downloaded and saved locally.

# Editing metadata-providers.xml
vi /opt/shibboleth-idp/conf/metadata-providers.xml
<!-- eduID.cz -->
<MetadataProvider
    id="eduidcz-metadata"
    xsi:type="FileBackedHTTPMetadataProvider"
    backingFile="%{idp.home}/metadata/eduidcz.xml"
    metadataURL="https://metadata.eduid.cz/entities/eduid+sp"
    maxRefreshDelay="PT15M">
    <MetadataFilter
        xsi:type="SignatureValidation"
        requireSignedRoot="true"
        certificateFile="%{idp.home}/credentials/metadata.eduid.cz.crt.pem" />
</MetadataProvider>

Code block stated above (element <MetadataProvider>) has to be put inside the <MetadataProvider> element in metadata-providers.xml configuration file otherwise it will not work. A valid XML file has to contain only ONE root element.

Description of individual <MetadataProvider> element's attributes follows:

Metadata is valid only for specific time and then it is invalid, which means untrustworthy and not usable. The default reaload interval is set to 4 hours. You can tweak it using maxRefreshDelay element of <MetadataProvider> as shown above.

If federation metadata is not digitally signed <MetadataFilter> is meaningless. However, production federation should definitelly sign metadata and entities within the federation should definitelly validate the signature.

To reload federation metadata manually, just run these commands:

# Reloading federation metadata
cd /opt/shibboleth-idp
./bin/reload-service.sh -id shibboleth.MetadataResolverService

In the metadata directory (/opt/shibboleth-idp/metadata/) a cached federation metadata should be available:

# Listing downloaded metadata
ls -1 metadata/
# Downloaded metadata
eduidcz.xml
idp-metadata.xml

conf/attribute-resolver.xml

Users' attributes will be obtained from an LDAP server. As a basic for such configuration it is wise to take an take advantage of the prepared configuration file attribute-resolver-ldap.xml which contains an LDAP connector. The original file attribute-resolver.xml can be safely overwritten as it will not be needed anymore.

# Preparing attribute-resolver.xml
cd /opt/shibboleth-idp/conf
cp attribute-resolver-ldap.xml attribute-resolver.xml
vi attribute-resolver.xml

Following attributes are user-specific and are to be obtained from an LDAP server. There is no need to specify „scope“ for eduPersonPrincipalName since it is loaded from idp.properties configuration file. The scope is defined during Shibboleth IdP installation

<!-- cn -->
<resolver:AttributeDefinition id="cn" xsi:type="ad:Simple" sourceAttributeID="cn">
    <resolver:Dependency ref="myLDAP" />
    <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:cn" />
    <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:2.5.4.3" friendlyName="cn" />
</resolver:AttributeDefinition>
 
<!-- eduPersonPrincipalName -->
<resolver:AttributeDefinition id="eduPersonPrincipalName" xsi:type="ad:Scoped" scope="%{idp.scope}" sourceAttributeID="uid">
    <resolver:Dependency ref="myLDAP" />
    <resolver:AttributeEncoder xsi:type="enc:SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
    <resolver:AttributeEncoder xsi:type="enc:SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />
</resolver:AttributeDefinition>
 
<!-- givenName -->
<resolver:AttributeDefinition id="givenName" xsi:type="ad:Simple" sourceAttributeID="givenName">
    <resolver:Dependency ref="myLDAP" />
    <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:givenName" encodeType="false" />
    <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:2.5.4.42" friendlyName="givenName" encodeType="false" />
</resolver:AttributeDefinition>
 
<!-- mail -->
<resolver:AttributeDefinition id="mail" xsi:type="ad:Simple" sourceAttributeID="mail">
    <resolver:Dependency ref="myLDAP" />
    <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:mail" encodeType="false" />
    <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:0.9.2342.19200300.100.1.3" friendlyName="mail" encodeType="false" />
</resolver:AttributeDefinition>
 
<!-- sn -->
<resolver:AttributeDefinition id="sn" xsi:type="ad:Simple" sourceAttributeID="sn">
    <resolver:Dependency ref="myLDAP" />
    <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:sn" encodeType="false" />
    <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:2.5.4.4" friendlyName="sn" encodeType="false" />
</resolver:AttributeDefinition>
 
<!-- uid -->
<resolver:AttributeDefinition id="uid" xsi:type="ad:Simple" sourceAttributeID="uid">
    <resolver:Dependency ref="myLDAP" />
    <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:uid" encodeType="false" />
    <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" encodeType="false" />
</resolver:AttributeDefinition>

o (organization name) and schacHomeOrg attributes are so called static. That means they are the same for every single user. It does not make sense to store them in an LDAP, but to have it defined statically.

<!-- o -->
<resolver:AttributeDefinition id="o" xsi:type="ad:Simple" sourceAttributeID="o">
    <resolver:Dependency ref="staticAttributes" />
    <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:o" />
    <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:2.5.4.10" friendlyName="o" />
</resolver:AttributeDefinition>
 
<!-- schacHomeOrg -->
<resolver:AttributeDefinition id="schacHomeOrg" xsi:type="ad:Simple" sourceAttributeID="schacHomeOrganization">
    <resolver:Dependency ref="staticAttributes" />
    <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:oid:1.3.6.1.4.1.25178.1.2.9" />
    <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:1.3.6.1.4.1.25178.1.2.9" friendlyName="schacHomeOrg" />
</resolver:AttributeDefinition>

Attributes received from an LDAP server employs myLDAP connector. All the information for the LDAP have been already set in the configuration file conf/ldap.properties. Static attributes are obtained from static data connector:

<!-- Static Data Connector -->
<resolver:DataConnector id="staticAttributes" xsi:type="dc:Static">
    <dc:Attribute id="o">
        <dc:Value>EXAMPLE, Ltd.</dc:Value>
    </dc:Attribute>
    <dc:Attribute id="schacHomeOrganization">
        <dc:Value>example.org</dc:Value>
    </dc:Attribute>
</resolver:DataConnector>

conf/attribute-filter.xml

After defining attributes it is needed to specify which ones will be released to service providers. This is defined in conf/attribute-filter.xml configuration file.

# Editing attribute-filter.xml
vi /opt/shibboleth-idp/conf/attribute-filter.xml

There are three different policies. One can release attributes to: * any service * a group of services (groupID, for example to a federation) * a specific service (entityID)

The following code releases givenName, sn, cn and o attributes for any service available in the federation.

<!-- Release to anyone -->
<AttributeFilterPolicy id="anyone">
    <PolicyRequirementRule xsi:type="ANY" />
 
    <AttributeRule attributeID="givenName">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="sn">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="cn">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="o">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
</AttributeFilterPolicy>

The following example releases givenName, sn, cn, o and schacHomeOrg to a group of services identified as https://example.org/metadata.

<!-- Release to Example.org group -->
<AttributeFilterPolicy id="exampleorg">
    <PolicyRequirementRule xsi:type="InEntityGroup"  
        groupID="https://example.org/metadata" />
 
    <AttributeRule attributeID="givenName">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="sn">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="cn">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="o">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="schacHomeOrg">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
</AttributeFilterPolicy>

The last example releases attributes to a specific service identified by https://www.myservice.org entitiyID. The code releases givenName, sn, cn, o, schacHomeOrg, eduPersonPrincipalName, uid and mail, so every single attribute we have defined earlier.

<AttributeFilterPolicy id="myserviceorg">
    <PolicyRequirementRule xsi:type="Requester"
        value="https://www.myservice.org" />
 
    <AttributeRule attributeID="eduPersonPrincipalName">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="uid">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="givenName">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="sn">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="cn">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="o">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="schacHomeOrg">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
 
    <AttributeRule attributeID="mail">
        <PermitValueRule xsi:type="ANY" />
    </AttributeRule>
</AttributeFilterPolicy>

metadata/idp-metadata.xml

Identity Provider's metadata (metadata/idp-metadata.xml) contains information about „endpoints“ (where to connect for authentization, etc.), however, it should always also include some non-technical/administration information such as <UIInfo> element inside <Extension> element as follows:

# Editing idp-metadata.xml
vi /opt/shibboleth-idp/metadata/idp-metadata.xml
<Extensions>
    <shibmd:Scope regexp="false">example.org</shibmd:Scope>
    <mdui:UIInfo>
        <mdui:DisplayName xml:lang="en">ORGANIZATION</mdui:DisplayName>
        <mdui:DisplayName xml:lang="cs">ORGANIZACE</mdui:DisplayName>
        <mdui:Description xml:lang="en">Identity Provider for ORGANIZATION employees.</mdui:Description>
        <mdui:Description xml:lang="cs">Identity Provider pro zaměstnance ORGANIZACE.</mdui:Description>
        <mdui:InformationURL xml:lang="en">http://www.example.org/en/</mdui:InformationURL>
        <mdui:InformationURL xml:lang="cs">http://www.example.org/cs/</mdui:InformationURL>
        <mdui:Logo height="X" width="Y">https://img.example.org/logo-200.gif</mdui:Logo>
        <mdui:Logo height="X" width="Y">https://img.example.org/logo-400.gif</mdui:Logo>
        <mdui:Logo height="X" width="Y">https://img.example.org/logo-800.gif</mdui:Logo>
    </mdui:UIInfo>
</Extensions>

There should also be specified the organization's name in <Organization> element within <EntityDescriptor> element:

<Organization>
    <OrganizationName xml:lang="en">Organization</OrganizationName>
    <OrganizationName xml:lang="cs">Organizace</OrganizationName>
    <OrganizationDisplayName xml:lang="en">ORGANIZATION</OrganizationDisplayName>
    <OrganizationDisplayName xml:lang="cs">ORGANIZACE</OrganizationDisplayName>
    <OrganizationURL xml:lang="en">http://www.example.org/en/</OrganizationURL>
    <OrganizationURL xml:lang="cs">http://www.example.org/cs/</OrganizationURL>
</Organization>

The last thing is a technical contact person, including e-mail address, specified within <ContactPerson> element. It might put right after <Organization> element:

<ContactPerson contactType="technical">
    <GivenName>GivenName</GivenName>
    <SurName>Surname</SurName>
    <EmailAddress>mailto:firtname.lastname@example.org</EmailAddress>
</ContactPerson>

Complete metadata of CESNET's IdP is available at https://whoami.cesnet.cz/idp/shibboleth address.

Login page customisation

The default login page looks similarly to the following screenshot.

Default login page appearance.

It is possible to tweak the appearance. All the modifications are made in /opt/shibboleth-idp/edit-webapp/ directory. This directory is not overwritten after upgrading Shibboleth IdP installation, so there is no need to be affraid of loosing modifications.

When done with tweaking the web appearance, a new idp.war file has to be build and Jetty restarted using the following commands:

# Rebuilding idp.war
cd /opt/shibboleth-idp
./bin/build.sh
 
# Restarting Jetty
service jetty restart
# Rebuilding idp.war and restarting Jetty terminal output
Installation Directory: [/opt/shibboleth-idp]
 
Rebuilding /opt/shibboleth-idp/war/idp.war ...
...done
 
BUILD SUCCESSFUL
Total time: 3 seconds
Stopping Jetty: OK
Starting Jetty: . . . . . . . . . . . . OK Tue Aug 11 13:15:19 CEST 2015

Adding metadata to a federation

To be a part of a federation, IdP's metadata has to be added into a federation metadata. Depending on the federation, metadata is registered using web-based management system or sent to the federation operator via email.

Startup and testing

Now, everything should be set properly and ready to test. If Jetty is not started yet, now it is the right time to run it:

# Starting Jetty
service jetty start

If Jetty is already running, but any configuration changes have been made like customizing login web page, restart should take place now. Restarting Jetty also verifies that everything works correctly and starts as expected after operating system reboot:

# Restarting Jetty
service jetty restart

Everything should be set correctly and run smoothly, thus it is time to test it out using any service within the federation the IdP is now a part of.