Swagger, Spring Boot 2 With a Reverse-Proxy in Docker

Brett Fisher
The Startup
Published in
4 min readOct 22, 2020

This article demonstrates how you can set up Spring Boot 2 with Swagger behind a reverse-proxy locally using NGINX and Docker.

In our use case, we needed to test X-Forwarded-* headers with a Spring Boot service to emulate how an authorization server would deal with forwarding requests.

In this example, the reverse proxy will have a route published on the public side (defaulting to port 80 for http traffic, 433 for https). The reverse-proxy links the public URL (i.e. https://example.com) to a service on the internal DNS, and forwards the request — setting the required X-Forwarded-* headers in the process.

This is a standard authentication/authorization pattern that can be used to block specific connections, authenticate, validate or change headers, etc.

Private network with reverse-proxy and service. A device connected through internet to the private network.

To replicate this locally, we used NGINX as a reverse-proxy server. We did not want to pollute this example with SSL/HTTPS configuration, so we have just used http://localhost on port 80.

Docker network with NGINX and service. A device connected through localhost to the docker network.

We can also extend the use of NGINX to map multiple backend services to the reverse-proxy, by adding locations to the services of the NGINX section.

Click here for a complete working example of Spring Boot 2, Swagger with a reverse-proxy using Docker.

Configuring Swagger and Spring Boot

For this example we use springdoc-openapi, which is a fully-compliant OpenApi 3.0 implementation for Spring Boot.

The configuration for Swagger was left to Spring Boot defaults except the path, defined in the application configuration:

application.yaml for configuring Spring Boot forward headers strategy

Swagger relies on internal routing to make requests from the clients perspective. Putting the service behind a reverse-proxy without providing the X-Forwarded headers will result in the user not being able to use the documentation as intended. It will also expose internal configuration to the users regarding the host and ports used for the service.

The default in most cases is for X-Forwarded-* headers to be disabled. This means that we would need to enable these for Spring Boot.

Since Spring Boot Release 2.2, you can set the forward-headers-strategy to framework in the application config, which allows headers with the prefix X-Forwarded-* through into the running service.

Setting the strategy to native tells Spring Boot to inherit the X-Forwarded settings from the container/runtime environment.

If you are using a version of Spring Boot before Release 2.2, you will need to create a ForwardHeadersStrategy to achieve the same outcome.

Note on Security

The services that you allow X-Forwarded-* headers on should trust the source of these requests.

The reason that in most cases it is set to not allow these headers by default, is because of security concerns with cache poisoning and spoofing.

Certain HTTP headers (e.g., X-Forwarded-Host) are sent by the reverse proxy or CDN to the web server and are many times presumed to be generated/modified by the CDN and therefore trusted

Good practise would be: X-Forwarded-Host and X-Forwarded-Proto to be cleared and reset by the reverse-proxy and not just allowing them through. If these headers are required, then you should put appropriate sanitation in place.

Getting the Service Running in Docker

The DockerFile used for the Spring Boot 2 application:

Dockerfile for the Swagger Service

Example of the docker-compose.yaml file that was used for NGINX and the container built from the above DockerFile.

docker-compose.yml with NGINX and the Swagger Service Running

For the purpose of this example, we only expose port 80 for NGINX. This is insecure and in production systems it is expected that you have the correct security using port 443 in place.

Docker has it’s own internal DNS, and in this case we use the depends-on and links to the swagger-service. Because of this, we do not need to expose any of the ports to localhost, ensuring the only way of accessing the service is through the proxy.

Configuring the Reverse-Proxy

We have shown how we can get the services running in docker. Currently, it will not work because we have not told it where to route the requests. We need to provide NGINX with the required configuration.

docker/nginx/nginx.conf for the Reverse Proxy configuration in NGINX

In the example nginx.conf file, we are routing requests on the root path to the swagger-service. We have setup the reverse proxy as http only, listening on port 80 (IPv4 and IPv6).

The object location refers to the path of the host server (NGINX). The proxy_pass refers to the location of where to forward those requests, and the proxy_set_header allows us to intercept and replace/add the X-Forwarded-* headers.

Pattern matching on the inputs, instead of explicitly replacing then, would be sufficient to protect against most of the vulnerabilities specified before.

Note that events {} was required in config for NGINX to boot correctly.

Running Everything Together

Click on this link to see a complete working example

To build the service that will host the swagger documentation, we use Gradle:

./gradlew clean build -x test

We want to build the docker image that will be used before we can run it:

docker-compose build

Then we want to launch the swagger service and the NGINX reverse-proxy:

docker-compose up

Summary

This is a worked example of getting an NGINX reverse-proxy working with Swagger and Spring Boot 2.

This was originally put together to reproduce a production issue that we had found locally for understanding and debugging purposes.

You require changes to Spring Boot configuration to get everything working properly, and your reverse-proxy (or identity server implementation) also needs to allow the X-Forwarded headers through, whilst still being aware of the potential introduction of security vulnerabilities.

The code examples within this article are available on GitHub

--

--