From f87a8fa9f5e084e1b19f88dd2d9e7b8715c5ea45 Mon Sep 17 00:00:00 2001 From: soup Date: Mon, 2 Dec 2024 01:20:08 +0100 Subject: [PATCH] New: Add config file setting for CGNAT authentication bypass (cherry picked from commit 4c41a4f368046f73f82306bbd73bec992392938b) --- .../Config/HostConfigResource.cs | 1 + .../Authentication/UiAuthorizationHandler.cs | 9 ++++++--- .../IPAddressExtensionsFixture.cs | 19 +++++++++++++++++++ .../Extensions/IpAddressExtensions.cs | 6 ++++++ src/NzbDrone.Common/Options/AuthOptions.cs | 1 + .../Configuration/ConfigFileProvider.cs | 3 +++ .../Configuration/ConfigService.cs | 6 ++++++ 7 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Lidarr.Api.V1/Config/HostConfigResource.cs b/src/Lidarr.Api.V1/Config/HostConfigResource.cs index 596b77c73..a7f13c1ca 100644 --- a/src/Lidarr.Api.V1/Config/HostConfigResource.cs +++ b/src/Lidarr.Api.V1/Config/HostConfigResource.cs @@ -45,6 +45,7 @@ public class HostConfigResource : RestResource public string BackupFolder { get; set; } public int BackupInterval { get; set; } public int BackupRetention { get; set; } + public bool TrustCgnatIpAddresses { get; set; } } public static class HostConfigResourceMapper diff --git a/src/Lidarr.Http/Authentication/UiAuthorizationHandler.cs b/src/Lidarr.Http/Authentication/UiAuthorizationHandler.cs index a8d212659..2270a7ade 100644 --- a/src/Lidarr.Http/Authentication/UiAuthorizationHandler.cs +++ b/src/Lidarr.Http/Authentication/UiAuthorizationHandler.cs @@ -27,10 +27,13 @@ protected override Task HandleRequirementAsync(AuthorizationHandlerContext conte if (_authenticationRequired == AuthenticationRequiredType.DisabledForLocalAddresses) { if (context.Resource is HttpContext httpContext && - IPAddress.TryParse(httpContext.GetRemoteIP(), out var ipAddress) && - ipAddress.IsLocalAddress()) + IPAddress.TryParse(httpContext.GetRemoteIP(), out var ipAddress)) { - context.Succeed(requirement); + if (ipAddress.IsLocalAddress() || + (_configService.TrustCgnatIpAddresses && ipAddress.IsCgnatIpAddress())) + { + context.Succeed(requirement); + } } } diff --git a/src/NzbDrone.Common.Test/ExtensionTests/IPAddressExtensionsFixture.cs b/src/NzbDrone.Common.Test/ExtensionTests/IPAddressExtensionsFixture.cs index f7ed71f94..39c71d33d 100644 --- a/src/NzbDrone.Common.Test/ExtensionTests/IPAddressExtensionsFixture.cs +++ b/src/NzbDrone.Common.Test/ExtensionTests/IPAddressExtensionsFixture.cs @@ -21,9 +21,28 @@ public void should_return_true_for_local_ip_address(string ipAddress) [TestCase("1.2.3.4")] [TestCase("172.55.0.1")] [TestCase("192.55.0.1")] + [TestCase("100.64.0.1")] + [TestCase("100.127.255.254")] public void should_return_false_for_public_ip_address(string ipAddress) { IPAddress.Parse(ipAddress).IsLocalAddress().Should().BeFalse(); } + + [TestCase("100.64.0.1")] + [TestCase("100.127.255.254")] + [TestCase("100.100.100.100")] + public void should_return_true_for_cgnat_ip_address(string ipAddress) + { + IPAddress.Parse(ipAddress).IsCgnatIpAddress().Should().BeTrue(); + } + + [TestCase("1.2.3.4")] + [TestCase("192.168.5.1")] + [TestCase("100.63.255.255")] + [TestCase("100.128.0.0")] + public void should_return_false_for_non_cgnat_ip_address(string ipAddress) + { + IPAddress.Parse(ipAddress).IsCgnatIpAddress().Should().BeFalse(); + } } } diff --git a/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs b/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs index b60d4271a..cbc1f5f83 100644 --- a/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs +++ b/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs @@ -52,5 +52,11 @@ private static bool IsLocalIPv4(byte[] ipv4Bytes) return isLinkLocal || isClassA || isClassC || isClassB; } + + public static bool IsCgnatIpAddress(this IPAddress ipAddress) + { + var bytes = ipAddress.GetAddressBytes(); + return bytes.Length == 4 && bytes[0] == 100 && bytes[1] >= 64 && bytes[1] <= 127; + } } } diff --git a/src/NzbDrone.Common/Options/AuthOptions.cs b/src/NzbDrone.Common/Options/AuthOptions.cs index 2b63308d3..64330b68b 100644 --- a/src/NzbDrone.Common/Options/AuthOptions.cs +++ b/src/NzbDrone.Common/Options/AuthOptions.cs @@ -6,4 +6,5 @@ public class AuthOptions public bool? Enabled { get; set; } public string Method { get; set; } public string Required { get; set; } + public bool? TrustCgnatIpAddresses { get; set; } } diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs index 04b4b339e..442a6fcd9 100644 --- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -64,6 +64,7 @@ public interface IConfigFileProvider : IHandleAsync, string PostgresPassword { get; } string PostgresMainDb { get; } string PostgresLogDb { get; } + bool TrustCgnatIpAddresses { get; } } public class ConfigFileProvider : IConfigFileProvider @@ -470,5 +471,7 @@ public void Execute(ResetApiKeyCommand message) { SetValue("ApiKey", GenerateApiKey()); } + + public bool TrustCgnatIpAddresses => _authOptions.TrustCgnatIpAddresses ?? GetValueBoolean("TrustCgnatIpAddresses", false, persist: false); } } diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index 239f000d6..34b94c927 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -426,6 +426,12 @@ public bool CleanupMetadataImages public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty); + public bool TrustCgnatIpAddresses + { + get { return GetValueBoolean("TrustCgnatIpAddresses", false); } + set { SetValue("TrustCgnatIpAddresses", value); } + } + private string GetValue(string key) { return GetValue(key, string.Empty);