Prerequisities: Spring Boot 2.1 application protected by OAuth 2.0 running on default Tomcat web server hidden behing NGINX reverse proxy.
In this scenario NGINX reverse proxy has to be configured properly, so the Spring Boot application can generate correct abosule URLs and redirect an user to right endpoints during authorization. To accomplish that, two configuration files will be mofigied.
-
NGINX configuration file, where something similar to the following snipped should be added to a server block.
location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; proxy_set_header Host $host; proxy_pass http://localhost:8080/; }
-
Spring application's properties file, where at least one of the following two lines should be placed.
# Note: Spring Boot 2.2 replaces following directive with server.forward-headers-strategy=native server.use-forward-headers = true # Optional: in case of your proxy running in a network different from Spring Boot application's network. # server.tomcat.internal-proxies=90\.91\.92\.93 # Bonus: let's not force user to re-authorize too often by setting a session cookie validity to one day. server.servlet.session.timeout=1d server.servlet.session.cookie.max-age=1d
Explanation
In a following scenario (user) -> (reverse proxy) -> (Spring application), the reverse proxy consumes a request sent by user and then transforms it to a new request and passes that request to Spring application. For Spring application this makes an incoming request to look like it was sent by local reverse proxy not by remote user. This also makes Spring application to "think" that request arrived to server's internall address (usually localhost) and not to a public address of reverse proxy.
To overcome these problems X-Forwarded
HTTP headers has been created. To make application work correctly, we have to configure three of those headers plus one extra header.
NGINX configuration
X-Forwarded-For
forwards request's origin IP address (address of user's computer). It is useful in case of logging origin of a request.X-Forwarded-Proto
passes protocol (http, https, ...) of public address. For example for site https://vkuzel.com ahttps
value is passed to Spring application. Wihtout it, Spring application would not be able to generate correct absolute URLs.X-Forwarded-Port
passes port number (80, 443, ...) of public address. Even though this configuration is not mentioned in Spring Boot's documentation it is needed by some utilities. For example inServletUriComponentsBuilder.initFromRequest()
method port number is used to determine whether application is running securely.Host
re-sets a host header to a public domain name like vkuzel.com from a local domain name (usually localhost). Again, it is useful for generating correct abosule URLs.
Spring Boot configuration
By default Spring Boot application does not accept forwarding headers. This has to be explicitly enabled by its configuration.
server.use-forward-headers = true
sets Spring Boot application to accept forwarding headers.server.tomcat.internal-proxies=90\\.91\\.92\\.93
for security reasons application accepts forwarding headers only from proxies located in a local network. If your proxy sits in a different network you will have to set IP address mask to match proxy's address.
It is worth mentioning that forward header names can be configured any arbitrary value by setting one or more of properties server.tomcat.*-header
.
Bonus, increasing validity time of a session cookie
If you don't want your users to re-authorize too often by OAuth services, which can be problematic in some cases, you can set longer validity of session cookie. Two properties has to be set. server.servlet.session.timeout
for setting validity of a session and server.servlet.session.cookie.max-age
for setting a validity of a session cookie. Both properties accepts values with duration units. For example 1d
for one day.
On the Internet there are multiple guidelines mentioning spring.session.timeout
property which is obsolete as of Spring Boot 2.x.
Troubleshooting
-
Enable logging of incommig requests by
logging.level.org.apache.coyote.http11.Http11InputBuffer=DEBUG
. This allows you to observe HTTP headers sent by NGINX.X-Forwarded
headers should be visible in logged requests. -
Enable logging of translation from forwarded headers to standard HTTP headers by
logging.level.org.apache.catalina.valves.RemoteIpValve=DEBUG
. -
An option is to observe requests at the entry point of Spring Boot application - DispatcherSrevlet object, by setting
logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG
. -
If you are able to attach debugger to the application you can place a breakpoint into
DispatcherServlet.doDispatch()
method. -
For observing cookies
logging.level.org.apache.tomcat.util.http.Rfc6265CookieProcessor=DEBUG
can be set.