Reputation: 52590
Right now, the load balancers handle https and then pass along that https to my web servers. So dealing with https double for each request. What I want to do is completely offload https so my web servers don't have to deal with it.
How do I configure Spring Security and JSP pages given that the web servers think all requests are http? Obviously I'll have to modify the <intercept-url>
elements of my configuration to have their requires-channel
attribute always be http
or any
. In my JSP pages I'll have to prepend the <c:url value=''/>
links with a ${secureUrl}
and ${nonSecureUrl}
depending whether the resulting page needs to be https or http. Redirects from controllers need to be modified like this as well... Anything else?
Seems like quite a pain to modify all links in JSP pages to include the scheme and host too. Is there a better way to do that?
Upvotes: 6
Views: 6960
Reputation: 859
To complete the great sourcedelica answer, here is the full code :
For Step 1 :
@Component
public class SecureChannelProcessorHack extends SecureChannelProcessor {
@Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException {
for (ConfigAttribute attribute : config) {
if (supports(attribute)) {
if ("http".equals(invocation.getHttpRequest().getHeader("X-Forwarded-Proto"))) {
getEntryPoint().commence(invocation.getRequest(),
invocation.getResponse());
}
}
}
}
}
@Component
public class InsecureChannelProcessorHack extends InsecureChannelProcessor {
@Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException {
for (ConfigAttribute attribute : config) {
if (supports(attribute)) {
if ("https".equals(invocation.getHttpRequest().getHeader("X-Forwarded-Proto"))) {
getEntryPoint().commence(invocation.getRequest(),
invocation.getResponse());
}
}
}
}
}
And step 2 :
@Configuration
public class LoadBalancerHack implements BeanPostProcessor {
@Inject
SecureChannelProcessorHack secureChannelProcessorHack;
@Inject
InsecureChannelProcessorHack insecureChannelProcessorHack;
@Value("${behind.loadbalancer?false}")
boolean behindLoadBalancer;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (behindLoadBalancer && bean instanceof ChannelDecisionManagerImpl) {
System.out.println("********* Post-processing " + beanName);
((ChannelDecisionManagerImpl) bean).setChannelProcessors(newArrayList(
insecureChannelProcessorHack,
secureChannelProcessorHack
));
}
return bean;
}
}
Upvotes: 3
Reputation: 459
Looks like Grails supports this as a part of the security plugin. Checkout the bottom section of http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/17%20Channel%20Security.html where they talk about checking request headers, which the LB will set.
grails.plugins.springsecurity.secureChannel.useHeaderCheckChannelSecurity = true
grails.plugins.springsecurity.secureChannel.secureHeaderName = '...'
grails.plugins.springsecurity.secureChannel.secureHeaderValue = '...'
grails.plugins.springsecurity.secureChannel.insecureHeaderName = '...'
grails.plugins.springsecurity.secureChannel.insecureHeaderValue = '...'
Upvotes: 1
Reputation: 24047
If you terminate SSL at the load balancer then your load balancer should send a header indicating what protocol was originally requested. For example, the F5 adds X-Forwarded-Proto.
From here you can create custom ChannelProcessor
s that look at this header instead of looking at request.isSecure()
. Then you can continue using <intercept-url requires-channel="https">
and relative <c:url>
.
The steps:
Subclass SecureChannelProcessor and InsecureChannelProcessor overriding decide()
. In decide()
check the header sent by your load balancer.
@Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException {
for (ConfigAttribute attribute : config) {
if (supports(attribute)) {
if (invocation.getHttpRequest().
getHeader("X-Forwarded-Proto").equals("http")) {
entryPoint.commence(invocation.getRequest(),
invocation.getResponse());
}
}
}
}
Then set these ChannelProcessors on the ChannelDecisionManagerImpl bean using a BeanPostProcessor
. See this Spring Security FAQ on why/how to use a BeanPostProcessor
for this.
Upvotes: 6