If you want to deploy multiple Shibboleth 1.3 IdPs and load balance across them, either for performance or fail-over reasons, there are several things you have to consider. First, the IdP contains some state that maintained in memory, this means that when a client makes an initial request to one IdP node the next request, which will likely end up at a different node, will fail because the second node does not have the necessary state information. This is true for a lot of load balanced applications and so almost all the load balancing solutions have a setting that allows subsequent requests from a client to be directed back to the same server that the original request went to. This solves the first problem but not the second issue, namely that the client isn't the only one contacting the IdP. The service provider also contacts the IdP to make attribute requests or resolve artifacts. This request comes from an IP that is different than the clients and thus will likely be sent to a different node than the client is talking to which again won't have the needed state information to act on the request. These problems can be addressed by dealing with the areas on the IdP as discussed next.
Since the IdP does not currently handle user authentication directly, you first need to consider the load balancing implications of the software you're using for that purpose. Since such systems often track users with a cookie, this may be simple, but if there is in-memory state, it may not be so simple. For those using Tomcat itself (or Apache) for authentication, version 1.3.1 now contains an option to enable cookie-based SSO as an extension of your regular authentication mechanism. The Crypto Handle mapping plugin described below is used to read and write an encrypted cookie containing the principal name and an expiration. This allows one server in a cluster to authenticate a user and then create the cookie, while the other servers read it later.
Prerequisites to enabling SSO include:
web.xml
and IdPXml except that one path is left unprotected and the other path is protected by the authentication mechanism in use. The location you publish in your metadata to SPs is the unprotected location.To enable the SSO option, you add the following attributes to the <IdPConfig>
element in IdPXml:
<IdPConfig ... authHeaderName="COOKIE" cryptoHandleId="crypto" protectedPath="/AuthSSO" ... > |
The reference to the Crypto Handle must match the identifier of the mapping definition in the file, and the protected path depends on how you decide to set up your locations.
The lifetime of the SSO session will be dictated by the handleTTL
value of the Crypto Handle mapping definition.
The Shibboleth IdP needs a way to translate a handle back in to the ID of the user it was generated for. This is accomplished by the Name Identifier Mapping whose default implementation simply uses an in-memory Map. Obviously this suffers from the problem discussed above. There are three ways to address this.
Instead of using the standard handle, which is just a generated random string you can use a handle generated by encrypting the principal's ID (with some random data). This way it is still opaque to the service provider but the principal can be reclaimed from the handle itself, by decrypting it, even if the handle is sent to a different IdP node than the one that created it. Setting this up is pretty simple, just follow these steps:
NameMapping
element and make a copy of itid
attribute to some descriptive name (e.g. cryptohandle)type
attribute to CryptoHandleGeneratorNameMapping
element, a KeyStorePath
element with the absolute path to the handle.jks file as its contentNameMapping
element, a KeyStorePassword
element with the keystore's password as its content (default is shibhs)NameMapping
element, a KeyStoreKeyPassword
element with the secret key's password as its content (default is shibhs)NameMapping
element, a KeyStoreKeyAlias
element with the alias you assigned the secret key as its content (default is handleKey)NameMapping
element, a KeyStoreType
element with the appropriate type identifier as its contentRelyingParty
element(s) that you want to use the crypto handle withnameMapping
attribute on the RelyingParty
child element NameID
to the name mapping ID you enerted in step ii.Note if you want to modify the default passwords or key alias, you can edit the build.xml ant script and modify the properties in the genSecret command.
Another option is to use the principal's ID as the handle. Before doing this consider whether the loss of anonymity provided by an opaque identifier is acceptable. If this is acceptable you can enable this handle mechanism by editing your idp.xml file as follows. Located the NameMapping element and change the type attribute to Principal
(it's probably currently set to SharedMemoryShibHandle
unless you've changed it before). Remember to do this on each IdP node.
This is certainly the most complicated method as it requires you to implement custom code. If you're going to pursue this route it is strongly recommended that you use the Shibboleth extension mechanism provided instead of placing your custom code directly in the Shibboleth source tree. Here's how to use your own handle mechanism.
edu.internet2.middleware.shibboleth.common.provider.BaseNameIdentifierMapping
and implement the getPrincipal()
and getNameIdentifier()
methods. The getNameIdentifier
method should create the mapping from principal ID to SAML name identifier (the handle in Shib parlance) some place that can be accessed by every node, perhaps in a database. The getPrincipal
method should reverse the process.NameMapping
attribute and make a copy of it.type
attribute with an attribute called class
that contains the full class name of the name identifier map you created in step 1.id
attribute. It doesn't matter what this is, just remember it for the next step.RelyingParty
element and its NameId
child elementnameMapping
attribute to whatever id you gave your custom name mapping two steps ago.Like the name mappings the IdP keeps the Map of artifacts to the SAML documents they resolve to in memory, by default. Unfortunately, at this time, there is no easy way to deal with this. The easiest thing to do is to not use Artifacts yet. If you need to use Artifacts and you need to deploy in a load balanced environment you can implement and use a custom Artifact mapper. If you're going to pursue this route it is strongly recommended that you use the Shibboleth extension mechanism provided instead of placing your custom code directly in the Shibboleth source tree. Here's how:
edu.internet2.middleware.shibboleth.artifact.ArtifactMapper
such that the artifact <-> SAML document mapping is stored somewhere accessible by all your IdP, perhaps a databaseArtifactMapper
elementimplementation
attribute to the full name of the class you created in stepCurrent versions of the IdP include a plugin that generates eduPersonTargetedID values dynamically by hashing various pieces of data. While this approach does work in a load-balanced environment, deployers are strongly cautioned that this plugin is merely for experimentation and is not suitable for production use. Proper use of persistent identifiers like this attribute requires more permanent storage, the ability to easily update the values, and the ability to map from them back to actual users when needed. They also shouldn't change just because other identifiers change. Deployers are strongly urged to consider developing a more robust plugin suitable for their environment before supporting this attribute.
HA-Shib is an IdP 1.3 extension that provides Name Identifier and Artifact Mapping implementations that replicate state across nodes within a cluster. This solves the issues described with these mappers in a load balanced environment.