Skip to content

Instantly share code, notes, and snippets.

@tpeczek
Last active March 2, 2023 12:42
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tpeczek/f3f341df637e46ea7d077e017ea309e1 to your computer and use it in GitHub Desktop.
Save tpeczek/f3f341df637e46ea7d077e017ea309e1 to your computer and use it in GitHub Desktop.
ASP.NET Core middleware for CloudFlare Connecting IP support
using System;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.HttpOverrides;
using Lib.AspNetCore.CloudFlareConnectingIp;
namespace Microsoft.AspNetCore.Builder
{
public static class CloudFlareConnectingIpExtensions
{
public static IApplicationBuilder UseCloudFlareConnectingIp(this IApplicationBuilder app)
{
return app.UseCloudFlareConnectingIp(alse);
}
public static IApplicationBuilder UseCloudFlareConnectingIp(this IApplicationBuilder app, bool checkOriginatesFromCloudflare)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (checkOriginatesFromCloudflare)
{
app.UseMiddleware<CloudFlareConnectingIpMiddleware>();
}
else
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedForHeaderName = CloudFlareConnectingIpMiddleware.CLOUDFLARE_CONNECTING_IP_HEADER_NAME,
ForwardedHeaders = ForwardedHeaders.XForwardedFor
});
}
return app;
}
}
}
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpOverrides;
using NetTools;
namespace Lib.AspNetCore.CloudFlareConnectingIp
{
internal class CloudFlareConnectingIpMiddleware
{
public const string CLOUDFLARE_CONNECTING_IP_HEADER_NAME = "CF_CONNECTING_IP";
private static readonly IPAddressRange[] _cloudFlareIpAddressRanges = new IPAddressRange[]
{
IPAddressRange.Parse("103.21.244.0/22"),
IPAddressRange.Parse("103.22.200.0/22"),
IPAddressRange.Parse("103.31.4.0/22"),
IPAddressRange.Parse("104.16.0.0/12"),
IPAddressRange.Parse("108.162.192.0/18"),
IPAddressRange.Parse("131.0.72.0/22"),
IPAddressRange.Parse("141.101.64.0/18"),
IPAddressRange.Parse("162.158.0.0/15"),
IPAddressRange.Parse("172.64.0.0/13"),
IPAddressRange.Parse("173.245.48.0/20"),
IPAddressRange.Parse("188.114.96.0/20"),
IPAddressRange.Parse("190.93.240.0/20"),
IPAddressRange.Parse("197.234.240.0/22"),
IPAddressRange.Parse("198.41.128.0/17"),
IPAddressRange.Parse("2400:cb00::/32"),
IPAddressRange.Parse("2405:8100::/32"),
IPAddressRange.Parse("2405:b500::/32"),
IPAddressRange.Parse("2606:4700::/32"),
IPAddressRange.Parse("2803:f800::/32"),
IPAddressRange.Parse("2c0f:f248::/32"),
IPAddressRange.Parse("2a06:98c0::/29")
};
private readonly RequestDelegate _next;
private readonly ForwardedHeadersMiddleware _forwardedHeadersMiddleware;
public CloudFlareConnectingIpMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_forwardedHeadersMiddleware = new ForwardedHeadersMiddleware(next, loggerFactory, Options.Create(new ForwardedHeadersOptions
{
ForwardedForHeaderName = CLOUDFLARE_CONNECTING_IP_HEADER_NAME,
ForwardedHeaders = Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedFor
}));
}
public Task Invoke(HttpContext context)
{
if (context.Request.Headers.ContainsKey(CLOUDFLARE_CONNECTING_IP_HEADER_NAME) && IsCloudFlareIp(context.Connection.RemoteIpAddress))
{
return _forwardedHeadersMiddleware.Invoke(context);
}
return _next(context);
}
private bool IsCloudFlareIp(IPAddress ipadress)
{
bool isCloudFlareIp = false;
for (int i = 0; i < _cloudFlareIpAddressRanges.Length; i++)
{
isCloudFlareIp = _cloudFlareIpAddressRanges[i].Contains(ipadress);
if (isCloudFlareIp)
{
break;
}
}
return isCloudFlareIp;
}
}
}
@jjxtra
Copy link

jjxtra commented Sep 10, 2019

Needs https://www.nuget.org/packages/IPAddressRange/

private static string[] GetStrings(string url)
{
	return new WebClient().DownloadString(url).Split('\n').Select(s => s.Trim()).ToArray();
}

private static string[] GetCloudflareIP()
{
	try
	{
		return GetStrings("https://www.cloudflare.com/ips-v4").Union(GetStrings("https://www.cloudflare.com/ips-v6")).ToArray();
	}
	catch
	{
		return @"173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/12
172.64.0.0/13
131.0.72.0/22
2400:cb00::/32
2606:4700::/32
2803:f800::/32
2405:b500::/32
2405:8100::/32
2a06:98c0::/29
2c0f:f248::/32
".Split('\n').Select(s => s.Trim()).ToArray();
	}
}

/// <summary>
/// Add cloudflare forward header options
/// </summary>
/// <param name="builder">Application builder</param>
public static void UseCloudflareForwardHeaderOptions(this IApplicationBuilder builder)
{
	ForwardedHeadersOptions options = new ForwardedHeadersOptions
	{
		ForwardedForHeaderName = "CF_CONNECTING_IP",
		ForwardedHeaders = ForwardedHeaders.All
	};
	try
	{
		ICollection<string> urls = builder.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
		if (urls != null && urls.Count != 0)
		{
			string[] cloudFlareIP = GetCloudflareIP();
			foreach (string line in cloudFlareIP)
			{
				if (IPAddressRange.TryParse(line, out IPAddressRange range))
				{
					options.KnownNetworks.Add(new IPNetwork(range.Begin, range.GetPrefixLength()));
				}
			}
		}
	}
	catch
	{
	}
	builder.UseForwardedHeaders(options);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment