Host and IP Resolution in a Micronaut application with Load Balancer and Elastic Beanstalk
When you run your application behind a Elastic Load Balancer, a Classic Load Balancer or an Application Load Balancer, AWS decorates your request with X-Forward HTTP Headers.
In a Micronaut application, you may need to resolve the domain name of your application, for example when using OAuth 2.0 for a redirect_uri
, or restrict an endpoint to a range of IP Addresses, for example a Stripe's callback.
Micronaut® framework contains two APIs:
HttpHostResolver
to resolve the hostHttpClientAddressResolver
to resolve the IP.
Given the following controller:
src/main/java/example/micronaut/HeadersController.java
package example.micronaut;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.server.util.HttpClientAddressResolver;
import io.micronaut.http.server.util.HttpHostResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
@Controller
public class HeadersController {
private static final Logger LOG = LoggerFactory.getLogger(HeadersController.class);
private final HttpHostResolver httpHostResolver;
private final HttpClientAddressResolver httpClientAddressResolver;
public HeadersController(HttpHostResolver httpHostResolver,
HttpClientAddressResolver httpClientAddressResolver) {
this.httpHostResolver = httpHostResolver;
this.httpClientAddressResolver = httpClientAddressResolver;
}
@Get
public Map<String, Object> index(HttpRequest<?> request) {
for (String name : request.getHeaders().names()) {
if (LOG.isInfoEnabled()) {
LOG.info("H {}: {}", name, request.getHeaders().get(name));
}
}
Map<String, Object> result = new HashMap<>();
result.put("ip", httpClientAddressResolver.resolve(request));
result.put("resolved_host", httpHostResolver.resolve(request));
return result;
}
}
If I run this application locally, I get:
% curl localhost:8080
{"ip":"0:0:0:0:0:0:0:1","resolved_host":"http://localhost:8080"}
with logs:
H Host: localhost:8080
H User-Agent: curl/7.64.1
H Accept: */*
To resolve the Host and IP in Elastic Beanstalk behind an Application Load Balancer add:
src/main/resources/application-ec2.yml
micronaut:
server:
host-resolution:
protocol-header: X-Forwarded-Proto
port-header: X-Forwarded-Port
host-header: Host
client-address-header: X-Real-IP
EC2 environment is automatically detected, because of that I typically use application-ec2.yml
for such configuration.
If you call your application's endpoint running in Elastic Beanstalk you get:
% curl http://micronaut.example
{"ip":"79.150.119.41","resolved_host":"http://micronaut.example"}
with logs:
H Connection: upgrade
H Host: micronaut.example
H X-Real-IP: 172.31.91.156
H X-Forwarded-For: 79.150.119.41, 172.31.91.156
H X-Forwarded-Proto: http
H X-Forwarded-Port: 80
H X-Amzn-Trace-Id: Root=1-60865015-39f63e107ca0f25410b0e66e
H User-Agent: curl/7.64.1
H Accept: */*
Tags: #micronaut #aws #elasticbeanstalk #elasticloadbalancing