Shibboleth Vhosts

The topic: using one single SP (one entityID) for multiple vhosts.

We have to protect 24 vhosts (some non-SSL) with one physical service provider (SSL). The URL of the SP may be https://sp1.example.com with an entityID https://sp1.example.com/shibboleth.

Handler

The basic configuration is to set a relative handlerURL in shibboleth.xml:

<Sessions lifetime="7200" timeout="3600" checkAddress="false"
        consistentAddress="true" handlerURL="/Shibboleth.sso"
        handlerSSL="false" idpHistory="true" idpHistoryDays="7">

Metadata

Now, put the appropriate (non-SSL) endpoints in the metadata of the Shibboleth service provider. Some Shibboleth 1 Example:

<AssertionConsumerService index="7"
        Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
        Location="http://www.bib.example.com/Shibboleth.sso/SAML/POST"/>
<AssertionConsumerService index="8"
        Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
        Location="http://www.bib.example.com/Shibboleth.sso/SAML/Artifact"/>
<AssertionConsumerService index="9"
        Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
        Location="http://www.cs.example.com/Shibboleth.sso/SAML/POST"/>
<AssertionConsumerService index="10"
        Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
        Location="http://www.cs.example.com/Shibboleth.sso/SAML/Artifact"/>
...

and a Shibboleth 2 example (more endpoints):

			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
				Location="https://www.example.com/Shibboleth.sso/SAML2/POST"
				index="1" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
				Location="https://www.example.com/Shibboleth.sso/SAML2/POST-SimpleSign"
				index="2" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
				Location="https://www.example.com/Shibboleth.sso/SAML2/Artifact"
				index="3" />
			<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS"
				Location="https://www.example.com/Shibboleth.sso/SAML2/ECP"
				index="4" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
				Location="https://www.example.com/Shibboleth.sso/SAML/POST"
				index="5" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
				Location="https://www.example.com/Shibboleth.sso/SAML/Artifact"
				index="6" />
 
 
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
				Location="http://www.test.org/Shibboleth.sso/SAML2/POST"
				index="7" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
				Location="http://www.test.org/Shibboleth.sso/SAML2/POST-SimpleSign"
				index="8" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
				Location="http://www.test.org/Shibboleth.sso/SAML2/Artifact"
				index="9" />
			<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS"
				Location="http://www.test.org/Shibboleth.sso/SAML2/ECP"
				index="10" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
				Location="http://www.test.org/Shibboleth.sso/SAML/POST"
				index="11" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
				Location="http://www.test.org/Shibboleth.sso/SAML/Artifact"
				index="12" />

A complete example for a EntityDescriptor may be found here: Shibboleth SP Multi Vhost Metadata

Let's assume that a user accesses a protected resource at http://www.test.org/secure, using SAML/POST.

This way, after the authentication the SAML handle gets POSTed unencrypted to /SAML/POST and Firefox shows a warning popup about sending unencrypted data, that may confuse the users… and someone may sniff and abuse the handle. Not a good approach, even though attribute assertions will go over the secure wire between the SP and the IDP.

What is not working

You may configure an absolute handlerURL for all vhosts:

<Sessions lifetime="7200" timeout="3600" checkAddress="false"
        consistentAddress="true" handlerURL="https://sp1.example.com/Shibboleth.sso"
        handlerSSL="false" idpHistory="true" idpHistoryDays="7">

and only two SSL-endpoints in the Metadata:

<AssertionConsumerService index="1" isDefault="true"
        Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
        Location="https://sp1.example.com/Shibboleth.sso/SAML/POST"/>
<AssertionConsumerService index="2"
        Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
        Location="https://sp1.example.com/Shibboleth.sso/SAML/Artifact"/>

Now, the SAML handle gets POSTed over a secure wire always to the URL https://sp1.example.com/Shibboleth.sso/SAML/POST. However, the user consequently recieves a Shibboleth session cookie for sp1.example.com and not for www.test.org. This causes a final redirect to the configured homeURL and not to http://www.test.org/secure.

As far as I know cookies and sessions, this can't work.

Configure BrowserArtifact

  • BrowserArtifact is described here

To avoid the dialog when protocting a non-SSL vhost you may switch to artifacts (instead of browser/post). This is done by changing the SessionInitiator in the SP's configuration file (shibboleth2.xml) to use other ACSs. In the following example the acsIndex attribute points to the artifact endpoints:

			<SessionInitiator type="Chaining" Location="/Login"
				isDefault="true" id="Intranet" relayState="cookie"
				entityID="https://shi-idp3.rz.fh-muenchen.de/idp/shibboleth">
				<SessionInitiator type="SAML2" acsIndex="3"
					template="bindingTemplate.html" />
				<SessionInitiator type="Shib1" acsIndex="6" />
			</SessionInitiator>
 
	                <md:AssertionConsumerService Location="/SAML2/POST"
				index="1" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" />
			<md:AssertionConsumerService Location="/SAML2/POST-SimpleSign"
				index="2" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" />
			<md:AssertionConsumerService Location="/SAML2/Artifact"
				index="3" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" />
			<md:AssertionConsumerService Location="/SAML2/ECP"
				index="4" Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" />
			<md:AssertionConsumerService Location="/SAML/POST"
				index="5" Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" />
			<md:AssertionConsumerService Location="/SAML/Artifact"
				index="6" Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01" />

Hints / Troubleshooting

Using Artifacts causes the SP to send signed SAML messages when it is connecting to the IDP. Thus, you should make sure that

  • the certificates in the metadata and the SP / IDP match
  • you do not configure <KeyDescriptor use=“encryption”> in your metadata but only

<KeyDescriptor> for your certificates (took me half a day to find this error!)

  • enable encryption and signing
<ApplicationDefaults id="default" policyId="default"
   entityID="https://moodle-test.cc.private.hm.edu/shibboleth" homeURL="https://moodle-test.cc.private.hm.edu/index.html"
   REMOTE_USER="uid" signing="true"
   encryption="true">
  • you may have to set a default acs with the isDefault attribute:
<md:AssertionConsumerService Location="/SAML2/Artifact"
   index="3" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" isDefault="true"/>

SSL Vhosts

You may configure SSL vhosts for each non-SSL vhost and the appropriate SSL endpoints in the metadata. Then you are able to use SAML/POST.

Modern browsers support the subjectAltName extension of TLS certificates so you need only one IP and one certificate for all SSL vhosts! Details are described at SSL vhosts

This approach has a lot of configuration overhead (the SSL vhosts).

  • The tags and Attributes in shibboleth.xml are described here

Scripts

The following script creates the endpoints for all vhosts and aliases. Run it and copy the contents of the outputfile vhost-endpoints.xml.inc.

You may have to change the location of your apache's configuration file:

my $rc = $c1->parse_file('/opt/pkg/etc/httpd/httpd.conf');
#!/usr/local/bin/perl 
 
my $LOGFORMAT = "1";
 
use Apache::ConfigParser;
 
use strict;
 
 
 
 
my $vhost_template =
'			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://SITE/Shibboleth.sso/SAML2/POST"
				index="h1" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
				Location="http://SITE/Shibboleth.sso/SAML2/POST-SimpleSign"
				index="h2" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
				Location="http://SITE/Shibboleth.sso/SAML2/Artifact"
				index="h3" />
			<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS"
				Location="http://SITE/Shibboleth.sso/SAML2/ECP"
				index="h4" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
				Location="http://SITE/Shibboleth.sso/SAML/POST"
				index="h5" />
			<AssertionConsumerService
				Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01" Location="http://SITE/Shibboleth.sso/SAML/Artifact"
				index="h6" />' . "\n";
 
my $index = 1;
 
sub addVhost {
	my $site  = shift;
	my $ssl = shift;
	my $vhost = $vhost_template;
	$vhost =~ s/SITE/$site/g;
	$vhost =~ s/h1/$index/g;
	$index++;
	$vhost =~ s/h2/$index/g;
	$index++;
	$vhost =~ s/h3/$index/g;
	$index++;
	$vhost =~ s/h4/$index/g;
	$index++;
	$vhost =~ s/h5/$index/g;
	$index++;
	$vhost =~ s/h6/$index/g;
	$index++;
	if ($ssl == 1) {
		$vhost =~ s/http/https/g;
	}
	return $vhost;
}
 
 
 
# Create a new empty parser.
my $c1 = Apache::ConfigParser->new;
 
# Load an Apache configuration file.
my $rc = $c1->parse_file('/opt/pkg/etc/httpd/httpd.conf');
 
# If there is an error in parsing the configuration file, then $rc
# will be false and an error string will be available.
if ( not $rc ) {
	print $c1->errstr, "\n";
}
 
# Get the root of a tree that represents the configuration file.
# This is an Apache::ConfigParser::Directive object.
my $root = $c1->root;
 
# Get all of the directives and starting of context's.
my @directives = $root->daughters;
my %vhosts;
my $content_xml = "";
foreach my $directive (@directives) {
	if ( $directive->name eq "virtualhost" ) {
		my $ip = $directive->value;
		print $directive->value . "\n";
		my $ssl = 0;
		#check for ssl 
		if ($ip =~ /:443/) {
			$ssl = 1;
		}
		my @vhost_directives = $directive->daughters;
		my %aliases;
		my $server_name = undef;
		foreach my $vhost_directive (@vhost_directives) {
			if ( $vhost_directive->name =~ /serveralias/ ) {
				$aliases{ $vhost_directive->value } = 1;
			}
			if ( $vhost_directive->name =~ /servername/ ) {
				$server_name = $vhost_directive->value;
			}
			if ( $vhost_directive->name =~ /SSLEngine/ && $vhost_directive->name =~ /On/ ) {
				$ssl = 1;
			}
 
		}
 
		my $vhost_xml = addVhost( ${server_name}, $ssl );
		$content_xml .= $vhost_xml;
 
		my $server_aliases = "";
		while ( my ( $k, $v ) = each %aliases ) {
			print "key: $k, value: $v.\n";
			$content_xml .=  addVhost( ${k}, $ssl);
		}
 
 
	}
}
 
my $server_aliases = "";
 
print "Writing vhost-endpoints.xml.inc...\n";
open OUT, "> vhost-endpoints.xml.inc";
print OUT $content_xml;
close OUT;

Tests

 
sp_vhosts.txt · Last modified: 2012/03/24 03:10 (external edit)
Recent changes RSS feed Creative Commons License Driven by DokuWiki Made on Mac