I tried to access my application on CloudFoundry with the following configuration in the spring security xml
<intercept-url pattern="/signup*" access="permitAll" requires-channel="https" />
but it gives me error This webpage has a redirect loop
However when I changed it to requires-channel="http" I can see my page normally. In both cases I used https on my application. Is this the expected behavior ?
First of all, taking a step back, this (https://johnpfield.wordpress.com/2014/09/10/configuring-ssltls-for-cloud-foundry/) provides excellent context for the nature of the problem.
The key paragraph being
“The threat model here is that the entry point to the cloud is a high availability, secured proxy server. Once the traffic traverses that perimeter, it is on a trusted subnet. In fact, the actual IP address and port number where the Tomcat application server are running are not visible from outside of the cloud. The only way to get an HTTP request to that port is to go via the secure proxy. This pattern is a well established best practice amongst security architecture practitioners.”
Therefore, we may not want or need SSL all the way down, but read on to see how to avoid the https redirect issue when using Spring Security deployed on Cloud Foundry.
You will have a load balancer, HAProxy or some kind of proxy terminating SSL at the boundary of your Cloud Foundry installation. As a convention, whatever you are using should be configured to set X-Forwarded-For and X-Forwarded-Proto headers. The request header “X-Forwarded-Proto" contains the value http or https depending on the original request and you need to use this header parameter for your security decisions further down the stack.
The cleanest way to do this is at the container level, so that Spring Security behaves the same independent of deployment container. Some typical options to configure this are as follows
1) Tomcat
Tomcat should be configured with a RemoteIPValve as described nicely here
The good news is that the Java buildpack for Cloud Foundry already does this for you as seen here
2) Spring Boot (Embedded Tomcat)
Because Tomcat is embedded, the Tomcat config in the Java buildpack will not be activated (see the buildpack Detection Criterion), and therefore some internal Spring Boot configuration is required. Luckily, it’s pretty trivial to configure as you would expect with Spring Boot and you can switch on Tomcat’s RemoteIPValve as explained here by simply defining
server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto
Both approaches lead to the same outcome of the Tomcat valve overriding the ServletRequest.isSecure() behaviour so that the application has no knowledge of the usage of any proxying. Note that the valve will only be used when the “X-Forwarded-Proto" header is set.
Alternatively, if you really want to go low-level you can dig into the guts of Spring Security, as demonstrated here. As part of that effort, there are some useful findings on how to make the “X-Forwarded-Proto" header available via the Servlet API for other containers (WebLogic, JBoss, Jetty, Glassfish) shared on the comments of https://github.com/BroadleafCommerce/BroadleafCommerce/issues/424
As an additional note, CloudFlare can also act as the SSL-terminating reverse proxy (this is the recommended approach via PWS as discussed here) and it does indeed forward the relevant headers.
References
https://stackoverflow.com/a/28300485/752167
http://experts.hybris.com/answers/33612/view.html
https://github.com/cloudfoundry/java-buildpack/commit/540633bc932299ef4335fde16d4069349c66062e
https://support.run.pivotal.io/entries/24898367-Spring-security-with-https
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-servlet-containers.html#howto-use-tomcat-behind-a-proxy-server
I have the same issue when I tried to secure my pages with HTTPS using Spring Security.
From the discussion on CloudFoundry Support, seems they "terminate SSL connections at the router". See "Is it possible to visit my application via SSL (HTTPS)?".
And after more than a year, no further information I can find regarding this issue.
In case it's still useful ... I found this post gave the clue to solve something similar to this.
The problem was the org.springframework.security.web.access.channel.SecureChannelProcessor bean was using ServletRequest.isSecure() to decide whether to accept the connection or redirect, which was getting confused inside the cloud.
The following override to that bean seemed to do the job under BlueMix - not sure if the $WSSC request header will apply to all environments.
#Component
public class ChannelProcessorsPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
if (bean instanceof SecureChannelProcessor) {
final SecureChannelProcessor scp = (SecureChannelProcessor) bean;
return new ChannelProcessor() {
#Override
public void decide(FilterInvocation invocation,
Collection<ConfigAttribute> config) throws IOException,
ServletException {
HttpServletRequest httpRequest = invocation.getHttpRequest();
// Running under BlueMix (CloudFoundry in general?), the
// invocation.getHttpRequest().isSecure() in SecureChannelProcessor
// was always returning false
if ("https".equals(httpRequest.getHeader("$WSSC"))) {
return;
}
scp.decide(invocation, config);
}
#Override
public boolean supports(ConfigAttribute attribute) {
return scp.supports(attribute);
}
};
}
return bean;
}
#Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
return bean;
}
}
Related
I'm going to put a spring cloud gateway in front of some existing web applications which are already using keycloak as their identity provider and I want to authenticate the incoming requests inside the gateway. currently, each web application is already configured with the proper client-id and it redirects to the keycloak with the proper values. now, the gateway must do the authorization-code flow instead of each application, so it has to know in advance which client-is is for which requested url.
so, I was investigating how to implement it and I'm still here without any proper solution.
what is the solution for it? or, is it really a gateway responsibility to do that?
You can have a look to this answer:
How to create Spring Cloud gateway filter to add client-credentials access token?
In order to support different client-ids (secrets, token-uris and so on), you can just define multiple configurations in the spring.security.oauth2.client .registration section and make the clientid dynamic in the Oauth2ClientGatewayFilter class:
String clientId = ...
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientId)
.principal("myPrincipal").build();
Actually, I found a solution however I'm not sure whether it's the best one.
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange().pathMatchers("/actuator/**").permitAll().and()
.authorizeExchange().anyExchange().authenticated().and().csrf().disable().oauth2Login()
.and()
.exceptionHandling().authenticationEntryPoint(createEntryPoints())
.and()
.oauth2ResourceServer().jwt()
.jwtAuthenticationConverter(grantedAuthoritiesExtractor());
return http.build();
}
public ServerAuthenticationEntryPoint createEntryPoints() {
List<DelegateEntry> entryPoints = new ArrayList<>();
entryPoints
.add(new DelegateEntry(ServerWebExchangeMatchers.pathMatchers("/app1"),
new RedirectServerAuthenticationEntryPoint("/oauth2/authorization/client1")));
//other clients will be added here
DelegatingServerAuthenticationEntryPoint defaultEntryPoint = new DelegatingServerAuthenticationEntryPoint(
entryPoints);
defaultEntryPoint.setDefaultEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED));
return defaultEntryPoint;
}
so the client1 will be used for the /app1 and so on.
as i said earlier i'm not sure and there might be better solution for that.
I have a web application composed by frontend code that invokes the same api implemented by two backend modules. This api returns a url in a JSON object. The backend modules are both written with spring mvc but in different versions.
The url-building is the same and it is something like this:
#GetMapping(path = "/app1/menu", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public JsonObject getMenu(HttpServletRequest req) throws IOException {
JsonObject menu = new JsonObject();
menu.addProperty("href", ServletUriComponentsBuilder.fromRequest(req)
.replacePath(req.getContextPath())
.path("/index.html")
.toUriString());
return menu;
}
As you can see this code simply adds a constant to the incoming request and returns it.
The first app uses spring mvc 4 (4.3.5.RELEASE precisely).
The second module uses the 5.1.4.RELEASE version.
When all these apps are deployed on a load balanced server (2 tomcat instance with a load balancer upfront) and https the problem shows up.
Say that the request url is, for app1, something like this:
https://example.com/context/app1/menu
The app1 returns correctly
https://example.com/context/index.html
For the app2 the request issued by the frontend is
https://example.com/context/app2/menu
And the answer is
http://example.com/context/another_index.html
So it looses the https scheme
It seems that the ServletUriComponentsBuilder.fromRequest has changed behaviour?
I have taken a (quick I admit) look at the commits in the git repo but haven't
found anything ....
I have an Azure webrole which is running an API service. I'm trying to enable CORS so that the API can be consumed by browser scripts. There are a quite a few questions that refer to enabling CORS on web-api applications but I haven't found one that gives an answer for webroles.
I've tried adding the magic customheaders block from this answer to my web.config but that doesn't work.
This document from Microsoft implies that the Microsoft.AspNet.Cors nuget package may be used but it's unclear to me how to get hold of the HttpConfiguration from within a webrole OnStart method. It also seems odd that I have to decorate every one of my API methods. Is there not a single 'switch' I can flick to enable CORS for the entire service?
Related questions...
What's the easiest way to verify that CORS is actually enabled? At the moment I'm using a Blazor PostJsonAsync call and relying on that to pass but it's getting pretty tedious repeatedly reconfiguring and uploading the role to Azure to try out changes.
Bigger question...am I fighting against the tide using a webrole? Much of the documentation refers to web-api and web-apps. Maybe these are the future and webroles are deprecated?
I would also recommend moving over to webapps. However, you might also get it to work with web roles and how you apply cors there also works for webapps if you use OWIN.
You might host your API in the web role like this:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/hosting-aspnet-web-api/host-aspnet-web-api-in-an-azure-worker-role
This gives you the HttpConfiguration you need (Startup.cs).
It also seems odd that I have to decorate every one of my API methods. Is there not a single 'switch' I can flick to enable CORS for the entire service?
You can use an ICorsPolicyProvider to enable it everywhere:
// in startup.cs
config.EnableCors(new AllowAllCorsPolicyProvider());
public class AllowAllCorsPolicyProvider : ICorsPolicyProvider
{
readonly CorsPolicy _CorsPolicy;
public AllowAllCorsPolicyProvider()
{
_CorsPolicy = new CorsPolicy {AllowAnyHeader = true, AllowAnyMethod = true, AllowAnyOrigin = true};
}
public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(_CorsPolicy);
}
I have a working robolectric and want to test a component of my application that does HTTP request. Since I don't want these requests to go to my live server but instead to a local test server I want to override a string resources (that contains the servers hostname) during testing.
However, I'm not capable of finding anything in the robolectric documentation that goes remotely in the direction I want :(
I've faced a similar issue in Robolectric 3; you can override a resource at application level using Mockito partial mocks.
First, you tell Robolectric to used a partially mocked Application and to return that when the application context is used: (thanks to this answer: https://stackoverflow.com/a/31386831/327648)
RuntimeEnvironment.application = spy(RuntimeEnvironment.application);
when(RuntimeEnvironment.application.getApplicationContext())
.thenReturn(RuntimeEnvironment.application);
Then you partially mock the Resources object:
Resources spiedResources = spy(app.getResources());
when(app.getResources())
.thenReturn(spiedResources);
Then you can do the real override:
when(spiedResources.getString(R.string.server_address))
.thenReturn("local server address");
I hope this helps.
You can use the technique mentioned at http://robolectric.blogspot.com/2013/04/the-test-lifecycle-in-20.html
This will allow you to override getResources() and use spying to return a hardcoded String or (by default) the String loaded from res/values:
#Override
public Resources getResources() {
Resources resources = spy(super.getResources());
when(resources.getString(R.string.server_address)).thenReturn("local test server address");
return resources;
}
I'm using a Grizzly HttpServer which has two HttpHandler instances registered:
under /api/* there is an Jersey REST - style application offering the API of the product, and
under /* there is an StaticHttpHandler which serves static HTML / JavaScript content (which, among other things, talks to the API under /api/
For authentication I'm currently securing only the API using a Jersey ContainerRequestFilter implementing HTTP Basic Auth, which looks quite similar to what is presented in another SO question.
But as requirements changed, now I'd like to require authentication for all requests hitting the server. So I'd like to move the authentication one level up, from Jersey to Grizzly. Unfortunately, I'm completely lost figuring out where I can hook up a "request filter" (or whatever it is called) in Grizzly. Can someone point me to the relevant API to accomplish this?
The easiest solution would leverage the Grizzly embedded Servlet support.
This of course would mean you'd need to do a little work to migrate your current HttpHandler logic over to Servlets - but that really shouldn't be too difficult as the HttpHandler API is very similar.
I'll give some high level points on doing this.
HttpServer server = HttpServlet.createSimpleServer(<docroot>, <host>, <port>);
// use "" for <context path> if you want the context path to be /
WebappContext ctx = new WebappContext(<logical name>, <context path>);
// do some Jersey initialization here
// Register the Servlets that were converted from HttpHandlers
ServletRegistration s1 = ctx.addServlet(<servlet name>, <Servlet instance or class name>);
s1.addMapping(<url pattern for s1>);
// Repeat for other Servlets ...
// Now for the authentication Filter ...
FilterRegistration reg = ctx.addFilter(<filter name>, <filter instance or class name>);
// Apply this filter to all requests
reg.addMapping(null, "/*");
// do any other additional initialization work ...
// "Deploy" ctx to the server.
ctx.deploy(server);
// start the server and test ...
NOTE: The dynamic registration of Servlets and Filters is based off the Servlet 3.0 API, so if you want information on how to deal with Servlet listeners, init parameters, etc., I would recommend reviewing the Servlet 3.0 javadocs.
NOTE2: The Grizzly Servlet implementation is not 100% compatible with the Servlet specification. It doesn't support standard Servlet annotations, or deployment of traditional Servlet web application archive deployment.
Lastly, there are examples of using the embedded Servlet API here
The "hookup" part can be done using a HttpServerProbe (tested with Grizzly 2.3.5):
srv.getServerConfiguration().getMonitoringConfig().getWebServerConfig()
.addProbes(new HttpServerProbe.Adapter() {
#Override
public void onRequestReceiveEvent(HttpServerFilter filter,
Connection connection, Request request) {
...
}
#Override
public void onRequestCompleteEvent(HttpServerFilter filter,
Connection connection, Response response) {
}
});
For the "linking" to the ContainerRequestFilter you might want to have a look at my question:
UnsupportedOperationException getUserPrincipal