diff --git a/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs b/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs index c994cbc8977bf..128727c27821d 100644 --- a/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs +++ b/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs @@ -87,18 +87,22 @@ private static async Task GetAuthenticationStatus(HttpContext co throw new InvalidOperationException(nameof(ClearFailedAuthorizationsTimer)); } - string? ipcPassword = ASF.GlobalConfig != null ? ASF.GlobalConfig.IPCPassword : GlobalConfig.DefaultIPCPassword; - - if (string.IsNullOrEmpty(ipcPassword)) { - return HttpStatusCode.OK; - } - IPAddress? clientIP = context.Connection.RemoteIpAddress; if (clientIP == null) { throw new InvalidOperationException(nameof(clientIP)); } + string? ipcPassword = ASF.GlobalConfig != null ? ASF.GlobalConfig.IPCPassword : GlobalConfig.DefaultIPCPassword; + + if (string.IsNullOrEmpty(ipcPassword)) { + if (IPAddress.IsLoopback(clientIP) || Startup.KnownNetworks.Any(network => network.Contains(clientIP))) { + return HttpStatusCode.OK; + } + + return HttpStatusCode.Forbidden; + } + if (FailedAuthorizations.TryGetValue(clientIP, out byte attempts)) { if (attempts >= MaxFailedAuthorizationAttempts) { return HttpStatusCode.Forbidden; diff --git a/ArchiSteamFarm/IPC/Startup.cs b/ArchiSteamFarm/IPC/Startup.cs index 0f377183cc8e5..1ab43a02d87eb 100644 --- a/ArchiSteamFarm/IPC/Startup.cs +++ b/ArchiSteamFarm/IPC/Startup.cs @@ -29,6 +29,7 @@ #endif using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.Net; using System.Reflection; @@ -53,6 +54,8 @@ namespace ArchiSteamFarm.IPC { internal sealed class Startup { + internal static ImmutableHashSet KnownNetworks { get; private set; } = ImmutableHashSet.Empty; + private readonly IConfiguration Configuration; public Startup(IConfiguration configuration) => Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); @@ -148,12 +151,12 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); #endif + // We want to protect our API with IPCPassword and additional security, this should be called after routing, so the middleware won't have to deal with API endpoints that do not exist + app.UseWhen(context => context.Request.Path.StartsWithSegments("/Api", StringComparison.OrdinalIgnoreCase), appBuilder => appBuilder.UseMiddleware()); + string? ipcPassword = ASF.GlobalConfig != null ? ASF.GlobalConfig.IPCPassword : GlobalConfig.DefaultIPCPassword; if (!string.IsNullOrEmpty(ipcPassword)) { - // We want to protect our API with IPCPassword, this should be called after routing, so the middleware won't have to deal with API endpoints that do not exist - app.UseWhen(context => context.Request.Path.StartsWithSegments("/Api", StringComparison.OrdinalIgnoreCase), appBuilder => appBuilder.UseMiddleware()); - // We want to apply CORS policy in order to allow userscripts and other third-party integrations to communicate with ASF API, this should be called before response compression, but can't be due to how our flow works // We apply CORS policy only with IPCPassword set as an extra authentication measure app.UseCors(); @@ -194,11 +197,10 @@ public void ConfigureServices(IServiceCollection services) { // Prepare knownNetworks that we'll use in a second HashSet? knownNetworksTexts = Configuration.GetSection("Kestrel:KnownNetworks").Get>(); - HashSet? knownNetworks = null; + HashSet knownNetworks = new(); if (knownNetworksTexts?.Count > 0) { - knownNetworks = new HashSet(knownNetworksTexts.Count); - + // Use specified known networks foreach (string knownNetworkText in knownNetworksTexts) { string[] addressParts = knownNetworkText.Split('/', StringSplitOptions.RemoveEmptyEntries); @@ -211,17 +213,22 @@ public void ConfigureServices(IServiceCollection services) { knownNetworks.Add(new IPNetwork(ipAddress, prefixLength)); } + } else { + // Use private address space networks by default, https://datatracker.ietf.org/doc/html/rfc1918#section-3 + knownNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); + knownNetworks.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12)); + knownNetworks.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16)); } + KnownNetworks = knownNetworks.ToImmutableHashSet(); + // Add support for proxies services.Configure( options => { options.ForwardedHeaders = ForwardedHeaders.All; - if (knownNetworks != null) { - foreach (IPNetwork knownNetwork in knownNetworks) { - options.KnownNetworks.Add(knownNetwork); - } + foreach (IPNetwork knownNetwork in KnownNetworks) { + options.KnownNetworks.Add(knownNetwork); } } );