-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCloudflareIpModule.cs
144 lines (127 loc) · 5.84 KB
/
CloudflareIpModule.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System;
using System.IO;
using System.Web;
using System.Configuration;
using NetTools;
using System.Net;
using System.Collections.Generic;
using System.Diagnostics;
namespace CloudflareProxyTrust
{
public class CloudflareIpModule : IHttpModule
{
private List<IPAddressRange> _cfips = new List<IPAddressRange>();
private DateTime _lastupdated = DateTime.MinValue;
private int count = 0;
public void Dispose() { }
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(Begin);
LoadCfipsData();
_lastupdated = DateTime.Now;
WriteDbg($"Init {_lastupdated}");
}
public void Begin(Object source, EventArgs e)
{
try
{
HttpApplication app = (HttpApplication)source;
HttpRequest request = app.Context.Request;
// stop the loop caused by defaultdocumentmodule
if (!string.IsNullOrEmpty(request.ServerVariables["CF_SECURE"]))
{
WriteDbg("we handled this already");
return;
}
// maybe we arent proxied
var cfConnectingIp = request.ServerVariables["HTTP_CF_CONNECTING_IP"];
if(AllowNonProxyRemote)
if (string.IsNullOrEmpty(cfConnectingIp))
return;
// or maybe we just havent been configured for this site yet
if (_cfips.Count == 0)
return;
// perhaps iis has been reusing this instance for some time
// since we have no idea how many times or for how long IIS may reuse an instance, we will keep track of when we last loaded the list
if (_lastupdated.AddMinutes(20) < DateTime.Now)
{
WriteDbg($"refreshing iplist {request.UserHostAddress}");
LoadCfipsData();
}
#if DEBUG
else
// some verification for our debug logging that we are indeed reusing instances across requests
count++;
#endif
IPAddress remoteaddr = IPAddress.Parse(request.ServerVariables["REMOTE_ADDR"]);
bool trusted = false;
foreach (IPAddressRange cfip in _cfips)
{
if (cfip.Contains(remoteaddr))
{
trusted = true;
// only if trusted, forward the real IP in REMOTE_ADDR
request.ServerVariables.Set("REMOTE_ADDR", cfConnectingIp);
// maybe you want the cloudflare IP? keep it here
request.ServerVariables.Set("HTTP_X_ORIGINAL_ADDR", remoteaddr.ToString());
// we cant rely on checking if X-Original-Addr exists since a malicious client could send this
// it is also useful to stop the defaultdocumentmodule looping
request.ServerVariables.Set("CF_SECURE", "true");
}
}
if(!trusted)
{
// spoofing attempt
request.ServerVariables.Set("CF_SECURE", "false");
// this ends up logged twice, unsure how to prevent this one
EventLog.WriteEntry(".NET Runtime",
$"[CloudflareProxyTrust]: possible spoofing attempt\r\n" +
$"ip:[{request.ServerVariables["REMOTE_ADDR"]}]\r\n" +
$"host:[{request.ServerVariables["SERVER_NAME"]}]\r\n" +
$"url:[{request.RawUrl}]\r\n" +
$"useragent:[{request.UserAgent}]",
EventLogEntryType.Information,
1000
);
}
if (!trusted && DenyUntrusted)
{
app.Response.StatusCode = DenyCode;
app.Response.StatusDescription = DenyDescription;
app.Response.Flush();
app.CompleteRequest();
}
WriteDbg($"trusted: {trusted} remoteaddr: {remoteaddr} cfip: {cfConnectingIp} url: {request.RawUrl} host: {request.ServerVariables["HTTP_HOST"]} reused {count} times");
}
catch (Exception ex)
{
WriteDbg("Exception occured " + ex.Message);
}
}
// this one to deny if untrusted
private static bool DenyUntrusted => Convert.ToBoolean(ConfigurationManager.AppSettings["CF_DenyUntrusted"]);
// this one to allow direct connections to the server, only has effect if were denying untrusted.
private static bool AllowNonProxyRemote => Convert.ToBoolean(ConfigurationManager.AppSettings["CF_AllowNonProxyRemote"] ?? "true");
private static int DenyCode => Convert.ToInt16(ConfigurationManager.AppSettings["CF_DenyCode"] ?? "400");
private static string DenyDescription => ConfigurationManager.AppSettings["CF_DenyDescription"] ?? "Bad Request";
private void LoadCfipsData()
{
var path = ConfigurationManager.AppSettings["CF_IP_Path"];
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
string[] filelines = File.ReadAllText(path).Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string line in filelines)
{
if (IPAddressRange.TryParse(line, out IPAddressRange ipRange))
_cfips.Add(ipRange);
}
}
}
private static void WriteDbg(string msg)
{
#if DEBUG
Debug.WriteLine($"[CloudflareProxyTrust]: {msg}");
#endif
}
}
}