Last active
June 26, 2021 17:54
-
-
Save winzig/ee57e559341a2a92d8ee7bc0daa1304a to your computer and use it in GitHub Desktop.
If you're using a load balancer that obscures the remote client's true IP address, you can run this IHttpModule to take the first IP address from the X-Forwarded-For header, and overwrite the REMOTE_ADDR and REMOTE_HOST server variables. (We tried to use the URL Rewrite module that everyone normally recommends to do this, but it's buggy.)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Web; | |
using System.Text.RegularExpressions; | |
namespace HttpModules | |
{ | |
/// <summary> | |
/// This module handles complications from our load balancer configuration not properly passing the client's true IP | |
/// address to our code via the REMOTE_ADDR and REMOTE_HOST variables. We tried to use URL Rewrite to compensate for | |
/// this, but it does not run when default documents are being accessed (a longstanding bug). | |
/// | |
/// Add a reference to this class to your <modules> section in either applicationHost.config (for your entire server), | |
/// or the web.config of a specific web server. | |
/// </summary> | |
public class ClientIP : IHttpModule | |
{ | |
/// <summary> | |
/// The IP we want will be in $1. X-Forwarded-For can carry multiple IPs in a comma-separated | |
/// list, but the first IP should belong to the original client. | |
/// </summary> | |
private static Regex REGEX_FIRST_IP = new Regex(@"^\s*(\d+\.\d+\.\d+\.\d+)", RegexOptions.Compiled); | |
/// <summary> | |
/// This overwrites the REMOTE_ADDR and REMOTE_HOST server variables with the first IP taken from the | |
/// X-Forwarded-For header, if it exists. Otherwise it does nothing. | |
/// </summary> | |
void OnBeginRequest(object sender, EventArgs e) | |
{ | |
HttpApplication app = (HttpApplication)sender; | |
string headervalue = app.Context.Request.Headers["X-Forwarded-For"]; | |
if (headervalue != null) | |
{ | |
Match m = REGEX_FIRST_IP.Match(headervalue); | |
if (m.Success) | |
{ | |
app.Context.Request.ServerVariables["REMOTE_ADDR"] = m.Groups[1].Value; | |
app.Context.Request.ServerVariables["REMOTE_HOST"] = m.Groups[1].Value; | |
} | |
} | |
} | |
/// <summary> | |
/// Put anything here that needs to happen when the module is disposed of. | |
/// </summary> | |
void IHttpModule.Dispose() | |
{ | |
} | |
/// <summary> | |
/// This wires up our OnBeginRequest method to the BeginRequest event. | |
/// </summary> | |
/// <param name="app"></param> | |
void IHttpModule.Init(HttpApplication app) | |
{ | |
app.BeginRequest += OnBeginRequest; | |
} | |
} | |
} |
Also this code likely should be updated to support IPv6 (the regex that matches the IP should be updated).
Do I need to create a DLL? as in the GlobalModules section there are dll file references.
@itdap77 I don't totally recall, but yeah I believe you create a DLL using the code above, and then you can register it with IIS. Check out this article as it seems to be kind of what I remember doing: https://docs.microsoft.com/en-us/iis/develop/runtime-extensibility/developing-iis-modules-and-handlers-with-the-net-framework
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@opiums9 @itdap77 Sorry, I haven’t used IIS in over 3 years, so can’t remember the details, but the comments in the above code say: