Spring Authorization Server Redirects to HTTP when behind proxy - spring-boot

I have a standalone spring authorization server at https://auth.foo.com with certificate configured in the spring boot application directly (no Proxy).
I have a client spring boot app using http 8080 behind a K8s Nginx Proxy. The certificate terminates on the proxy and then delivers http upstream to the spring boot app. https://client.foo.com
I have enabled the following in the client application.yaml
server:
port: 8080
forward-headers-strategy: native
tomcat:
use-relative-redirects: true
remoteip:
remote-ip-header: x-forwarded-for
protocol-header: x-forwarded-proto
When navigating to the https://client.foo.com app, the relative redirects work fine, all links redirect to https.
However on successful login via https://auth.foo.com, the auth server redirects to https://client.foo.com/login/oauth2/code/foo-client-oidc?code=XXX then the client responds with a 302 but the Location header is insecure http://client.foo.com
Is there a way to force the redirect to use https in Spring Authorisation Server
Thanks in advance

Related

Spring cloud gateway does not forward HTTPS correctly

I've recently migrated a microservices backend to Spring boot v2.6.1 + spring cloud v2021.0.0 (old version was v2.2.1.RELEASE).
Previously, the setup included a discovery server (Eureka), a gateway (Zuul) and various microservices accessible externally via the gateway. All of these were TLS/secure port enabled so all requests (discovery, registration, gateway forwarding etc) required SSL trust store configuration.
Since Zuul is no longer supported in this version, I am switching to Spring cloud gateway, with the following application.properties setup:
server.port = 8080
eureka.instance.securePortEnabled=false
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/
spring.application.name= gateway-service
spring.cloud.gateway.routes[0].id=first-service
spring.cloud.gateway.routes[0].uri=lb://FIRST-SERVICE
spring.cloud.gateway.routes[0].predicates[0].name=Path
spring.cloud.gateway.routes[0].predicates[0].args.pattern=/firstService/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
security.require-ssl=true
server.ssl.key-store=keystore.p12
server.ssl.key-store-password=password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=localhost
spring.cloud.gateway.httpclient.ssl.trustedX509Certificates[0]=cert.pem
Without ssl universally disabled, I can access a first service URL via that gateway without issue. With SSL enabled, I can access the first service directly and again no issue:
https://localhost:8100/test
{ "message": "Hello World!" }
However, the gateway is not correctly forwarding the requests either via the correct protocol or with the correct client certificate. I am getting the below error response instead of "Hello World" message:
https://localhost:8080/firstService/test
Bad Request This combination of host and port requires TLS.
(It's worth noting that the Eureka secure port is disabled, and registration/registry fetch is done via HTTP due to issues logged at Error setting custom trust store for Eureka Discovery client by overriding DiscoveryClient.DiscoveryClientOptionalArgs
Ok, this comes down to how the microservices register with Eureka.
Although FIRST-SERVICE was also setup with security.require-ssl=true for direct access it was also necessary to register with Eureka with SSL enabled.
Adding the settings below to application.properties fixed the issue without any changes needed for the gateway configuration:
eureka.client.tls.enabled=true
eureka.client.tls.key-store=file:<path-to-key-store>
eureka.client.tls.key-store-password=<password>
eureka.client.tls.keyStoreType=PKCS12
eureka.client.tls.keyPassword=<password>
eureka.client.tls.trust-store=file:<path-to-trust-store>
eureka.client.tls.trust-store-password=<password>

Spring boot Disable response when https port is hit with http request

I have a spring boot application in which tomcat is listening on port 8000 over HTTPS. However, when port 8000 is hit with plain http request, the server responds with http 400.
Bad Request
This combination of host and port requires TLS.
Is there a way to prevent this? I don't want server to send any response when http://localhost:8000 is requested. Spring boot and tomcat versions are as below:
sprint-boot v2.0.0.RELEASE
tomcat v8.5.28
Update: application.properties is as below
server.port=8000
server.ssl.enabled=true
server.ssl.key-alias=alias
server.ssl.key-store=classpath:key.jks
server.ssl.key-store-type=JKS
server.ssl.key-store-password=<password>
server.ssl.key-password=<password>
You enabled SSL Just change server.ssl.enabled=true toserver.ssl.enabled=false

Spring Boot & ELB - How do I make the load balancer redirect http to https?

I have deployed a Spring Boot application via Elastic Beanstalk. I'm using a load balancer, so this is the flow (as far as I understand):
Internet/Browser request ---HTTPS---> Load Balancer ---HTTP---> Spring Boot App Server
So essentially, the SSL terminates at the load balancer and the app server just deals with plain old HTTP.
But in the case of a HTTP request from the browser, I would like the load balancer to automatically redirect to HTTPS.
There are several questions about this issue:
Spring Boot with Embedded Tomcat behind AWS ELB - HTTPS redirect
How to redirect automatically to https with Spring Boot
Spring Boot redirect HTTP to HTTPS
But none of the answers to these questions make sense to me. Perhaps I'm misunderstanding, but all the answers basically make the Spring Boot app only server HTTPS request (for example when using http.requiresChannel().anyRequest().requiresSecure()).
However, this goes against the flow because I'm perfectly fine with the SSL terminating at the load balancer and the Spring Boot app server just dealing with HTTP. So if I require SSL at the spring boot level, then I'll need to do an end-to-end SSL connection, which isn't really required for my application.
I have also used the following properties, which don't seem to help either:
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
With the help of this article, I was finally able to figure out how to do this for a Spring Boot app in an ELB environment.
I had to create a conf file in src/main/webapp/.ebextensions/nginx/conf.d. I just called it myconf.conf.
In myconf.conf, I put this code in:
server {
listen 80;
server_name www.my-site.com;
if ($http_x_forwarded_proto != "https") {
rewrite ^(.*)$ https://$server_name$REQUEST_URI permanent;
}
}
Also, make sure that both HTTP and HTTPS listeners are open for the load balancer.
Additionally, my spring boot app only opens up HTTP since the load balancer already terminates SSL.
AWS Load balancer cannot handle redirection. You may do it via your server or by using cloudfront distributions.

Spring Boot forcing https cause redirection too many times in Google App Engine

I enabled https for all the requests within the App Engine - Flexible Environment.
override fun configure(http: HttpSecurity) {
if (securityProperties.isRequireSsl) http.requiresChannel().anyRequest().requiresSecure()
}
But it is causing too many redirects and fails.
How to force https with Spring Boot on App Engine?
The Google Cloud Load Balancer terminates all https connections, and
then forwards traffic to App Engine instances over http. Read More
Which was causing the redirects as the Spring Application was unaware of this switch.
App Engine proxies the request to the application by adding the X-Forwarded-Proto and X-Forwarded-For headers with which spring identifies the actual request protocol and where the request actually originated from.
To enable such behavior, I added the following configuration in application.yml:
server:
tomcat:
remote_ip_header: X-Forwarded-For
protocol_header: X-Forwarded-Proto

Spring Boot with embedded Tomcat behind Apache proxy

We have a Spring Boot (Spring MVC) app with embedded Tomcat on a dedicated appserver behind an Apache SSL proxy.
The SSL port on the proxy server is 4433, forwarding to port 8080 on the appserver.
So the URL to the proxy server is forwarding like:
https://proxyserver:4433/appname >>forward>> http://appserver:8080/
When running WITHOUT proxy, the first thing that happens is that
Spring Security redirects the request, like:
http://appserver:8080/ >>redirect>> http://appserver:8080/login
to display the login form, by extending WebSecurityConfigurerAdapter with
...
httpSecurity.formLogin().loginPage("/login") ...
...
It works fine without the proxy, but WITH proxy the redirect needs to be changed,
so Spring should instead redirect to the corresponding proxy URL, like:
http://appserver:8080/ >>redirect>> https://proxyserver:4433/appname/login
but no success yet.
I am trying to apply this solution:
59.8 Use Tomcat behind a front-end proxy server
We have configured mod_proxy in Apache, and verified that it sends the expected headers:
X-Forwarded-For: xxx.xxx.xxx.xxx
X-Forwarded-Host: proxyserver
X-Forwarded-Port: 4433
X-Forwarded-Proto: https
The application is started with parameters:
export ARG1='-Dserver.tomcat.protocol-header=x-forwarded-proto'
export ARG2='-Dserver.tomcat.remote-ip-header=x-forwarded-for'
java $ARG1 $ARG2 -jar webapp.jar
Still the redirect does not work.
It will keep redirecting locally, to http://appserver:8080/login which is not available to the clients.
Is there anything else we need to do to make this scenario work?
UPDATE
Also, I am concerned about the "/appname" part in the proxy URL. On the appserver the application is rooted at "/". How should Spring be instructed that "/appname" should be included in all URLs sent back to the clients, when going thru the proxy?
I had the same problem the other day. After some debugging of Spring Boot 1.3 I found the following solution.
1. You have to setup the headers on your Apache proxy:
<VirtualHost *:443>
ServerName www.myapp.org
ProxyPass / http://127.0.0.1:8080/
RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Port 443
ProxyPreserveHost On
... (SSL directives omitted for readability)
</VirtualHost>
2. You have to tell your Spring Boot app to use these headers. So put the following line in your application.properties (or any other place where Spring Boots understands properties):
server.use-forward-headers=true
If you do these two things correctly, every redirect your application sends will not go to http://127.0.0.1:8080/[path] but automatically to https://www.myapp.com/[path]
Update 1. The documentation about this topic is here. You should read it at least to be aware of the property server.tomcat.internal-proxies which defines the range of IP-addresses for proxy servers that can be trusted.
Update 2021 The documentation is moved to here. The Spring Boot configuration is a litte different now.
Your proxy looks fine, and so does the backend app, up to a point, but it doesn't seem to be seeing the RemoteIpValve modified request. The default behaviour of the RemoteIpValve includes a pattern match for the proxy IP address (as a security check) and it only modifies requests that it thinks are from a valid proxy. The pattern defaults in Spring Boot to a well-known set of internal IP addresses like 10.*.*.* and 192.168.*.*, so if your proxy isn't on one of those you need to explicitly configure it, e.g.
server.tomcat.internal-proxies=172\\.17\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}
(using properties file format, which means you have to double escape the backslashes).
You can see the what is happening in the RemoteIpValve if you set
logging.level.org.apache.catalina.valves.RemoteIpValve=DEBUG
or set a breakpoint in it.
A typical solution to this problem is to let the proxy handle any required rewrite. For example, in Apache you can use the rewrite_module and/or headers_module to correct headers. As another example, Nginx handles this and other similar cases automatically for you after configuring upstream servers.
In response to comments:
What are the remote_ip_header and protocol_header spring boot configuration values?
Let's forget Spring Boot for a moment. Tomcat, the embedded servlet container, features a valve known as the RemoteIpValve. This valve is a port of the Apache remotip_module. The primary purpose of this valve is to treat the "useragent which initiated the request as the originating useragent" for "the purposes of authorization and logging". In order for this valve to be used it needs to be configured.
Please find more information about this valve here.
Spring Boot conveniently supports configuring this valve via application.properties through the server.tomcat.remote_ip_header and server.tomcat.protocol_header properties.
I had exactly the same case using haproxy as load balancer with the below configuration, which worled for me. The only thing is the client IP is in request.getRemoteAddr() and not in "X-Forwarded-For" header
frontend www
bind *:80
bind *:443 ssl crt crt_path
redirect scheme https if !{ ssl_fc }
mode http
default_backend servers
backend servers
mode http
balance roundrobin
option forwardfor
server S1 host1:port1 check
server S2 host2:port2 check
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
In application.properties:
server.use-forward-headers=true
There are several properties that you can configure, related to this.
application.yaml example:
server:
forward-headers-strategy: native
tomcat:
use-relative-redirects: true
protocol-header: x-forwarded-proto
remote-ip-header: x-forwarded-for
Setting server.forward-headers-strategy: native is the replacement of the deprecated server.use-forward-headers:true
Have you tried setting
server.context-path=/appname
In Spring Boot?
Try setting the Rewrite rule like:
https://proxyserver:4433/appname >>forward>> http://appserver:8080/appname
And then set your application context to "appname"
server.context-path=/appname
So locally you can run by http://appserver:8080/appname and via Reverse Proxy you access via https://proxyserver:4433/appname
Since I am using JBOSS, changes in standalone.xm of jboss:
<http-listener name="default" socket-binding="http" redirect-socket="https" proxy-address-forwarding="true" enable-http2="true"/>
Tomcat would have similar config, to inform Tomcat (proxy-address-forwarding="true") to respect the proxy forwarding address.
server.use-forward-headers=true did not work for me, experienced a weird issue where X-Forwarded-For header is not populated to HttpServletRequest consistently.
Ended up using ForwardedHeaderFilter: https://stackoverflow.com/a/51500554/986942.
On top of that, make sure the load balancer (proxy) provide the following headers properly:
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;

Resources