Get the client IP address in AKS with .Net Core and NGNIX
- 3 minutes read - 468 wordsThere are many times when you need to get your client IP address: for application telemetry, for getting the client country or any other info you may need. Anyway, when you deploy a .Net Core app on AKS and you are using an NGINX ingress, that you didn’t explicitely configure, the IP address you will be receiving into your app may not be what you expected.
To fix this, there’s the X-Forwarded-For
header, that should contain the
original IP address, but for this to work you need to change two different
things.
Ingress Controller configuration
When deploying the ingress controller with helm
on AKS, you need to set an
specific option; it is well documented but it is not
activated by default:
--set controller.service.externalTrafficPolicy=Local
As in my case I use Flux, so I can do GitOps on my cluster, I need to
define the this configuration setting inside the values
section (Flux does not
admit the abbreviated form):
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: ingress-nginx
namespace: ingress-basic
spec:
chart:
spec:
chart: ingress-nginx
sourceRef:
kind: HelmRepository
name: ingress-nginx
values:
controller:
service:
externalTrafficPolicy: Local
interval: 1m0s
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
Once configured, we can already see our IP in the X-Forwarded-For
header, but
when watching the
Request.HttpContext.Connection.LocalIpAddress
property, it still has the
::ffff:10.240.0.14 value, that, in this particular case, as I’m using Azure CNI,
it comes from the assigned IP of the Pod that is currently running.
Code changes
ASP.Net Core does not use the X-Forwarded-For
header by default. This helps
you avoid security risks, and furthermore, if the application is not behind a
proxy you wouldn’t see the real IP address. In our case, we can configure the
Forwarded Headers middleware to correctly use the header. There’s an important
detail to configure to make it work: you need to
properly configure the KnownProxies
and KnownNetworks
.
Otherwise it will not work. In the official example the list is cleared:
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
if (string.Equals(
Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"),
"true", StringComparison.OrdinalIgnoreCase))
{
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto;
// Only loopback proxies are allowed by default.
// Clear that restriction because forwarders are enabled by explicit
// configuration.
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
}
var app = builder.Build();
app.UseForwardedHeaders();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
But, for the sake of security, I would preferrably put the actual values we have in our K8s cluster. We can pass these values as environment variables, but it is really important to notice in which format they are coming, because sometimes we will get an IPv4 address format (e.g: ‘10.240.0.1’) and in other configurations we may have an IPv6 address format (e.g: ‘::ffff:10.240.0.1’).
After applying these changes you can see how we finally got the actual client address IP:
Credits
- Cover picture by B Klug.