case 4732 - trusted reverse proxies setting
New config setting AYANOVA_TRUSTED_PROXIES (IP addresses or CIDR networks) for reverse proxies on another computer: their X-Forwarded-For is used, a chain of trusted proxies is followed, invalid entries are warned at boot. X-Forwarded-For from a proxy that is not trusted logs one warning per server start naming the proxy's address. Without the setting nothing changes (only a loopback proxy is trusted). Shown in server info; manual page, Linux install note, changelog. F6 follow-up recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -194,6 +194,7 @@ namespace AyaNova.Api.Controllers
|
||||
AYANOVA_LOG_LEVEL = ServerBootConfig.AYANOVA_LOG_LEVEL,
|
||||
AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG = ServerBootConfig.AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG,
|
||||
AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE = ServerBootConfig.AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE,
|
||||
AYANOVA_TRUSTED_PROXIES = ServerBootConfig.AYANOVA_TRUSTED_PROXIES,
|
||||
serverinfo = ServerBootConfig.BOOT_DIAGNOSTIC_INFO,
|
||||
dbserverinfo = ServerBootConfig.DBSERVER_DIAGNOSTIC_INFO
|
||||
}));
|
||||
@@ -317,6 +318,7 @@ namespace AyaNova.Api.Controllers
|
||||
sbRet.AppendLine($"AYANOVA_LOG_LEVEL: {ServerBootConfig.AYANOVA_LOG_LEVEL}");
|
||||
sbRet.AppendLine($"AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG: {ServerBootConfig.AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG}");
|
||||
sbRet.AppendLine($"AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE: {ServerBootConfig.AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE}");
|
||||
sbRet.AppendLine($"AYANOVA_TRUSTED_PROXIES: {ServerBootConfig.AYANOVA_TRUSTED_PROXIES}");
|
||||
|
||||
sbRet.AppendLine("#########################################################");
|
||||
sbRet.AppendLine("SERVER DIAGNOSTICS");
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace AyaNova
|
||||
}
|
||||
|
||||
private readonly ILogger _newLog;
|
||||
private static int _untrustedProxyWarned = 0;
|
||||
private string _connectionString = "";
|
||||
private readonly Microsoft.AspNetCore.Hosting.IWebHostEnvironment _hostingEnvironment;
|
||||
|
||||
@@ -325,9 +326,37 @@ namespace AyaNova
|
||||
ServiceProviderProvider.Provider = app.ApplicationServices;
|
||||
|
||||
//Enable ability to handle reverse proxy
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
//case 4732: a proxy on this computer (loopback) is always trusted; proxies on other computers only when named in AYANOVA_TRUSTED_PROXIES
|
||||
var forwardedHeadersOptions = new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedFor | Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedProto
|
||||
};
|
||||
if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_TRUSTED_PROXIES))
|
||||
{
|
||||
foreach (var entry in ServerBootConfig.AYANOVA_TRUSTED_PROXIES.Split(new[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (entry.Contains('/') && System.Net.IPNetwork.TryParse(entry, out var network))
|
||||
forwardedHeadersOptions.KnownIPNetworks.Add(network);
|
||||
else if (!entry.Contains('/') && System.Net.IPAddress.TryParse(entry, out var address))
|
||||
forwardedHeadersOptions.KnownProxies.Add(address);
|
||||
else
|
||||
_newLog.LogWarning($"AYANOVA_TRUSTED_PROXIES entry \"{entry}\" is not an IP address or network and is ignored");
|
||||
}
|
||||
//follow a chain of trusted proxies (e.g. a load balancer behind another proxy) back to the first address that is not trusted
|
||||
forwardedHeadersOptions.ForwardLimit = null;
|
||||
}
|
||||
app.UseForwardedHeaders(forwardedHeadersOptions);
|
||||
|
||||
//case 4732: X-Forwarded-For from a proxy that is not trusted is ignored (no X-Original-For is set), so every user appears to come
|
||||
//from the proxy's address and shares one failed login limit; say so once in the log
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
if (_untrustedProxyWarned == 0
|
||||
&& context.Request.Headers.ContainsKey("X-Forwarded-For")
|
||||
&& !context.Request.Headers.ContainsKey("X-Original-For")
|
||||
&& System.Threading.Interlocked.Exchange(ref _untrustedProxyWarned, 1) == 0)
|
||||
_newLog.LogWarning($"A request came through a reverse proxy at {(context.Connection.RemoteIpAddress.IsIPv4MappedToIPv6 ? context.Connection.RemoteIpAddress.MapToIPv4() : context.Connection.RemoteIpAddress)} that is not trusted, so users connecting through it all appear to come from that address and share one limit on failed login attempts. If this is your reverse proxy, add its address to the AYANOVA_TRUSTED_PROXIES setting");
|
||||
await next.Invoke();
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,8 @@ namespace AyaNova.Util
|
||||
|
||||
//SECURITY
|
||||
internal static string AYANOVA_SET_SUPERUSER_PW { get; set; }
|
||||
//case 4732: reverse proxies on other computers whose X-Forwarded-For is trusted (addresses or networks, comma separated)
|
||||
internal static string AYANOVA_TRUSTED_PROXIES { get; set; }
|
||||
|
||||
//EMAIL
|
||||
//case 4724: the SMTP server's certificate is checked unless this is set (a mail server with a self-signed or internal certificate)
|
||||
@@ -189,6 +191,8 @@ namespace AyaNova.Util
|
||||
//backdoor back door password superuser reset
|
||||
AYANOVA_SET_SUPERUSER_PW = config.GetValue<string>("AYANOVA_SET_SUPERUSER_PW");
|
||||
|
||||
AYANOVA_TRUSTED_PROXIES = config.GetValue<string>("AYANOVA_TRUSTED_PROXIES");
|
||||
|
||||
//REPORT RENDERING
|
||||
|
||||
//RENDER OVERRIDE URL FOR CORS ISSUES BEHIND IIS (case 4398)
|
||||
|
||||
Reference in New Issue
Block a user