From 6abe5f3df33729339bddaade221bc973e3a63bf2 Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Mon, 27 Apr 2020 23:58:35 +0200 Subject: [PATCH] Log Real IP on Authentication failure in case of a reverse proxy (cherry picked from commit 686a14cdff63fcffe4fceee1f9a06d0b2dc3e813) --- .../Authentication/AuthenticationService.cs | 54 +++++++++++++++++-- .../Extensions/IpAddressExtensions.cs | 35 +++++++----- .../X509CertificateValidationService.cs | 2 +- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/src/Lidarr.Http/Authentication/AuthenticationService.cs b/src/Lidarr.Http/Authentication/AuthenticationService.cs index 5cdb756d2..d3588f3e8 100644 --- a/src/Lidarr.Http/Authentication/AuthenticationService.cs +++ b/src/Lidarr.Http/Authentication/AuthenticationService.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Net; using System.Security.Claims; using System.Security.Principal; using Lidarr.Http.Extensions; @@ -208,27 +209,70 @@ namespace Lidarr.Http.Authentication public void LogUnauthorized(NancyContext context) { - _authLogger.Info("Auth-Unauthorized ip {0} url '{1}'", context.Request.UserHostAddress, context.Request.Url.ToString()); + _authLogger.Info("Auth-Unauthorized ip {0} url '{1}'", GetRemoteIP(context), context.Request.Url.ToString()); } private void LogInvalidated(NancyContext context) { - _authLogger.Info("Auth-Invalidated ip {0}", context.Request.UserHostAddress); + _authLogger.Info("Auth-Invalidated ip {0}", GetRemoteIP(context)); } private void LogFailure(NancyContext context, string username) { - _authLogger.Warn("Auth-Failure ip {0} username '{1}'", context.Request.UserHostAddress, username); + _authLogger.Warn("Auth-Failure ip {0} username '{1}'", GetRemoteIP(context), username); } private void LogSuccess(NancyContext context, string username) { - _authLogger.Info("Auth-Success ip {0} username '{1}'", context.Request.UserHostAddress, username); + _authLogger.Info("Auth-Success ip {0} username '{1}'", GetRemoteIP(context), username); } private void LogLogout(NancyContext context, string username) { - _authLogger.Info("Auth-Logout ip {0} username '{1}'", context.Request.UserHostAddress, username); + _authLogger.Info("Auth-Logout ip {0} username '{1}'", GetRemoteIP(context), username); + } + + private string GetRemoteIP(NancyContext context) + { + if (context == null || context.Request == null) + { + return "Unknown"; + } + + var remoteAddress = context.Request.UserHostAddress; + IPAddress remoteIP; + + // Only check if forwarded by a local network reverse proxy + if (IPAddress.TryParse(remoteAddress, out remoteIP) && remoteIP.IsLocalAddress()) + { + var realIPHeader = context.Request.Headers["X-Real-IP"]; + if (realIPHeader.Any()) + { + return realIPHeader.First().ToString(); + } + + var forwardedForHeader = context.Request.Headers["X-Forwarded-For"]; + if (forwardedForHeader.Any()) + { + // Get the first address that was forwarded by a local IP to prevent remote clients faking another proxy + foreach (var forwardedForAddress in forwardedForHeader.SelectMany(v => v.Split(',')).Select(v => v.Trim()).Reverse()) + { + if (!IPAddress.TryParse(forwardedForAddress, out remoteIP)) + { + return remoteAddress; + } + + if (!remoteIP.IsLocalAddress()) + { + return forwardedForAddress; + } + + remoteAddress = forwardedForAddress; + } + } + } + + return remoteAddress; } } } diff --git a/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs b/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs index ce4f3bb0b..2b154ca52 100644 --- a/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs +++ b/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Net.Sockets; namespace NzbDrone.Common.Extensions { @@ -6,24 +7,34 @@ namespace NzbDrone.Common.Extensions { public static bool IsLocalAddress(this IPAddress ipAddress) { - if (ipAddress.ToString() == "::1") + if (ipAddress.IsIPv6LinkLocal) { return true; } - byte[] bytes = ipAddress.GetAddressBytes(); - switch (bytes[0]) + if (IPAddress.IsLoopback(ipAddress)) { - case 10: - case 127: - return true; - case 172: - return bytes[1] < 32 && bytes[1] >= 16; - case 192: - return bytes[1] == 168; - default: - return false; + return true; } + + if (ipAddress.AddressFamily == AddressFamily.InterNetwork) + { + byte[] bytes = ipAddress.GetAddressBytes(); + switch (bytes[0]) + { + case 10: + case 127: + return true; + case 172: + return bytes[1] < 32 && bytes[1] >= 16; + case 192: + return bytes[1] == 168; + default: + return false; + } + } + + return false; } } } diff --git a/src/NzbDrone.Core/Security/X509CertificateValidationService.cs b/src/NzbDrone.Core/Security/X509CertificateValidationService.cs index 7b6afca75..c3e2f27c8 100644 --- a/src/NzbDrone.Core/Security/X509CertificateValidationService.cs +++ b/src/NzbDrone.Core/Security/X509CertificateValidationService.cs @@ -50,7 +50,7 @@ namespace NzbDrone.Core.Security } if (certificateValidation == CertificateValidationType.DisabledForLocalAddresses && - ipAddresses.All(i => i.IsIPv6LinkLocal || i.IsLocalAddress())) + ipAddresses.All(i => i.IsLocalAddress())) { return true; }