Skip to content

Add proxy/load-balancer/ssl termination support, like SAMLContextProviderLB #8977

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jdzijlstra opened this issue Aug 20, 2020 · 6 comments
Closed
Labels
status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement

Comments

@jdzijlstra
Copy link

jdzijlstra commented Aug 20, 2020

Current Behavior

I have a springboot application using SAML2 (using spring-security-saml2-service-provider). My application (the SP) needs to work with Microsoft Azure (the IDP) and is running in a docker image behind a proxy with SSL termination.
Azure (rightfully) requires my 'service-url' ( assertionConsumerServiceUrlTemplate) to use https.

This means that for Azure, my application is accessible via https only, where the proxy proxies to my application via http, so my application receives an http-request. Therefore my 'service-url' is set to an https-address, otherwise Azure cannot access my application via the proxy, but the application reconstructs the URL, via the request, to an http-address.

The problem now is that the response from Azure is not accepted. I get the error: "An error occurred while validating the assertion: No subject confirmation methods were met for assertion with ID '_90d4b4e4-ba9f-44ee-9fa2-4cbf4f951f00'".

Debug-logging shows

DEBUG [http-nio-9500-exec-7] org.opensaml.saml.saml2.assertion.impl.AbstractSubjectConfirmationValidator - Evaluating SubjectConfirmationData@Recipient of : https://myhost/login/saml2/sso/myapp
DEBUG [http-nio-9500-exec-7] org.opensaml.saml.saml2.assertion.impl.AbstractSubjectConfirmationValidator - Failed to match SubjectConfirmationData@Recipient to any supplied valid recipients: [http://myhost/login/saml2/sso/myapp]
DEBUG [http-nio-9500-exec-7] org.opensaml.saml.saml2.assertion.SAML20AssertionValidator - No subject confirmation methods were met for assertion with ID '_242557fc-f506-4c1d-b50a-49edd4430f00'

(of course myhost and myapp are replaced)

The SubjectConfirmationData@Recipient ('https://myhost/login/saml2/sso/myapp') is received from the IDP, but is actually the value set via

    RelyingPartyRegistration.withRegistrationId(...) 
                            .localEntityIdTemplate(...) 
                            .assertionConsumerServiceUrlTemplate("https://myhost/login/saml2/sso/myapp")

The supplied valid recipients ('http://myhost/login/saml2/sso/myapp') is deducted from the http-request (in Saml2WebSsoAuth.attemptAuthentication: request.getRequestURL().toString()):

Expected Behavior

I'd like to be able to overrule the URL (valid recipients), and not always deduct it from the request.
In my case I would like to overrule the scheme, but I can image that also the hostname, port and path may be necessary to be overruled.

Actually, while thinking it over again, shouldn't the URL as received from the IDP not be compared to the value as configured in RelyingPartyRegistration.assertionConsumerServiceUrlTemplate(String)? So, we tell the IDP to contact us at a certain URL and in the message says it is meant for that URL and we check if that URL is indeed the URL we have configured.
Thus, i.s.o. passing request.getRequestURL().toString() to Saml2WebSsoAuth.attemptAuthentication, pass in the value based on rp.getAssertionConsumerServiceUrlTemplate() (and the request to compute {baseUrl} if it is in there)

Context

At this moment I don't see a workaround, other than let my application (Tomcat) handle the HTTPS itself, but this is not allowed by my company.
Another workaround is not to use spring-security-saml2-service-provider, but the 'old' spring-security-saml. This requires of course a rewrite of my code get that to work again.

@jdzijlstra jdzijlstra added status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Aug 20, 2020
@jdzijlstra
Copy link
Author

Maybe this is a bug and not an enhancement request.

@jdzijlstra
Copy link
Author

jdzijlstra commented Aug 21, 2020

I've patched Saml2WebSsoAuthenticationFilter locally with

101a102
> 		String recipientUri = Saml2ServletUtils.resolveUrlTemplate(rp.getAssertionConsumerServiceUrlTemplate(), applicationUri, rp);
104c105
< 				request.getRequestURL().toString(),
---
> 				recipientUri,

This works for me.

@jzheaux
Copy link
Contributor

jzheaux commented Aug 25, 2020

@jdzijlstra, I believe this is resolved by #8887, which was released as part of 5.4.0-RC1. It ensures that the ACS URL is compared against RelyingPartyRegistration#assertionConsumerServiceLocation. Also, if that's not quite what you need, it provides an interface where you can modify how the RelyingPartyRegistration is resolved.

If after you try 5.4.0-RC1, you discover that you need something more, please feel free to re-open the issue.

@jzheaux jzheaux closed this as completed Aug 25, 2020
@dailytabs
Copy link

dailytabs commented Oct 15, 2021

I'm using 5.5.2 and can't get my providers SAML working.

Works fine in lower envs, but the higher envs have an nginx proxy. The provider sends to URL with context, nginx removes context, now the SAML data doesn't match with what Spring thinks is the full URL. This is apparently not supported in any way?

Updated nginx to proxy full URL and added server.servlet.context-path to properties files. Now, Spring knows about the URLs and they should match, right? Well, the incoming URL (Received [POST /api/login/saml2/sso/default HTTP/1.1) triggers the filter, but I still get SubjectConfirmationData > Recipient errors that get reported as "invalid_destination". How is this possible, they are identical and are built by you, so why don't they match?

Both the top-level Destination and the SubjectConfirmationData > Recipient have identical values. And they match the public URL exactly.

I assume the proxy sending it to http://localhost:8080 is the issue? How do I tell Spring Security SAML about this?

I can't (or don't know how) to take advantage of DefaultRelyingPartyRegistrationResolver, since I am using:

    return new InMemoryRelyingPartyRegistrationRepository(
            RelyingPartyRegistrations.fromMetadataLocation(properties.getSaml2().getIdpMetadataUri())
                                     .registrationId("default")
                                     .entityId(properties.getUiUrl())
                                     .assertingPartyDetails(party -> party.wantAuthnRequestsSigned(false))
                                     .build()
    );

We have ansible download the metadata and deploy it on the server, the getIdpMetaDataUri comes from a custom spring property (ansible sets that, too) and has the path of our IdPs metadata.xml (e.g. file:///path/to/metadata.xml). This allows us to "autoconfigure" the majority of things, then we have to override their signing request, as we are using IdP-initiated only, and there is no point in adding a local cert to never use it (spring security blows up on boot if metadata requests it and you don't have the properties set/beans created). This could be made simpler by allowing the already existing property to override the metadata - but that may be a spring boot autoconfigure issue, not yours.

[It's also standard, when asserting, to log both the actual and the expected on failure. Your log messages only show one of these and they don't even make it clear which it is (the actual or expected). This is not helpful.]

@jzheaux
Copy link
Contributor

jzheaux commented Oct 28, 2021

I assume the proxy sending it to http://localhost:8080 is the issue? How do I tell Spring Security SAML about this?

In Spring Boot, you can use server.forward-headers-strategy: framework to use ForwardedHeaderFilter. If this doesn't seem to work, I'd recommend asking a question on StackOverflow.

This could be made simpler by allowing the already existing property to override the metadata - but that may be a spring boot autoconfigure issue, not yours.

This sounds like what Spring Boot already does, so it might be worth it to log an issue in Spring Boot with a sample demonstrating what's not working for you.

It's also standard, when asserting, to log both the actual and the expected on failure

I think the logging could be improved. The logs are defensive because these are user-specified values. To add values to the logs, the values would ideally be escaped or only logged when signature validation has passed. You are welcome to file an issue.

@dailytabs
Copy link

I'm sorry, I thought I deleted this. We were able to solve it by modifying the ACS during our registration setup by passing in a property for the base url part: .assertionConsumerServiceLocation(properties.getApiUrl() + "/login/saml2/sso/{registrationId}"). I completely forgot that the ACS included {baseUrl} - thought it was a path only.

Although, your solution of server.forward-headers-strategy may be simpler, so I will check that out!

Apologies for the trouble and the necro.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants