Skip to content

Commit

Permalink
Allow bypassing of x-forwarded header removal (#2728)
Browse files Browse the repository at this point in the history
* Allow bypassing of x-forwarded header removal

* Allowing bypass of removing forwarded header
  • Loading branch information
rkargMsft authored Feb 14, 2025
1 parent 6cff486 commit f52bfb9
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 33 deletions.
23 changes: 15 additions & 8 deletions src/ReverseProxy/Transforms/ForwardedTransformExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,21 @@ public static TransformBuilderContext AddXForwardedPrefix(this TransformBuilderC
/// Adds the transform which will add X-Forwarded-* request headers.
/// </summary>
/// <remarks>
/// Also removes the <c>Forwarded</c> header when enabled.
/// Also optionally removes the <c>Forwarded</c> header when enabled.
/// </remarks>
public static TransformBuilderContext AddXForwarded(this TransformBuilderContext context, ForwardedTransformActions action = ForwardedTransformActions.Set)
public static TransformBuilderContext AddXForwarded(this TransformBuilderContext context, ForwardedTransformActions action = ForwardedTransformActions.Set, bool removeForwardedHeader = true)
{
context.AddXForwardedFor(action: action);
context.AddXForwardedPrefix(action: action);
context.AddXForwardedHost(action: action);
context.AddXForwardedProto(action: action);

// Remove the Forwarded header when an X-Forwarded transform is enabled
TransformHelpers.RemoveForwardedHeader(context);
if (removeForwardedHeader)
{
// Remove the Forwarded header when an X-Forwarded transform is enabled
TransformHelpers.RemoveForwardedHeader(context);
}

return context;
}

Expand Down Expand Up @@ -177,11 +181,11 @@ public static RouteConfig WithTransformForwarded(this RouteConfig route, bool us
/// Adds the transform which will add the Forwarded header as defined by [RFC 7239](https://tools.ietf.org/html/rfc7239).
/// </summary>
/// <remarks>
/// Also removes the <c>X-Forwarded</c> headers when enabled.
/// Also optionally removes the <c>X-Forwarded</c> headers when enabled.
/// </remarks>
public static TransformBuilderContext AddForwarded(this TransformBuilderContext context,
bool useHost = true, bool useProto = true, NodeFormat forFormat = NodeFormat.Random,
NodeFormat byFormat = NodeFormat.Random, ForwardedTransformActions action = ForwardedTransformActions.Set)
NodeFormat byFormat = NodeFormat.Random, ForwardedTransformActions action = ForwardedTransformActions.Set, bool removeAllXForwardedHeaders = true)
{
context.UseDefaultForwarders = false;

Expand All @@ -196,8 +200,11 @@ public static TransformBuilderContext AddForwarded(this TransformBuilderContext
context.RequestTransforms.Add(new RequestHeaderForwardedTransform(random,
forFormat, byFormat, useHost, useProto, action));

// Remove the X-Forwarded headers when an Forwarded transform is enabled
TransformHelpers.RemoveAllXForwardedHeaders(context, ForwardedTransformFactory.DefaultXForwardedPrefix);
if (removeAllXForwardedHeaders)
{
// Remove the X-Forwarded headers when a Forwarded transform is enabled
TransformHelpers.RemoveAllXForwardedHeaders(context, ForwardedTransformFactory.DefaultXForwardedPrefix);
}
}
return context;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,45 +143,68 @@ public void WithTransformForwarded(NodeFormat forFormat, bool useHost, bool useP

var builderContext = ValidateAndBuild(routeConfig, _factory, CreateServices());

ValidateForwarded(builderContext, useHost, useProto, forFormat, byFormat, action);
ValidateForwarded(builderContext, useHost, useProto, forFormat, byFormat, action, true);
}

[Theory]
[InlineData(NodeFormat.Random, true, true, NodeFormat.Random, ForwardedTransformActions.Append)]
[InlineData(NodeFormat.RandomAndPort, true, true, NodeFormat.Random, ForwardedTransformActions.Set)]
[InlineData(NodeFormat.None, false, false, NodeFormat.None, ForwardedTransformActions.Append)]
[InlineData(NodeFormat.None, false, false, NodeFormat.None, ForwardedTransformActions.Set)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Random, ForwardedTransformActions.Append)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Random, ForwardedTransformActions.Set)]
[InlineData(NodeFormat.None, false, true, NodeFormat.None, ForwardedTransformActions.Set)]
[InlineData(NodeFormat.None, false, true, NodeFormat.RandomAndPort, ForwardedTransformActions.Set)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Unknown, ForwardedTransformActions.Set)]
[InlineData(NodeFormat.None, false, true, NodeFormat.UnknownAndPort, ForwardedTransformActions.Set)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Ip, ForwardedTransformActions.Set)]
[InlineData(NodeFormat.None, false, true, NodeFormat.IpAndPort, ForwardedTransformActions.Set)]
public void AddForwarded(NodeFormat forFormat, bool useHost, bool useProto, NodeFormat byFormat, ForwardedTransformActions action)
[InlineData(NodeFormat.Random, true, true, NodeFormat.Random, ForwardedTransformActions.Append, true)]
[InlineData(NodeFormat.Random, true, true, NodeFormat.Random, ForwardedTransformActions.Append, false)]
[InlineData(NodeFormat.RandomAndPort, true, true, NodeFormat.Random, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.RandomAndPort, true, true, NodeFormat.Random, ForwardedTransformActions.Set, false)]
[InlineData(NodeFormat.None, false, false, NodeFormat.None, ForwardedTransformActions.Append, true)]
[InlineData(NodeFormat.None, false, false, NodeFormat.None, ForwardedTransformActions.Append, false)]
[InlineData(NodeFormat.None, false, false, NodeFormat.None, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.None, false, false, NodeFormat.None, ForwardedTransformActions.Set, false)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Random, ForwardedTransformActions.Append, true)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Random, ForwardedTransformActions.Append, false)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Random, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Random, ForwardedTransformActions.Set, false)]
[InlineData(NodeFormat.None, false, true, NodeFormat.None, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.None, false, true, NodeFormat.None, ForwardedTransformActions.Set, false)]
[InlineData(NodeFormat.None, false, true, NodeFormat.RandomAndPort, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.None, false, true, NodeFormat.RandomAndPort, ForwardedTransformActions.Set, false)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Unknown, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Unknown, ForwardedTransformActions.Set, false)]
[InlineData(NodeFormat.None, false, true, NodeFormat.UnknownAndPort, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.None, false, true, NodeFormat.UnknownAndPort, ForwardedTransformActions.Set, false)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Ip, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.None, false, true, NodeFormat.Ip, ForwardedTransformActions.Set, false)]
[InlineData(NodeFormat.None, false, true, NodeFormat.IpAndPort, ForwardedTransformActions.Set, true)]
[InlineData(NodeFormat.None, false, true, NodeFormat.IpAndPort, ForwardedTransformActions.Set, false)]
public void AddForwarded(NodeFormat forFormat, bool useHost, bool useProto, NodeFormat byFormat, ForwardedTransformActions action, bool removeAllXForwardedHeaders)
{
var builderContext = CreateBuilderContext(services: CreateServices());
builderContext.AddForwarded(useHost, useProto, forFormat, byFormat, action);
builderContext.AddForwarded(useHost, useProto, forFormat, byFormat, action, removeAllXForwardedHeaders);

ValidateForwarded(builderContext, useHost, useProto, forFormat, byFormat, action);
ValidateForwarded(builderContext, useHost, useProto, forFormat, byFormat, action, removeAllXForwardedHeaders);
}

private static void ValidateForwarded(TransformBuilderContext builderContext, bool useHost, bool useProto,
NodeFormat forFormat, NodeFormat byFormat, ForwardedTransformActions action)
NodeFormat forFormat, NodeFormat byFormat, ForwardedTransformActions action, bool removeAllXForwardedHeaders)
{
Assert.False(builderContext.UseDefaultForwarders);

if (byFormat != NodeFormat.None || forFormat != NodeFormat.None || useHost || useProto)
{
Assert.Equal(5, builderContext.RequestTransforms.Count);
Assert.All(
builderContext.RequestTransforms.Skip(1).Select(t => (dynamic)t),
t =>
{
Assert.StartsWith("X-Forwarded-", t.HeaderName);
Assert.Equal(ForwardedTransformActions.Remove, t.TransformAction);
});
if (removeAllXForwardedHeaders)
{
Assert.Equal(5, builderContext.RequestTransforms.Count);
Assert.All(
builderContext.RequestTransforms.Skip(1).Select(t => (dynamic)t),
t =>
{
Assert.StartsWith("X-Forwarded-", t.HeaderName);
Assert.Equal(ForwardedTransformActions.Remove, t.TransformAction);
});
}
else
{
Assert.Equal(1, builderContext.RequestTransforms.Count);
var xForwardedTransforms = builderContext.RequestTransforms.Skip(1).Cast<dynamic>().Where(requestTransform =>
requestTransform.HeaderName.ToLowerInvariant().StartsWith("x-forwarded")).ToList();
Assert.Empty(xForwardedTransforms);
}

var transform = builderContext.RequestTransforms[0];
var requestHeaderForwardedTransform = Assert.IsType<RequestHeaderForwardedTransform>(transform);
Assert.Equal(action, requestHeaderForwardedTransform.TransformAction);
Expand Down

0 comments on commit f52bfb9

Please sign in to comment.