using System.Net; using System.Net.Sockets; namespace UniVerse.Api.Middleware; public sealed class LocalNetworksOnlyMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public LocalNetworksOnlyMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { var remoteIpAddress = context.Connection.RemoteIpAddress; if (remoteIpAddress is null || !IsLocalNetwork(remoteIpAddress)) { _logger.LogWarning("Blocked metrics request from non-local address {RemoteIpAddress}", remoteIpAddress); context.Response.StatusCode = StatusCodes.Status403Forbidden; await context.Response.WriteAsync("Metrics endpoint is available only from local networks."); return; } await _next(context); } private static bool IsLocalNetwork(IPAddress ipAddress) { if (IPAddress.IsLoopback(ipAddress)) { return true; } if (ipAddress.IsIPv4MappedToIPv6) { ipAddress = ipAddress.MapToIPv4(); } return ipAddress.AddressFamily switch { AddressFamily.InterNetwork => IsPrivateOrLinkLocalIPv4(ipAddress), AddressFamily.InterNetworkV6 => IsPrivateOrLinkLocalIPv6(ipAddress), _ => false }; } private static bool IsPrivateOrLinkLocalIPv4(IPAddress ipAddress) { var bytes = ipAddress.GetAddressBytes(); return bytes[0] == 10 || bytes[0] == 127 || (bytes[0] == 192 && bytes[1] == 168) || (bytes[0] == 172 && bytes[1] is >= 16 and <= 31) || (bytes[0] == 169 && bytes[1] == 254); } private static bool IsPrivateOrLinkLocalIPv6(IPAddress ipAddress) { var bytes = ipAddress.GetAddressBytes(); return ipAddress.IsIPv6LinkLocal || ipAddress.IsIPv6SiteLocal || (bytes[0] & 0xfe) == 0xfc; } }