Przeglądaj źródła

Merge pull request #4116 from cvium/add_known_proxies

Add Known Proxies to system configuration
Bond-009 4 lat temu
rodzic
commit
4447589460

+ 1 - 1
Emby.Server.Implementations/HttpServer/Security/SessionContext.cs

@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
             var authorization = _authContext.GetAuthorizationInfo(requestContext);
 
             var user = authorization.User;
-            return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.Request.RemoteIp(), user);
+            return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp(), user);
         }
 
         public SessionInfo GetSession(object requestContext)

+ 2 - 1
Jellyfin.Api/Auth/BaseAuthorizationHandler.cs

@@ -1,6 +1,7 @@
 using System.Security.Claims;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Library;
 using Microsoft.AspNetCore.Authorization;
@@ -69,7 +70,7 @@ namespace Jellyfin.Api.Auth
                 return false;
             }
 
-            var ip = RequestHelpers.NormalizeIp(_httpContextAccessor.HttpContext.Connection.RemoteIpAddress).ToString();
+            var ip = _httpContextAccessor.HttpContext.GetNormalizedRemoteIp();
             var isInLocalNetwork = _networkManager.IsInLocalNetwork(ip);
             // User cannot access remotely and user is remote
             if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !isInLocalNetwork)

+ 2 - 1
Jellyfin.Api/Controllers/MediaInfoController.cs

@@ -9,6 +9,7 @@ using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Models.MediaInfoDtos;
 using Jellyfin.Api.Models.VideoDtos;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
@@ -165,7 +166,7 @@ namespace Jellyfin.Api.Controllers
                         enableTranscoding,
                         allowVideoStreamCopy,
                         allowAudioStreamCopy,
-                        Request.HttpContext.Connection.RemoteIpAddress.ToString());
+                        Request.HttpContext.GetNormalizedRemoteIp());
                 }
 
                 _mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate);

+ 3 - 2
Jellyfin.Api/Controllers/SystemController.cs

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Constants;
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
@@ -178,8 +179,8 @@ namespace Jellyfin.Api.Controllers
         {
             return new EndPointInfo
             {
-                IsLocal = Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress),
-                IsInNetwork = _network.IsInLocalNetwork(Request.HttpContext.Connection.RemoteIpAddress.ToString())
+                IsLocal = HttpContext.IsLocal(),
+                IsInNetwork = _network.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp())
             };
         }
 

+ 2 - 1
Jellyfin.Api/Controllers/UniversalAudioController.cs

@@ -8,6 +8,7 @@ using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Models.StreamingDtos;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
@@ -160,7 +161,7 @@ namespace Jellyfin.Api.Controllers
                         true,
                         true,
                         true,
-                        Request.HttpContext.Connection.RemoteIpAddress.ToString());
+                        Request.HttpContext.GetNormalizedRemoteIp());
                 }
 
                 _mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate);

+ 11 - 10
Jellyfin.Api/Controllers/UserController.cs

@@ -7,6 +7,7 @@ using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Models.UserDtos;
 using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Configuration;
@@ -117,7 +118,7 @@ namespace Jellyfin.Api.Controllers
                 return NotFound("User not found");
             }
 
-            var result = _userManager.GetUserDto(user, HttpContext.Connection.RemoteIpAddress.ToString());
+            var result = _userManager.GetUserDto(user, HttpContext.GetNormalizedRemoteIp());
             return result;
         }
 
@@ -203,7 +204,7 @@ namespace Jellyfin.Api.Controllers
                     DeviceName = auth.Device,
                     Password = request.Pw,
                     PasswordSha1 = request.Password,
-                    RemoteEndPoint = HttpContext.Connection.RemoteIpAddress.ToString(),
+                    RemoteEndPoint = HttpContext.GetNormalizedRemoteIp(),
                     Username = request.Username
                 }).ConfigureAwait(false);
 
@@ -212,7 +213,7 @@ namespace Jellyfin.Api.Controllers
             catch (SecurityException e)
             {
                 // rethrow adding IP address to message
-                throw new SecurityException($"[{HttpContext.Connection.RemoteIpAddress}] {e.Message}", e);
+                throw new SecurityException($"[{HttpContext.GetNormalizedRemoteIp()}] {e.Message}", e);
             }
         }
 
@@ -246,7 +247,7 @@ namespace Jellyfin.Api.Controllers
             catch (SecurityException e)
             {
                 // rethrow adding IP address to message
-                throw new SecurityException($"[{HttpContext.Connection.RemoteIpAddress}] {e.Message}", e);
+                throw new SecurityException($"[{HttpContext.GetNormalizedRemoteIp()}] {e.Message}", e);
             }
         }
 
@@ -290,7 +291,7 @@ namespace Jellyfin.Api.Controllers
                     user.Username,
                     request.CurrentPw,
                     request.CurrentPw,
-                    HttpContext.Connection.RemoteIpAddress.ToString(),
+                    HttpContext.GetNormalizedRemoteIp(),
                     false).ConfigureAwait(false);
 
                 if (success == null)
@@ -496,7 +497,7 @@ namespace Jellyfin.Api.Controllers
                 await _userManager.ChangePassword(newUser, request.Password).ConfigureAwait(false);
             }
 
-            var result = _userManager.GetUserDto(newUser, HttpContext.Connection.RemoteIpAddress.ToString());
+            var result = _userManager.GetUserDto(newUser, HttpContext.GetNormalizedRemoteIp());
 
             return result;
         }
@@ -511,8 +512,8 @@ namespace Jellyfin.Api.Controllers
         [ProducesResponseType(StatusCodes.Status200OK)]
         public async Task<ActionResult<ForgotPasswordResult>> ForgotPassword([FromBody] string? enteredUsername)
         {
-            var isLocal = HttpContext.Connection.RemoteIpAddress.Equals(HttpContext.Connection.LocalIpAddress)
-                          || _networkManager.IsInLocalNetwork(HttpContext.Connection.RemoteIpAddress.ToString());
+            var isLocal = HttpContext.IsLocal()
+                          || _networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp());
 
             var result = await _userManager.StartForgotPasswordProcess(enteredUsername, isLocal).ConfigureAwait(false);
 
@@ -559,7 +560,7 @@ namespace Jellyfin.Api.Controllers
 
             if (filterByNetwork)
             {
-                if (!_networkManager.IsInLocalNetwork(HttpContext.Connection.RemoteIpAddress.ToString()))
+                if (!_networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp()))
                 {
                     users = users.Where(i => i.HasPermission(PermissionKind.EnableRemoteAccess));
                 }
@@ -567,7 +568,7 @@ namespace Jellyfin.Api.Controllers
 
             var result = users
                 .OrderBy(u => u.Username)
-                .Select(i => _userManager.GetUserDto(i, HttpContext.Connection.RemoteIpAddress.ToString()));
+                .Select(i => _userManager.GetUserDto(i, HttpContext.GetNormalizedRemoteIp()));
 
             return result;
         }

+ 5 - 5
Jellyfin.Api/Helpers/DynamicHlsHelper.cs

@@ -8,6 +8,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Models.StreamingDtos;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
@@ -198,12 +199,12 @@ namespace Jellyfin.Api.Helpers
 
             if (!string.IsNullOrWhiteSpace(subtitleGroup))
             {
-                AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.Request.HttpContext.User);
+                AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.User);
             }
 
             AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup);
 
-            if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming, _httpContextAccessor.HttpContext.Request.HttpContext.Connection.RemoteIpAddress))
+            if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming, _httpContextAccessor.HttpContext.GetNormalizedRemoteIp()))
             {
                 var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0;
 
@@ -334,11 +335,10 @@ namespace Jellyfin.Api.Helpers
             }
         }
 
-        private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, IPAddress ipAddress)
+        private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, string ipAddress)
         {
             // Within the local network this will likely do more harm than good.
-            var ip = RequestHelpers.NormalizeIp(ipAddress).ToString();
-            if (_networkManager.IsInLocalNetwork(ip))
+            if (_networkManager.IsInLocalNetwork(ipAddress))
             {
                 return false;
             }

+ 2 - 1
Jellyfin.Api/Helpers/MediaInfoHelper.cs

@@ -6,6 +6,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
@@ -498,7 +499,7 @@ namespace Jellyfin.Api.Helpers
                     true,
                     true,
                     true,
-                    httpRequest.HttpContext.Connection.RemoteIpAddress.ToString());
+                    httpRequest.HttpContext.GetNormalizedRemoteIp());
             }
             else
             {

+ 2 - 6
Jellyfin.Api/Helpers/RequestHelpers.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Net;
 using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Querying;
@@ -119,7 +120,7 @@ namespace Jellyfin.Api.Helpers
                 authorization.Version,
                 authorization.DeviceId,
                 authorization.Device,
-                request.HttpContext.Connection.RemoteIpAddress.ToString(),
+                request.HttpContext.GetNormalizedRemoteIp(),
                 user);
 
             if (session == null)
@@ -172,10 +173,5 @@ namespace Jellyfin.Api.Helpers
                 .Select(i => i!.Value)
                 .ToArray();
         }
-
-        internal static IPAddress NormalizeIp(IPAddress ip)
-        {
-            return ip.IsIPv4MappedToIPv6 ? ip.MapToIPv4() : ip;
-        }
     }
 }

+ 11 - 1
Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs

@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Net;
 using System.Reflection;
 using Jellyfin.Api.Auth;
 using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
@@ -28,6 +29,7 @@ using Microsoft.AspNetCore.HttpOverrides;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.OpenApi.Models;
 using Swashbuckle.AspNetCore.SwaggerGen;
+using AuthenticationSchemes = Jellyfin.Api.Constants.AuthenticationSchemes;
 
 namespace Jellyfin.Server.Extensions
 {
@@ -136,8 +138,9 @@ namespace Jellyfin.Server.Extensions
         /// </summary>
         /// <param name="serviceCollection">The service collection.</param>
         /// <param name="pluginAssemblies">An IEnumerable containing all plugin assemblies with API controllers.</param>
+        /// <param name="knownProxies">A list of all known proxies to trust for X-Forwarded-For.</param>
         /// <returns>The MVC builder.</returns>
-        public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, IEnumerable<Assembly> pluginAssemblies)
+        public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, IEnumerable<Assembly> pluginAssemblies, IReadOnlyList<string> knownProxies)
         {
             IMvcBuilder mvcBuilder = serviceCollection
                 .AddCors()
@@ -145,6 +148,13 @@ namespace Jellyfin.Server.Extensions
                 .Configure<ForwardedHeadersOptions>(options =>
                 {
                     options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
+                    for (var i = 0; i < knownProxies.Count; i++)
+                    {
+                        if (IPAddress.TryParse(knownProxies[i], out var address))
+                        {
+                            options.KnownProxies.Add(address);
+                        }
+                    }
                 })
                 .AddMvc(opts =>
                 {

+ 2 - 2
Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs

@@ -32,13 +32,13 @@ namespace Jellyfin.Server.Middleware
         /// <returns>The async task.</returns>
         public async Task Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager)
         {
-            if (httpContext.Request.IsLocal())
+            if (httpContext.IsLocal())
             {
                 await _next(httpContext).ConfigureAwait(false);
                 return;
             }
 
-            var remoteIp = httpContext.Request.RemoteIp();
+            var remoteIp = httpContext.GetNormalizedRemoteIp();
 
             if (serverConfigurationManager.Configuration.EnableRemoteAccess)
             {

+ 2 - 1
Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs

@@ -1,6 +1,7 @@
 using System.Diagnostics;
 using System.Globalization;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Extensions;
@@ -69,7 +70,7 @@ namespace Jellyfin.Server.Middleware
                 _logger.LogWarning(
                     "Slow HTTP Response from {url} to {remoteIp} in {elapsed:g} with Status Code {statusCode}",
                     context.Request.GetDisplayUrl(),
-                    context.Connection.RemoteIpAddress,
+                    context.GetNormalizedRemoteIp(),
                     watch.Elapsed,
                     context.Response.StatusCode);
             }

+ 2 - 1
Jellyfin.Server/Startup.cs

@@ -52,7 +52,7 @@ namespace Jellyfin.Server
             {
                 options.HttpsPort = _serverApplicationHost.HttpsPort;
             });
-            services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies());
+            services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies(), _serverConfigurationManager.Configuration.KnownProxies);
 
             services.AddJellyfinApiSwagger();
 
@@ -103,6 +103,7 @@ namespace Jellyfin.Server
                     mainApp.UseDeveloperExceptionPage();
                 }
 
+                mainApp.UseForwardedHeaders();
                 mainApp.UseMiddleware<ExceptionMiddleware>();
 
                 mainApp.UseMiddleware<ResponseTimeMiddleware>();

+ 12 - 34
MediaBrowser.Common/Extensions/HttpContextExtensions.cs

@@ -1,5 +1,4 @@
 using System.Net;
-using MediaBrowser.Common.Net;
 using Microsoft.AspNetCore.Http;
 
 namespace MediaBrowser.Common.Extensions
@@ -10,54 +9,33 @@ namespace MediaBrowser.Common.Extensions
     public static class HttpContextExtensions
     {
         /// <summary>
-        /// Checks the origin of the HTTP request.
+        /// Checks the origin of the HTTP context.
         /// </summary>
-        /// <param name="request">The incoming HTTP request.</param>
+        /// <param name="context">The incoming HTTP context.</param>
         /// <returns><c>true</c> if the request is coming from LAN, <c>false</c> otherwise.</returns>
-        public static bool IsLocal(this HttpRequest request)
+        public static bool IsLocal(this HttpContext context)
         {
-            return (request.HttpContext.Connection.LocalIpAddress == null
-                    && request.HttpContext.Connection.RemoteIpAddress == null)
-                   || request.HttpContext.Connection.LocalIpAddress.Equals(request.HttpContext.Connection.RemoteIpAddress);
+            return (context.Connection.LocalIpAddress == null
+                    && context.Connection.RemoteIpAddress == null)
+                   || context.Connection.LocalIpAddress.Equals(context.Connection.RemoteIpAddress);
         }
 
         /// <summary>
-        /// Extracts the remote IP address of the caller of the HTTP request.
+        /// Extracts the remote IP address of the caller of the HTTP context.
         /// </summary>
-        /// <param name="request">The HTTP request.</param>
+        /// <param name="context">The HTTP context.</param>
         /// <returns>The remote caller IP address.</returns>
-        public static string RemoteIp(this HttpRequest request)
+        public static string GetNormalizedRemoteIp(this HttpContext context)
         {
-            var cachedRemoteIp = request.HttpContext.Items["RemoteIp"]?.ToString();
-            if (!string.IsNullOrEmpty(cachedRemoteIp))
-            {
-                return cachedRemoteIp;
-            }
-
-            IPAddress ip;
-
-            // "Real" remote ip might be in X-Forwarded-For of X-Real-Ip
-            // (if the server is behind a reverse proxy for example)
-            if (!IPAddress.TryParse(request.Headers[CustomHeaderNames.XForwardedFor].ToString(), out ip))
-            {
-                if (!IPAddress.TryParse(request.Headers[CustomHeaderNames.XRealIP].ToString(), out ip))
-                {
-                    ip = request.HttpContext.Connection.RemoteIpAddress;
-
-                    // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests)
-                    ip ??= IPAddress.Loopback;
-                }
-            }
+            // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests)
+            var ip = context.Connection.RemoteIpAddress ?? IPAddress.Loopback;
 
             if (ip.IsIPv4MappedToIPv6)
             {
                 ip = ip.MapToIPv4();
             }
 
-            var normalizedIp = ip.ToString();
-
-            request.HttpContext.Items["RemoteIp"] = normalizedIp;
-            return normalizedIp;
+            return ip.ToString();
         }
     }
 }

+ 6 - 0
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -268,6 +268,11 @@ namespace MediaBrowser.Model.Configuration
         /// </summary>
         public string[] CorsHosts { get; set; }
 
+        /// <summary>
+        /// Gets or sets the known proxies.
+        /// </summary>
+        public string[] KnownProxies { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
@@ -378,6 +383,7 @@ namespace MediaBrowser.Model.Configuration
             EnableSlowResponseWarning = true;
             SlowResponseThresholdMs = 500;
             CorsHosts = new[] { "*" };
+            KnownProxies = Array.Empty<string>();
         }
     }