vkuzel.com

Configuration of NGINX reverse proxy in front of a Spring Boot 2.1 application protected by OAuth 2.0

2019-07-09

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.

  1. 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/;
        }
    
  2. 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 a https 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 in ServletUriComponentsBuilder.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.