Skip to content

Commit 9c0da20

Browse files
committed
PR feedback
- Move new ServiceCollectionExtension to Configuration.CloudFoundry - Use IConfigureOptions<ForwardedHeadersOptions> - add missing using on ServiceProviders, true in BuildServiceProvider - more consistent usage of EnvironmentVariableScope - remove CF_INSTANCE var parsing option
1 parent 507a8c2 commit 9c0da20

13 files changed

Lines changed: 422 additions & 227 deletions

src/Common/src/Hosting/Properties/AssemblyInfo.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,3 @@
1313
[assembly: InternalsVisibleTo("Steeltoe.Management.Endpoint")]
1414
[assembly: InternalsVisibleTo("Steeltoe.Management.Endpoint.Test")]
1515
[assembly: InternalsVisibleTo("Steeltoe.Management.Prometheus.Test")]
16-
[assembly: InternalsVisibleTo("Steeltoe.Security.Authorization.Certificate.Test")]
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
#nullable enable
2-
static Steeltoe.Common.Hosting.ServiceCollectionExtensions.ConfigureForwardedHeadersOptionsForCloudFoundry(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
3-
Steeltoe.Common.Hosting.ServiceCollectionExtensions

src/Common/src/Hosting/ServiceCollectionExtensions.cs

Lines changed: 0 additions & 98 deletions
This file was deleted.

src/Common/test/Hosting.Test/ServiceCollectionExtensionsTest.cs

Lines changed: 0 additions & 111 deletions
This file was deleted.

src/Configuration/src/CloudFoundry/CloudFoundryServiceCollectionExtensions.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information.
44

5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.HttpOverrides;
57
using Microsoft.Extensions.DependencyInjection;
68
using Microsoft.Extensions.DependencyInjection.Extensions;
79
using Microsoft.Extensions.Options;
@@ -41,4 +43,32 @@ public static IServiceCollection AddCloudFoundryOptions(this IServiceCollection
4143

4244
return services;
4345
}
46+
47+
/// <summary>
48+
/// Configures <see cref="ForwardedHeadersOptions" /> to use forwarded headers as they are provided in Cloud Foundry. Includes
49+
/// <see cref="ForwardedHeaders.XForwardedHost" /> and <see cref="ForwardedHeaders.XForwardedProto" />, and allows any network.
50+
/// </summary>
51+
/// <param name="services">
52+
/// The <see cref="IServiceCollection" /> to configure.
53+
/// </param>
54+
/// <returns>
55+
/// The same <see cref="IServiceCollection" /> instance, for chaining.
56+
/// </returns>
57+
/// <remarks>
58+
/// IMPORTANT: <see cref="ForwardedHeadersExtensions.UseForwardedHeaders(IApplicationBuilder)" /> must be called separately to activate these options.
59+
/// </remarks>
60+
/// <exception cref="ArgumentNullException">
61+
/// Thrown if <paramref name="services" /> is <c>null</c>.
62+
/// </exception>
63+
public static IServiceCollection ConfigureForwardedHeadersOptionsForCloudFoundry(this IServiceCollection services)
64+
{
65+
ArgumentNullException.ThrowIfNull(services);
66+
67+
services.AddLogging();
68+
services.AddOptions<ForwardedHeadersSettings>().BindConfiguration(ForwardedHeadersSettings.ConfigurationKey);
69+
services.AddOptions<ForwardedHeadersOptions>();
70+
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<ForwardedHeadersOptions>, ConfigureForwardedHeadersOptions>());
71+
72+
return services;
73+
}
4474
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Globalization;
6+
using System.Net;
7+
using System.Net.Sockets;
8+
using Microsoft.AspNetCore.Builder;
9+
using Microsoft.AspNetCore.HttpOverrides;
10+
using Microsoft.Extensions.Logging;
11+
using Microsoft.Extensions.Options;
12+
using Steeltoe.Common;
13+
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
14+
15+
namespace Steeltoe.Configuration.CloudFoundry;
16+
17+
internal sealed class ConfigureForwardedHeadersOptions(IOptions<ForwardedHeadersSettings> headerSettings, ILogger<ConfigureForwardedHeadersOptions> logger)
18+
: IConfigureOptions<ForwardedHeadersOptions>
19+
{
20+
public void Configure(ForwardedHeadersOptions options)
21+
{
22+
ArgumentNullException.ThrowIfNull(options);
23+
24+
if (!Platform.IsCloudFoundry)
25+
{
26+
return;
27+
}
28+
29+
options.ForwardedHeaders |= ForwardedHeaders.XForwardedHost | ForwardedHeaders.XForwardedProto;
30+
31+
if (!IsDefaultKnownNetworks(options.KnownNetworks) || !IsDefaultKnownProxies(options.KnownProxies))
32+
{
33+
logger.LogTrace("Known proxies or networks have already been configured.");
34+
return;
35+
}
36+
37+
if (headerSettings.Value.TrustAllNetworks)
38+
{
39+
logger.LogInformation(
40+
"'TrustAllNetworks' has been set, forwarded headers will be allowed from any source. This should only be used behind a trusted ingress.");
41+
42+
options.KnownNetworks.Clear();
43+
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("0.0.0.0"), 0));
44+
}
45+
else
46+
{
47+
AddKnownNetworksFromConfiguration(options);
48+
}
49+
}
50+
51+
private static bool IsDefaultKnownNetworks(IList<IPNetwork> networks)
52+
{
53+
if (networks.Count != 1)
54+
{
55+
return false;
56+
}
57+
58+
IPNetwork network = networks[0];
59+
60+
return network is { PrefixLength: 8, Prefix.AddressFamily: AddressFamily.InterNetwork } &&
61+
network.Prefix.GetAddressBytes().SequenceEqual(IPAddress.Parse("127.0.0.1").GetAddressBytes());
62+
}
63+
64+
private static bool IsDefaultKnownProxies(IList<IPAddress> proxies)
65+
{
66+
return proxies.Count == 1 && proxies[0].Equals(IPAddress.IPv6Loopback);
67+
}
68+
69+
private void AddKnownNetworksFromConfiguration(ForwardedHeadersOptions options)
70+
{
71+
foreach (string cidr in headerSettings.Value.KnownNetworks?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? [])
72+
{
73+
if (TryParseCidr(cidr, out IPNetwork network) && !options.KnownNetworks.Any(knownNetwork =>
74+
knownNetwork.Prefix.Equals(network.Prefix) && knownNetwork.PrefixLength.Equals(network.PrefixLength)))
75+
{
76+
logger.LogDebug("Adding known network {Network}/{PrefixLength} from configuration.", network.Prefix, network.PrefixLength);
77+
options.KnownNetworks.Add(network);
78+
}
79+
else
80+
{
81+
logger.LogWarning("Invalid CIDR format in {KnownNetworksKey}: '{CIDR}'", $"{ForwardedHeadersSettings.ConfigurationKey}:KnownNetworks", cidr);
82+
}
83+
}
84+
}
85+
86+
private static bool TryParseCidr(string cidr, out IPNetwork network)
87+
{
88+
network = null!;
89+
string[] parts = cidr.Split('/');
90+
91+
if (parts.Length != 2)
92+
{
93+
return false;
94+
}
95+
96+
if (IPAddress.TryParse(parts[0], out IPAddress? address) && int.TryParse(parts[1], CultureInfo.InvariantCulture, out int prefixLength))
97+
{
98+
network = new IPNetwork(address, prefixLength);
99+
return true;
100+
}
101+
102+
return false;
103+
}
104+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace Steeltoe.Configuration.CloudFoundry;
6+
7+
public sealed class ForwardedHeadersSettings
8+
{
9+
internal const string ConfigurationKey = "Steeltoe:ForwardedHeaders";
10+
11+
/// <summary>
12+
/// Gets or sets a value indicating whether to trust all networks (adds 0.0.0.0/0). WARNING: Use only behind a trusted ingress.
13+
/// </summary>
14+
public bool TrustAllNetworks { get; set; } = true;
15+
16+
/// <summary>
17+
/// Gets or sets known networks to trust, in CIDR notation. Example: "10.0.0.0/8,192.168.0.0/16".
18+
/// </summary>
19+
public string? KnownNetworks { get; set; }
20+
}

src/Configuration/src/CloudFoundry/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
[assembly: InternalsVisibleTo("Steeltoe.Configuration.ConfigServer.Test")]
1212
[assembly: InternalsVisibleTo("Steeltoe.Connectors.Test")]
1313
[assembly: InternalsVisibleTo("Steeltoe.Discovery.HttpClients.Test")]
14+
[assembly: InternalsVisibleTo("Steeltoe.Security.Authorization.Certificate.Test")]

0 commit comments

Comments
 (0)