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

Added reverse proxy support #211

Merged
merged 2 commits into from
Dec 13, 2023
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
57 changes: 57 additions & 0 deletions Models/ReverseProxySettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace Fenrus.Models;

/// <summary>
/// Configuration object that maps to the ReverseProxySettings section in appsettings.json
/// </summary>
public class ReverseProxySettings
{
/// <summary>
/// If the request headers should be printed to the console when being redirected to the
/// authentication provider.
/// </summary>
public bool DebugPrintRequestHeaders { get; set; }

/// <summary>
/// If the forwarded headers should be used.
/// </summary>
public bool UseForwardedHeaders { get; set; }

/// <summary>
/// The IP-addresses of known proxies.
/// </summary>
public string[] KnownProxies { get; set; } = Array.Empty<string>();

/// <summary>
/// The IP-address specification of a known network in IPv4 format
/// For example: 192.168.2.0/24, which will allow all IP-addresses from
/// 192.168.2.1 - 192.168.2.254
/// </summary>
public KnownNetwork KnownIpv4Network { get; set; } = new();

/// <summary>
/// The IP-address specification of a known network in IPv6 format
/// For example: 2001:db8::/32, which will allow all IP-addresses from
/// 2001:db8:0:0:0:0:0:0 - 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
/// </summary>
public KnownNetwork KnownIpv6Network { get; set; } = new();

}

public class KnownNetwork
{
/// <summary>
/// If the known network should be added to the list of known networks.
/// </summary>
public bool Enabled { get; set; }

/// <summary>
/// The IP-address of the known network.
/// For example: 192.168.2.0 or 2001:db8::
/// </summary>
public string IpAddress { get; set; }

/// <summary>
/// The prefix length of the known network. For example: 24
/// </summary>
public int PrefixLength { get; set; }
}
54 changes: 50 additions & 4 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System.Net;
using Blazored.Toast;
using Fenrus;
using Fenrus.Models;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.HttpOverrides;
using NUglify.Helpers;

if (args?.Any() == true && args[0] == "--init-config")
Expand All @@ -23,8 +26,18 @@
StartUpHelper.Run();

var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddEnvironmentVariables();
builder.Services.AddControllersWithViews();
builder.Services.AddMvc();

//Gets the reverse proxy settings from the appsettings.json file
//to check if the app is running behind a reverse proxy
ReverseProxySettings reverseProxySettings = builder.Configuration.GetSection(nameof(ReverseProxySettings)).Get<ReverseProxySettings>();

if(reverseProxySettings.UseForwardedHeaders)
{
ConfigureUsingForwardedHeaders(builder, reverseProxySettings);
}
builder.Services.AddWebOptimizer(pipeline =>
{
pipeline.MinifyJsFiles("js/**/*.js");
Expand Down Expand Up @@ -83,16 +96,18 @@
options.ClientId = system.OAuthStrategyClientId;
options.ClientSecret = system.OAuthStrategySecret;
options.Authority = system.OAuthStrategyIssuerBaseUrl;

options.Scope.Add("email");
options.Scope.Add("openid");
options.Scope.Add("profile");
options.GetClaimsFromUserInfoEndpoint = true;
options.DisableTelemetry = true;
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Prompt = "login";
return Task.CompletedTask;
//Added option to debug request headers for reverse proxy
//Sometimes it can be difficult to find out if X-Forwarded-X headers are set correctly
if(reverseProxySettings.DebugPrintRequestHeaders)
Logger.DLog($"Request headers: {string.Join(Environment.NewLine, context.Request.Headers)}");
return Task.FromResult(0);
};
});
}
Expand All @@ -109,6 +124,8 @@

var app = builder.Build();

if(reverseProxySettings.UseForwardedHeaders)
app.UseForwardedHeaders();
bool debug = Environment.GetEnvironmentVariable("DEBUG") == "1";
app.UseWhen(context =>
{
Expand Down Expand Up @@ -184,4 +201,33 @@
Logger.ILog($"Fenrus v{Fenrus.Globals.Version} started");
app.Run();
Logger.ILog($"Fenrus v{Fenrus.Globals.Version} stopped");
workers.ForEach(x => x.Stop());
workers.ForEach(x => x.Stop());

// Configure the app to use forwarded headers
//If the app is running behind a reverse proxy, the app needs to be configured to use the forwarded headers
//This means that X-Forwarded-For and X-Forwarded-Proto headers are used to determine if the request goes over https,
//but is using ssl termination
void ConfigureUsingForwardedHeaders(WebApplicationBuilder webApplicationBuilder,
ReverseProxySettings reverseProxySettings1)
{
webApplicationBuilder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
foreach (var knownProxy in reverseProxySettings1.KnownProxies)
options.KnownProxies.Add(IPAddress.Parse($"{knownProxy}"));
if (reverseProxySettings1.KnownIpv4Network.Enabled)
{
if (string.IsNullOrWhiteSpace(reverseProxySettings1.KnownIpv4Network.IpAddress) ||
reverseProxySettings1.KnownIpv4Network.PrefixLength == 0)
throw new InvalidOperationException("Invalid IPv4 network configuration");
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse(reverseProxySettings1.KnownIpv4Network.IpAddress),
reverseProxySettings1.KnownIpv4Network.PrefixLength));
}

if (!reverseProxySettings1.KnownIpv6Network.Enabled) return;
if(string.IsNullOrWhiteSpace(reverseProxySettings1.KnownIpv6Network.IpAddress) || reverseProxySettings1.KnownIpv6Network.PrefixLength == 0)
throw new InvalidOperationException("Invalid IPv6 network configuration");
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse(reverseProxySettings1.KnownIpv6Network.IpAddress), reverseProxySettings1.KnownIpv6Network.PrefixLength));
});
}
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ All the configuration is saved into LiteDB file, Fenrus.db. There is an encryp

---

### Reverse Proxy configuration

If you want to host Fenrus behind a ReverseProxy in combination with oauth authentication, you need to make sure that you are passing X-Forwarded-Proto and X-Forwarded-For headers from your reverse proxy to Fenrus.
Fenrus will use these headers to generate the correct urls for oauth authentication.

The following environment variables can be set to change the reverse proxy settings:

| Variable | Description | Default | Example |
|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|------------|----------------------------------|
| ReverseProxySettings__UseForwardedHeaders | Enable reverse proxy support, all the below settings are negated, if this setting is false | false | true |
| ReverseProxySettings__DebugPrintRequestHeaders | Print request headers to console, to verify if X-Forwarded-X headers are present | false | true |
| ReverseProxySettings__KnownProxies | String array of trusted proxy IP addresses | [] | ["192.168.2.15", "192.168.2.16"] |
| ReverseProxySettings__KnownIPv4Network__Enabled | Enable adding a Known IPv4 Network | true | false |
| ReverseProxySettings__KnownIPv4Network__IpAddress | Network network to add, for example 192.168.2.0/24 (the prefixLength, will be added in the below variable | "10.0.0.0" | "192.168.2.0" |
| ReverseProxySettings__KnownIPv4Network__PrefixLength | The prefix length for the Known network to add. | 8 | 24 |
| ReverseProxySettings__KnownIPv6Network__Enabled | Enable adding a Known IPv6 Network | true | false |
| ReverseProxySettings__KnownIPv6Network__IpAddress | Network network to add, for example fe80::/24 (the prefixLength, will be added in the below variable | "fe80::" | "fe80::" |
| ReverseProxySettings__KnownIPv6Network__PrefixLength | The prefix length for the Known network to add. | 64 | 24 |

## Getting Started

First, you need to register a user, you can do this on the login page by entering a username and password and clicking "Register" if no user with that username exists, a new one will be created.
Expand Down
17 changes: 16 additions & 1 deletion appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,20 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"ReverseProxySettings": {
"DebugPrintRequestHeaders": false,
"UseForwardedHeaders": false,
"KnownProxies": [],
"KnownIPv4Network": {
"Enabled": true,
"IpAddress": "10.0.0.0",
"PrefixLength": 8
},
"KnownIPv6Network": {
"Enabled": false,
"IpAddress": "fe80::",
"PrefixLength": 64
}
}
}