Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow bypassing of x-forwarded header removal #2728

Merged
merged 5 commits into from
Feb 14, 2025
Merged
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
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
Loading