Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security SAML using multiple subdomains to access the same application (same IP)

I have one application which is accessed via multiple subdomains, because of branding and support information which is dynamically shown. We're moving to an SSO solution, which is now making this a little interesting. Currently I added Spring Security SAML (1.0.1) into the Spring Security mix, and am using WSO2 Identity Server at the IdP. It runs great.

Where were are having issue is preserving the subdomain of the Service Provider side when there are multiple DNS entries for the same address. For example, let's say I have the following subdomains going to the exact same place, guy1.mistersite.com and guy2.mistersite.com. When accessing the application for absolute first time when the app server is first started via guy1.mistersite.com, the AuthnRequest coming from the SP will have indicate in the AssertionConsumerServiceURL and Issuer that it is coming from guy1.mistersite.com. Good. Now the next access tried from guy2.mistersite.com, the AssertionConsumerServiceURL and Issuer will actually still have guy1.mistersite.com in the AuthnRequest. Bummer.

Looking through the code, you can see it's effectively cached in org.springframework.security.saml.metadata.MetadataGeneratorFilter. When the processMetadataInitialization() method is hit, is checks whether the hostedSPName, entityAlias, and defaultBaseURL were set. If they were, well, move onward. That makes sense.

Is subclassing MetadataGeneratorFilter and have an override of processMetadataInitialization() really an effective and "safe" solution?

I already made a little proof of concept and commented out all the null checks so each value is reestablished each time this method is hit, so at a simple case this works fine. I am wondering since this bean is essentially a singleton, what's going to happen when a whole of a different subdomain requests are coming at once. Is there a chance stuff's going to get jumbled up? Is the synchronized block in that method enough to protect against that consideration?

So yes, if this is a safe and sane way to approach this, like if I'm on the right track or not, let me know! Anything else to think about. What I am right about, what am I not? :)

We are considering altering the model of approach as far as the infrastructure and subdomains go, so I'd please like to stick on the topic of the viability of extending Spring Security SAML with a new class or not.

Gratitude in advance! ;)

Below is my quick fix on the method (just commented some stuff out basically):

    protected void processMetadataInitialization(HttpServletRequest request) throws ServletException {

        // In case the hosted SP metadata weren't initialized, let's do it now
//Forcing this to reload the host SP name every time to deal with various subdomains where a user could come to for the same app (think branding for partner companies)
//        if (manager.getHostedSPName() == null) {

            synchronized (MetadataManager.class) {

//                if (manager.getHostedSPName() == null) {

                    try {

                        log.info("No default metadata configured, generating with default values, please pre-configure metadata for production use");

                        // Defaults
                        String alias = generator.getEntityAlias();
                        String baseURL = getDefaultBaseURL(request);

                        // Use default baseURL if not set
//                        if (generator.getEntityBaseURL() == null) {
                            log.warn("Generated default entity base URL {} based on values in the first server request. Please set property entityBaseURL on MetadataGenerator bean to fixate the value.", baseURL);
                            generator.setEntityBaseURL(baseURL);
//                        } else {
                            baseURL = generator.getEntityBaseURL();
//                        }

                        // Use default entityID if not set
//                        if (generator.getEntityId() == null) {
                            generator.setEntityId(getDefaultEntityID(baseURL, alias));
//                        }

                        EntityDescriptor descriptor = generator.generateMetadata();
                        ExtendedMetadata extendedMetadata = generator.generateExtendedMetadata();

                        log.info("Created default metadata for system with entityID: " + descriptor.getEntityID());
                        MetadataMemoryProvider memoryProvider = new MetadataMemoryProvider(descriptor);
                        memoryProvider.initialize();
                        MetadataProvider metadataProvider = new ExtendedMetadataDelegate(memoryProvider, extendedMetadata);

                        manager.addMetadataProvider(metadataProvider);
                        manager.setHostedSPName(descriptor.getEntityID());
                        manager.refreshMetadata();

                    } catch (MetadataProviderException e) {
                        log.error("Error generating system metadata", e);
                        throw new ServletException("Error generating system metadata", e);
                    }

//                }

            }

//        }

    }

And portion of the security config:

    <bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
        <constructor-arg>
            <bean class="org.springframework.security.saml.metadata.MetadataGenerator">
                <property name="extendedMetadata">
                    <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                        <property name="idpDiscoveryEnabled" value="false"/>
                        <property name="signMetadata" value="false"/>
                    </bean>
                </property>
                <!-- We leave these out and values are defaulted to entityId = url that user came in on
                and entityId = <entityBaseUrl>/saml/metadata.  We have to configure a SP in WSO2 for every
                subdomain we have -->
                    <property name="wantAssertionSigned" value="false" />
            </bean>
        </constructor-arg>
    </bean>
like image 713
jeremy simon Avatar asked Nov 09 '22 08:11

jeremy simon


1 Answers

I have been googling around for this and it have seen two possible approaches. According to the spec you can sign the authentication requests as the SP in order to specify an AssertionConsumerServiceURL without the requirement that it was published and configured earlier as part of the SP metadata exchange, but I don't fully understand how to do that with Spring Security SAML extendtion. Here is the source of that information.

Another that I have been debating doing myself would be to override the buildSPSSODescriptor in MetadataGenerator and then look up the possible subdomains for that environment (we have that available in our database) and then I would loop over that list and create an entry for each one.

Like your way of doing it this last way seems like a hack as well. Hopefully this helps.

What did you end up doing?

like image 157
testing123 Avatar answered Dec 21 '22 13:12

testing123