diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index 5acdc63489f4..d78900a642ff 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -644,6 +644,16 @@ public static string GetApplicationCacheServiceBusSubscriptionName(GlobalSetting { return realConnectingIp.ToString(); } + else if (globalSettings.SelfHosted && httpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var xForwardedFor)) + { + // X-Forwarded-For is a de-facto standard + // RFC7239 normalizes into the Forwarded header (https://datatracker.ietf.org/doc/rfc7239/) + // it may replace the X-Forwarded-For header in the future + // Using a library should be more convenient to take benefit of both format and future evolutions + // + // Security: Considering here that Nginx has sanitized the header and there is no value forged from outside + return xForwardedFor.ToString().Split(",")[0]; + } return httpContext.Connection?.RemoteIpAddress?.ToString(); } diff --git a/test/Core.Test/Context/CurrentContextTests.cs b/test/Core.Test/Context/CurrentContextTests.cs index b868d6ceaab5..95a0ac5e0631 100644 --- a/test/Core.Test/Context/CurrentContextTests.cs +++ b/test/Core.Test/Context/CurrentContextTests.cs @@ -107,6 +107,40 @@ public async Task BuildAsync_HttpContext_SetsDeviceType( Assert.Equal(deviceType, sutProvider.Sut.DeviceType); } + [Theory, BitAutoData] + public async Task BuildAsync_HttpContext_TestXConnectingIP( + SutProvider sutProvider) + { + var httpContext = new DefaultHttpContext(); + var globalSettings = new Core.Settings.GlobalSettings(); + // Arrange + globalSettings.SelfHosted = false; + httpContext.Request.Headers["X-Connecting-IP"] = "10.11.12.13"; + + // Act + await sutProvider.Sut.BuildAsync(httpContext, globalSettings); + + // Assert + Assert.Equal("10.11.12.13", sutProvider.Sut.IpAddress); + } + + [Theory, BitAutoData] + public async Task BuildAsync_HttpContext_TestXForwardedInSelfHosted( + SutProvider sutProvider) + { + var httpContext = new DefaultHttpContext(); + var globalSettings = new Core.Settings.GlobalSettings(); + // Arrange + globalSettings.SelfHosted = true; + httpContext.Request.Headers["X-Forwarded-For"] = "1.2.3.4,5.6.7.8"; + + // Act + await sutProvider.Sut.BuildAsync(httpContext, globalSettings); + + // Assert + Assert.Equal("1.2.3.4", sutProvider.Sut.IpAddress); + } + [Theory, BitAutoData] public async Task BuildAsync_HttpContext_SetsCloudflareFlags( SutProvider sutProvider)