Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion code-secure-api/code-secure-api/Api/ApiServer.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Net;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using CodeSecure.Application;
Expand All @@ -18,9 +19,16 @@ public static class ApiServer
public static void Run(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

// Load app configuration to get trusted proxies
var appConfig = AppConfig.Load();

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;
options.ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost | ForwardedHeaders.XForwardedFor;

// Parse trusted proxies from configuration
ParseTrustedProxies(appConfig.TrustedProxies, options);
});
builder.Host.UseSerilog((context, configuration) =>
configuration.ReadFrom.Configuration(context.Configuration));
Expand Down Expand Up @@ -89,6 +97,56 @@ public static void Run(string[] args)
app.LoadAuthenticationProviders();
app.Run();
}

private static void ParseTrustedProxies(string trustedProxies, ForwardedHeadersOptions options)
{
if (string.IsNullOrWhiteSpace(trustedProxies))
return;

var proxies = trustedProxies.Split(',', StringSplitOptions.RemoveEmptyEntries);

foreach (var proxy in proxies)
{
var trimmedProxy = proxy.Trim();

// Check if it's a CIDR block
if (trimmedProxy.Contains('/'))
{
if (TryParseIPNetwork(trimmedProxy, out var network, out var prefixLength))
{
options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(network, prefixLength));
}
}
// Single IP address
else if (IPAddress.TryParse(trimmedProxy, out var ipAddress))
{
options.KnownProxies.Add(ipAddress);
}
}
}

private static bool TryParseIPNetwork(string cidr, out IPAddress network, out int prefixLength)
{
network = IPAddress.None;
prefixLength = 0;

var parts = cidr.Split('/');
if (parts.Length != 2)
return false;

if (!IPAddress.TryParse(parts[0], out network))
return false;

if (!int.TryParse(parts[1], out prefixLength))
return false;

// Validate prefix length based on address family
var maxPrefixLength = network.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128;
if (prefixLength < 0 || prefixLength > maxPrefixLength)
return false;

return true;
}
}

internal sealed partial class SlugifyParameterTransformer : IOutboundParameterTransformer
Expand Down
3 changes: 3 additions & 0 deletions code-secure-api/code-secure-api/Application/AppConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public class AppConfig
[Option(Env = "FRONTEND_URL", Default = "")]
public string FrontendUrl { get; set; } = string.Empty;

[Option(Env = "TRUSTED_PROXIES", Default = "127.0.0.1,::1")]
public string TrustedProxies { get; set; } = string.Empty;

[JsonIgnore] internal SecurityKey AccessTokenSecurityKey = null!;
[JsonIgnore] internal SecurityKey RefreshTokenSecurityKey = null!;

Expand Down
Loading