Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get spring security to work behind a load balancer across multiple domains?

We are moving an old java / spring app into AWS, so it is behind an AWS Application Load Balancer. Tomcat is running directly behind the load balancers on port 8080, and we are using HTTP between the load balancer and tomcat.

The problem is under this scenario the spring security module doesn't recognize that the connection is secure.

I can resolve this issue by configuring the Connection:

<Connector port="8080"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           proxyName="single-host.example.com"
           secure="true"
           scheme="https"
           redirectPort="443"
           proxyPort="443" />

Which works for a single host name. However, I need this to work across multiple host names.

I have tried skipping the proxy and adding:

server.tomcat.remote_ip_header=X-Forwarded-For
server.tomcat.protocol_header=X-Forwarded-Proto

But this doesn't seem to make any difference.

Is there a way to support multiple hostnames in this scenario?

like image 883
chris Avatar asked Jul 18 '17 12:07

chris


People also ask

Will Spring Security secures all the applications?

If Spring Security is on the classpath, Spring Boot automatically secures all HTTP endpoints with “basic” authentication. However, you can further customize the security settings. The first thing you need to do is add Spring Security to the classpath.

How do I provide security to my spring boot project?

For adding a Spring Boot Security to your Spring Boot application, we need to add the Spring Boot Starter Security dependency in our build configuration file. Maven users can add the following dependency in the pom. xml file. Gradle users can add the following dependency in the build.

How many ways we can implement Spring Security?

There are basically 2 ways to implement spring security. through bean configuration in . xml files and other by using Annotations.


2 Answers

AWS LoadBalancer sends X-Forwarded-Proto header when proxying request.

On Tomcat configure RemoteIpValve to have request.secure and other request variable interpreted from those headers.

<Valve className="org.apache.catalina.valves.RemoteIpValve"/>

You should also omit setting proxyName on Connector conifiguration since it should come automatically from valve.

like image 53
ikettu Avatar answered Sep 24 '22 13:09

ikettu


I am got some solution procedure. So I have provided 2 suggestion. First one is step by step pictorial view to solve your issue. If not, then go to the second one.

Second one is using X-Forwarded-Proto and related configuration to solve the issue. Hope it will help you.

Suggestion#1:

Amazon cloud environment with load balance support process is pretty straight-forward. A step by step tutorial is given here:Elastic Load Balancing (ELB) with a Java Web Application + Tomcat + Session Stickiness

Suggestion#2:

phillipuniverse has given a solution.

Configuring the following valve in Tomcat will make request.isSecure() function properly with the X-Forwarded-Proto header:

<Valve className="org.apache.catalina.valves.RemoteIpValve" protocolHeader="X-Forwarded-Proto" />

This can be added to Tomcat's server.xml under the <Host> element.


And of course, after all that, there is a very, VERY simple solution that fixes this problem from the very beginning. All that really needed to happen was to modify the proto channel filters from this:

if ("https".equals(invocation.getHttpRequest().getHeader("X-Forwarded-Proto"))) {
    getEntryPoint().commence(invocation.getRequest(), invocation.getResponse());
}

to:

if (invocation.getHttpRequest().isSecure() || 
        "https".equals(invocation.getHttpRequest().getHeader("X-Forwarded-Proto"))) {
    getEntryPoint().commence(invocation.getRequest(), invocation.getResponse());
}

The final configuration here should be this:

<bean class="org.broadleafcommerce.common.security.channel.ProtoChannelBeanPostProcessor">
    <property name="channelProcessorOverrides">
      <list>
        <bean class="org.broadleafcommerce.common.security.channel.ProtoInsecureChannelProcessor" />
        <bean class="org.broadleafcommerce.common.security.channel.ProtoSecureChannelProcessor" />
      </list>
    </property>
</bean>

After that,

Some prefer to terminate SSL at the load balancer, and to not use Apache web server. In that case, you often accept traffic at the LB on 80 / 443, and then route traffic to Tomcat on 8080.

If you are using Spring's port mapping:

<sec:port-mappings>
    <sec:port-mapping http="8080" https="443"/>
</sec:port-mappings>

This will not work as it does not override the port mapping in the new Channel Processors. Here is a configuration that will work, though:

<bean class="org.broadleafcommerce.common.security.channel.ProtoChannelBeanPostProcessor">
    <property name="channelProcessorOverrides">
        <list>
            <bean class="org.broadleafcommerce.common.security.channel.ProtoInsecureChannelProcessor" >
                <property name="entryPoint">
                    <bean class="org.springframework.security.web.access.channel.RetryWithHttpEntryPoint">
                        <property name="portMapper" ref="portMapper"/>
                    </bean>
                </property>
            </bean>
            <bean class="org.broadleafcommerce.common.security.channel.ProtoSecureChannelProcessor" >
                <property name="entryPoint">
                    <bean class="org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint">
                        <property name="portMapper" ref="portMapper"/>
                    </bean>
                </property>
            </bean>
        </list>
    </property>
</bean>

Resource Link: HTTPS/SSL/Spring Security doesn't work in both a load balancer and non-load balancer environment #424

like image 24
SkyWalker Avatar answered Sep 24 '22 13:09

SkyWalker