|
SAML stands for Security Assertion Markup Language. It's an XML-based markup language and also an open security protocol standard. The most important feature of this protocol is Web Browser Single Sign-On (SSO.) SSO allows users to login once and never be prompted for login/password later.
In this post we'll describe how to add SAML support to existing web application with the bare minimum of modifications to the application code itself.
While it's not impossible to implement the SAML protocol standard manually, it's much easier (and much less time-consuming) to take a ready to use solution, such as Shibboleth, and this is what we are going to do: we'll integrate an existing JBoss application, running behind an Apache web server, with Shibboleth SAML2 authentication.
The first step would be to download and install Shibboleth. Depending on your system, you might need to build it from source (or source RPMs,) but on Debian it comes pre-packaged:
# apt-get install libapache2-mod-shib2This will download and install Shibboleth authentication Apache module as well as
shibd daemon, which handles the details of
SAML protocol.
Currently, Debian ships with 2.3 series of Shibboleth, while the newest released version is 2.4. There's not much difference between the two versions (although the newer one allows for more compact configuration file.) If you need the 2.4 version, you'll have to install it in some more involved way. Refer to the installation docs here: https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLinuxInstall
Generally, Shibboleth works out of the box, but you need to provide some minimal configuration for it to do anything sensible.
Note: when installing from Source RPMs on
rpm-based systems, you might need to adjust some build parameters or
make /etc/XML-Tooling a symlink to
/etc/shibboleth, since different parts of it seem to look
for configuration files in different directories (I don't recall the
exact name for the first dir, but strace is your friend
if shibd won't start and print some cryptic errors on
console.)
For this scenario we will be configuring Shibboleth as a SAML
Service Provider (the authentication side which provides restricted
access to the resources and asks SAML Identity Provider for user
access authorization.) Start by editing
/etc/shibboleth/shibboleth2.xml and replace the default
value of the entityID attribute in
ApplicationDefaults element with some string which
identifies your Service Provider in a unique way.
Typically a URL of the form
http://my.service-provider.com/shibboleth can be used
here (where /shibboleth URI not necessarily represents a
resource which can be actually fetched using HTTP, it's just a common
naming convention.)
In order to present your Service Provider with a security
certificate you'll need to add/edit the
CredentialResolver configuration element. On Debian (with openssl package
installed) you may use the auto-generated "SnakeOil" certificate and
private key, like this (for testing purposes only):
<CredentialResolver type="File"
key="/etc/ssl/private/ssl-cert-snakeoil.key"
certificate="/etc/ssl/certs/ssl-cert-snakeoil.pem"/>
Be sure to replace this with the real certificate and key once you're
done with your testing.
Note: you'll need to restart shibd daemon for this change to take effect, since the above private key is normally not world-readable. Other changes to Shibboleth configuration take effect w/o a restart generally.
Now, any Service Provider (SP) in SAML must know to which Identity
Providers (IdP) it should talk when authorizing or denying access to
any particular restricted resource. To give your SP this information
you'll need to edit entityID attribute of an appropriate
SessionInitiator element (pre-2.4 versions) or of a
SSO element (versions 2.4 and higher.) The
SSO element in the newer versions' configuration is a
nice shortcut to replace the whole series of configuration elements
required in earlier versions (those, on the other hand, provide more
flexibility.)
After putting the appropriate entityID for the IdP
you are going to use for user authentication, you also need to add the
IdP metadata file to the configuration, using an element like this:
<MetadataProvider type="XML" file="idp-metadata.xml"/>Where
idp-metadata.xml is the XML-file containing SAML
metadata for the IdP being used. You should contact your IdP to
obtain this file (if you don't have this information at this point,
refer to the "Testing" section below for testing options.) There is
also a possibility to refer to IdP metadata by using a URL to it. The
Shibboleth daemon will poll the URL periodically to check for any
update on the metadata. Refer to Shibboleth documentation on
MetadataProvider configuration element for details.
The above sections cover the Shibboleth configuration. However, this doesn't automagically adds SAML to your application... yet.
Shibboleth comes with a standalone daemon which actually handles the authentication and a web-server module, which observes client requests and talks to the Shibboleth daemon to deny or authorize the access. In this post we will be configuring Apache web server, please refer to Shibboleth documentation for use with different web servers.
To enable Shibboleth authentication for /secure URI
on your server, add the following block to your
VirtualHost:
<Location /secure>
AuthType shibboleth
ShibRequestSetting requireSession 1
Require valid-user
</Location>
Or use / location to restrict access to your site as a
whole. Restart or reload Apache to make the change take it's effect
(by the way, Shibboleth daemon doesn't require a restart after
modification of it's configuration files: it reloads them
automatically.)
At this point your Shibboleth and Apache installations are configured and now it's time to test the setup. However, sometimes you won't have the Identity Provider metadata or you just want to perform some internal testing before approaching the IdP.
You may try setting up a local IdP using the same Shibboleth installation for your test. Also there are some public online IdPs which you may use to test your newly setup SP, but I've found that installing a different SAML implementation for testing IdP works the best. First, testing on a different implementation gives your extra confidence that you've got it right, as opposed to testing with the same implementation as that of SP. Second, a locally installed solution allows for easier debugging, as opposed to using established online services.
I've found that SimpleSAMLphp works really well for this purpose and requires minimum of configuration. So you might want to install it either on the same host or use a separate host for your testing IdP.
After configuring your testing IdP you'll need to add your SP
metadata, so the IdP may verify the authentication requests coming
from your SP. The SP metadata with Shibboleth is not stored in a
file, but generated on request to
http://localhost/Shibboleth.sso/Metadata (by default,
from localhost only.) SimpleSAMLphp provides a web form to parse the
XML metadata into it's internal format (PHP code actually) which you
may paste into the IdP configuration file afterwards (typically,
simplesamlphp/metadata/saml20-sp-remote.php.)
When this is done you are ready to test. Try accessing a restricted URL on your Service Provider site and watch you being redirected to your testing Identity Provider for authentication. After filling the login form correctly, you should be redirected back to your SP site.
However, at this point we didn't make any change to our application, so it won't notice any difference when we visit the restricted URL and are already authenticated with the IdP.
Shibboleth passes the authentication information to the web
application in form of CGI environment variables
($_SERVER array variable in PHP.) The sign of an
established SAML session is presence of Shib-Session-ID
environment variable.
However, if you use JBoss application server your code lives in a
different process and has no direct access to the Apache server
environment. To pass the required variables from Apache to JBoss
you'll need to add the following mod_jk configuration
directive:
JkEnvVar Shib-Session-IDAfter that, you may get the value of this variable using
request.getAttribute("Shib-Session-ID") in your Java or
JSP code.
Now you may tell apart SAML-authenticated access to your application from the non-authenticated. But session id is not suitable for identifying actual users, so how would you tell different users apart?
Here's where additional Shibboleth configuration files come into
play. First, you might want to check the shibd.log file
after successfully authenticating with your IdP. It will list the
user identity attributes extracted from the IdP SAML response (such as
name, email, affiliation, etc.)
If you don't see the expected attributes in the log, you might
need to edit attribute-map.xml configuration file to add
some Attribute elements. If you don't know which
attributes to expect, it might be helpful to increase logging level
for your SP or IdP to capture the exact SAML response XML text being
sent and look for saml:Attribute elements which might
look familiar. For example, you might want to add something like this
to the attribute map file:
<Attribute name="username" id="SAML-UserName"/>This will pass the value of SAML
username attribute in
the SAML-UserName environment variable (remember to add
appropriate mod_jk directives if you need to pass them
further to JBoss.)
As a special case, the user identity (NameID)
attributes extraction require special configuration syntax. For
example:
<Attribute name="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
id="SAML-UserName">
<AttributeDecoder xsi:type="NameIDAttributeDecoder" formatter="$Name"
defaultQualifiers="false"/>
</Attribute>
Refer to Shibboleth documentation for details.
Now you should be able to identify a SAML-authenticated user. Depending on your application details you might need to find the corresponding user record in your application's authentication structures or add the new user unconditionally (after all you trust the IdP that the user is the one he pretends to be.)
Note: this is the first place where you'll need to touch your application code. All of the above only requires external configuration changes.
If you successfully managed to handle Single Sign-On using the above information, you'll likely want to add handling for Single Logout, so that user may log out of your Service Provider as well as Identity Provider.
Single Logout in Shibboleth is handled by
/Shibboleth.sso/Logout URI by default. You might want to
redirect user to that URI after logging out of your application (local
logout.) This is the second place where your application code
might need to be modified.
In this post we've shown how you can add SAML-based authentication to your application with the minimum of changes to the application code, with the use of Shibboleth SAML authentication engine.