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.
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:
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.
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.
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.
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.
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
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>
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>
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.
The default login page looks similarly to the following screenshot.
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
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.
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.