Skip to content

Commit fef6322

Browse files
authored
Fix ReverseProxySettings from Configuration (OrchardCMS#18739)
1 parent c051b51 commit fef6322

File tree

9 files changed

+164
-27
lines changed

9 files changed

+164
-27
lines changed

src/OrchardCore.Cms.Web/appsettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@
278278
//}
279279
//"OrchardCore_ReverseProxy": {
280280
// "ForwardedHeaders": "None",
281-
// "KnownNetworks": ["192.168.1.100", "192.168.1.101"],
281+
// "KnownNetworks": ["192.168.1.100/4", "192.168.1.101/4"],
282282
// "KnownProxies": ["192.168.1.200", "192.168.1.201"],
283283
//},
284284
//"OrchardCore_Facebook": {

src/OrchardCore.Modules/OrchardCore.ReverseProxy/Drivers/ReverseProxySettingsDisplayDriver.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
using Microsoft.AspNetCore.Authorization;
22
using Microsoft.AspNetCore.Http;
33
using Microsoft.AspNetCore.HttpOverrides;
4+
using Microsoft.AspNetCore.Mvc.Localization;
5+
using Microsoft.Extensions.Options;
46
using OrchardCore.DisplayManagement.Entities;
57
using OrchardCore.DisplayManagement.Handlers;
8+
using OrchardCore.DisplayManagement.Notify;
69
using OrchardCore.DisplayManagement.Views;
710
using OrchardCore.Environment.Shell;
811
using OrchardCore.ReverseProxy.Settings;
@@ -19,14 +22,25 @@ public sealed class ReverseProxySettingsDisplayDriver : SiteDisplayDriver<Revers
1922
private readonly IHttpContextAccessor _httpContextAccessor;
2023
private readonly IAuthorizationService _authorizationService;
2124

25+
private readonly ReverseProxySettings _reverseProxySettings;
26+
private readonly INotifier _notifier;
27+
28+
internal readonly IHtmlLocalizer H;
29+
2230
public ReverseProxySettingsDisplayDriver(
2331
IShellReleaseManager shellReleaseManager,
2432
IHttpContextAccessor httpContextAccessor,
25-
IAuthorizationService authorizationService)
33+
IAuthorizationService authorizationService,
34+
IOptionsSnapshot<ReverseProxySettings> reverseProxySettings,
35+
INotifier notifier,
36+
IHtmlLocalizer<ReverseProxySettingsDisplayDriver> htmlLocalizer)
2637
{
2738
_shellReleaseManager = shellReleaseManager;
2839
_httpContextAccessor = httpContextAccessor;
2940
_authorizationService = authorizationService;
41+
_reverseProxySettings = reverseProxySettings.Value;
42+
_notifier = notifier;
43+
H = htmlLocalizer;
3044
}
3145

3246
protected override string SettingsGroupId
@@ -43,11 +57,20 @@ public override async Task<IDisplayResult> EditAsync(ISite site, ReverseProxySet
4357

4458
context.AddTenantReloadWarningWrapper();
4559

60+
// Set the settings from configuration when AdminSettings are overridden via ConfigureReverseProxySettings()
61+
var currentSettings = settings;
62+
if (_reverseProxySettings.FromConfiguration)
63+
{
64+
currentSettings = _reverseProxySettings;
65+
66+
await _notifier.InformationAsync(H["The current settings are coming from configuration sources, saving the settings will affect the AdminSettings not the configuration."]);
67+
}
68+
4669
return Initialize<ReverseProxySettingsViewModel>("ReverseProxySettings_Edit", model =>
4770
{
48-
model.EnableXForwardedFor = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedFor);
49-
model.EnableXForwardedHost = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedHost);
50-
model.EnableXForwardedProto = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedProto);
71+
model.EnableXForwardedFor = currentSettings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedFor);
72+
model.EnableXForwardedHost = currentSettings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedHost);
73+
model.EnableXForwardedProto = currentSettings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedProto);
5174
}).Location("Content:2")
5275
.OnGroup(SettingsGroupId);
5376
}

src/OrchardCore.Modules/OrchardCore.ReverseProxy/Extensions/OrchardCoreBuilderExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ public static OrchardCoreBuilder ConfigureReverseProxySettings(this OrchardCoreB
1212
{
1313
var configurationSection = serviceProvider.GetRequiredService<IShellConfiguration>().GetSection("OrchardCore_ReverseProxy");
1414

15-
tenantServices.PostConfigure<ReverseProxySettings>(settings => configurationSection.Bind(settings));
15+
tenantServices.PostConfigure<ReverseProxySettings>(settings =>
16+
{
17+
configurationSection.Bind(settings);
18+
19+
settings.FromConfiguration = true;
20+
});
1621
});
1722

1823
return builder;
Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
1+
using System.Net;
12
using Microsoft.AspNetCore.Builder;
2-
using Microsoft.AspNetCore.HttpOverrides;
33
using Microsoft.Extensions.Options;
4+
using OrchardCore.ReverseProxy.Settings;
45

56
namespace OrchardCore.ReverseProxy.Services;
67

78
public sealed class ForwardedHeadersOptionsConfiguration : IConfigureOptions<ForwardedHeadersOptions>
89
{
9-
private readonly ReverseProxyService _reverseProxyService;
10+
private readonly ReverseProxySettings _reverseProxySettings;
1011

11-
public ForwardedHeadersOptionsConfiguration(ReverseProxyService reverseProxyService)
12+
public ForwardedHeadersOptionsConfiguration(IOptions<ReverseProxySettings> reverseProxySettingsOptions)
1213
{
13-
_reverseProxyService = reverseProxyService;
14+
_reverseProxySettings = reverseProxySettingsOptions.Value;
1415
}
1516

1617
public void Configure(ForwardedHeadersOptions options)
1718
{
18-
var reverseProxySettings = _reverseProxyService.GetSettingsAsync().GetAwaiter().GetResult();
19-
options.ForwardedHeaders = reverseProxySettings.ForwardedHeaders;
19+
options.ForwardedHeaders = _reverseProxySettings.ForwardedHeaders;
2020

21-
// In .NET 10, KnownNetworks is obsolete, use KnownIPNetworks instead
2221
options.KnownIPNetworks.Clear();
2322
options.KnownProxies.Clear();
2423

25-
foreach (var network in reverseProxySettings.KnownNetworks)
24+
foreach (var network in _reverseProxySettings.KnownNetworks)
2625
{
27-
// In .NET 10, use System.Net.IPNetwork instead of the obsolete IPNetwork
28-
options.KnownIPNetworks.Add(System.Net.IPNetwork.Parse(network));
26+
options.KnownIPNetworks.Add(IPNetwork.Parse(network));
2927
}
3028

31-
foreach (var proxy in reverseProxySettings.KnownProxies)
29+
foreach (var proxy in _reverseProxySettings.KnownProxies)
3230
{
33-
options.KnownProxies.Add(System.Net.IPAddress.Parse(proxy));
31+
options.KnownProxies.Add(IPAddress.Parse(proxy));
3432
}
3533
}
3634
}

src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ReverseProxyService.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ public class ReverseProxyService
88
private readonly ISiteService _siteService;
99

1010
public ReverseProxyService(ISiteService siteService)
11-
{
12-
_siteService = siteService;
13-
}
11+
=> _siteService = siteService;
1412

1513
public Task<ReverseProxySettings> GetSettingsAsync()
1614
=> _siteService.GetSettingsAsync<ReverseProxySettings>();
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Microsoft.Extensions.Options;
2+
using OrchardCore.ReverseProxy.Settings;
3+
4+
namespace OrchardCore.ReverseProxy.Services;
5+
6+
public class ReverseProxySettingsConfiguration : IConfigureOptions<ReverseProxySettings>
7+
{
8+
private readonly ReverseProxyService _reverseProxyService;
9+
10+
public ReverseProxySettingsConfiguration(ReverseProxyService reverseProxyService)
11+
=> _reverseProxyService = reverseProxyService;
12+
13+
public void Configure(ReverseProxySettings options)
14+
{
15+
var settings = _reverseProxyService.GetSettingsAsync()
16+
.GetAwaiter()
17+
.GetResult();
18+
19+
options.ForwardedHeaders = settings.ForwardedHeaders;
20+
options.KnownNetworks = settings.KnownNetworks;
21+
options.KnownProxies = settings.KnownProxies;
22+
}
23+
}
24+

src/OrchardCore.Modules/OrchardCore.ReverseProxy/Settings/ReverseProxySettings.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Text.Json.Serialization;
12
using Microsoft.AspNetCore.HttpOverrides;
23

34
namespace OrchardCore.ReverseProxy.Settings;
@@ -9,4 +10,7 @@ public class ReverseProxySettings
910
public string[] KnownNetworks { get; set; } = [];
1011

1112
public string[] KnownProxies { get; set; } = [];
13+
14+
[JsonIgnore]
15+
internal bool FromConfiguration { get; set; }
1216
}

src/OrchardCore.Modules/OrchardCore.ReverseProxy/Startup.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public override void ConfigureServices(IServiceCollection services)
3131

3232
services.AddSingleton<ReverseProxyService>();
3333

34+
services.AddTransient<IConfigureOptions<ReverseProxySettings>, ReverseProxySettingsConfiguration>();
35+
3436
services.AddTransient<IConfigureOptions<ForwardedHeadersOptions>, ForwardedHeadersOptionsConfiguration>();
3537
}
3638
}

src/docs/reference/modules/ReverseProxy/README.md

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,101 @@ Enables configuration of hosting scenarios with a reverse proxy, like which HTTP
44

55
## Reverse Proxy Settings Configuration
66

7-
The `OrchardCore.ReverseProxy` module allows the user to use configuration values to override the settings configured from the admin area by calling the `ConfigureReverseProxySettings()` extension method on `OrchardCoreBuilder` when initializing the app.
7+
### Admin Configuration
88

9-
The following configuration values can be customized:
9+
You can configure reverse proxy settings from the admin area by navigating to **Settings → Reverse Proxy**.
10+
11+
The following settings are available:
12+
13+
- **X-Forwarded-For**: Enables forwarding of the client IP address
14+
- **X-Forwarded-Host**: Enables forwarding of the original host requested by the client
15+
- **X-Forwarded-Proto**: Enables forwarding of the protocol (HTTP or HTTPS) used by the client
16+
17+
These settings are stored in the site configuration and can be managed per tenant.
18+
19+
### File Settings Configuration
20+
21+
The `OrchardCore.ReverseProxy` module allows you to use configuration values from `appsettings.json` to configure reverse proxy settings by calling the `ConfigureReverseProxySettings()` extension method on `OrchardCoreBuilder` when initializing the app.
22+
23+
In your `Program.cs` or startup code, call the extension method:
24+
25+
```csharp
26+
builder.Services.AddOrchardCore()
27+
.ConfigureReverseProxySettings()
28+
// ... other configuration
29+
```
30+
31+
Add the following section to your `appsettings.json`:
1032

1133
```json
1234
{
1335
"OrchardCore_ReverseProxy": {
14-
"ForwardedHeaders": "None",
15-
"KnownNetworks": ["192.168.1.100", "192.168.1.101"],
16-
"KnownProxies": ["192.168.1.200", "192.168.1.201"],
36+
"ForwardedHeaders": "XForwardedFor, XForwardedHost, XForwardedProto",
37+
"KnownNetworks": ["192.168.1.0/24"],
38+
"KnownProxies": ["192.168.1.200", "192.168.1.201"]
1739
}
1840
}
1941
```
2042

21-
For more information please refer to [Configuration](../Configuration/README.md).
43+
## Configuration Options
44+
45+
| Setting | Description | Example Values |
46+
|---------|-------------|----------------|
47+
| `ForwardedHeaders` | Specifies which headers to forward | `None`, `XForwardedFor`, `XForwardedHost`, `XForwardedProto`, `XForwardedPrefix`, `All`, or a combination (comma-separated) |
48+
| `KnownNetworks` | Array of known proxy networks in CIDR notation | `["192.168.1.0/24", "10.0.0.0/8"]` |
49+
| `KnownProxies` | Array of known proxy IP addresses | `["192.168.1.200", "192.168.1.201"]` |
50+
51+
!!! warning
52+
The `KnownNetworks` values must be specified in CIDR notation (e.g., `192.168.1.0/24`).
53+
54+
The `KnownProxies` values must be valid IP addresses.
55+
56+
## Configuration Precedence
57+
58+
When `ConfigureReverseProxySettings()` is called, settings from the configuration file are **merged** with settings configured through the admin UI:
59+
60+
- Configuration file values take **precedence** over admin UI settings for the same properties
61+
- If a property is not specified in the configuration file, the admin UI value is used
62+
- The admin UI will display a warning when configuration file settings are active
63+
64+
**Scenario 1: Admin UI Only**
65+
- `ConfigureReverseProxySettings()` is NOT called
66+
- All settings are managed through the admin UI
67+
- No configuration file override
68+
69+
**Scenario 2: Configuration File Override**
70+
- `ConfigureReverseProxySettings()` is called
71+
- `OrchardCore_ReverseProxy` section exists in appsettings.json
72+
- Configuration file values override admin UI values
73+
- Admin UI shows a warning about the override
74+
75+
**Scenario 3: Partial Override**
76+
- `ConfigureReverseProxySettings()` is called
77+
- Only some settings specified in appsettings.json (e.g., only `KnownProxies`)
78+
- Configuration file values are merged with admin UI values
79+
- Specified values from config file take precedence
80+
81+
## Security Considerations
82+
83+
!!! danger
84+
Improperly configured reverse proxy settings can expose your application to security risks, including IP spoofing and header injection attacks.
85+
86+
When configuring reverse proxy settings:
87+
88+
1. **Always configure KnownProxies and KnownNetworks** when using forwarded headers in production
89+
2. **Only trust headers from known proxies** to prevent header spoofing
90+
3. **Use CIDR notation carefully** to avoid exposing your application to untrusted networks
91+
4. **Test your configuration** to ensure headers are forwarded correctly
92+
93+
## Multi-Tenant Considerations
94+
95+
- Admin UI settings are **tenant-specific**
96+
- Configuration file settings apply to **all tenants** when specified in the main appsettings.json
97+
- For tenant-specific configuration file settings, use the tenant's appsettings.json file in `App_Data/Sites/{tenant}/appsettings.json`
98+
99+
## Additional Resources
100+
101+
For more information, please refer to:
102+
103+
- [Configuration](../Configuration/README.md)
104+
- [Microsoft ASP.NET Core Forwarded Headers Documentation](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer)

0 commit comments

Comments
 (0)