// Copyright Epic Games, Inc. All Rights Reserved. using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace HordeServer.Server; /// /// Middleware that detects if the server is running behind a load balancer or reverse proxy. /// If detected, logs an error if is not configured. /// /// Next middleware in the pipeline /// Logger /// Settings public class ReverseProxyDetectionMiddleware(RequestDelegate next, ILogger logger, IOptionsMonitor settings) { private const int MaxWarnings = 5; private const int MaxRequests = 5000; private readonly List _headers = ["X-Forwarded-For", "X-Forwarded-Proto", "X-Forwarded-Host", "X-Forwarded-Server",]; private int _requestCount; private int _warningCount; /// /// Processes an HTTP request to detect reverse proxy headers and validate server configuration /// public async Task InvokeAsync(HttpContext context) { // Only check the first X number of requests for performance if (_requestCount < MaxRequests && _warningCount < MaxWarnings) { _requestCount++; bool isReverseProxyRequest = _headers.Exists(header => context.Request.Headers.ContainsKey(header)); if (isReverseProxyRequest && settings.CurrentValue.IsDefaultServerUrlUsed()) { _warningCount++; logger.LogError("Load balancer or reverse proxy detected. Ensure {Setting} is configured in server settings", nameof(ServerSettings.ServerUrl)); } } await next(context); } } /// /// Extension methods for configuring the reverse proxy detection middleware. /// public static class ReverseProxyDetectionMiddlewareExtensions { /// /// Adds middleware to detect if the application is running behind a reverse proxy /// public static IApplicationBuilder UseReverseProxyDetection(this IApplicationBuilder builder) { return builder.UseMiddleware(); } }