[backend] Reject requests if Host header doesn't match configured WebDomain or AccountDomain
This commit is contained in:
parent
ee449ec751
commit
b72a22b9b0
8 changed files with 52 additions and 7 deletions
|
@ -31,6 +31,7 @@ public sealed class Config {
|
||||||
public string UserAgent => $"Iceshrimp.NET/{Version} (https://{WebDomain})";
|
public string UserAgent => $"Iceshrimp.NET/{Version} (https://{WebDomain})";
|
||||||
|
|
||||||
public required int ListenPort { get; init; } = 3000;
|
public required int ListenPort { get; init; } = 3000;
|
||||||
|
public required string ListenHost { get; init; } = "localhost";
|
||||||
public required string WebDomain { get; init; }
|
public required string WebDomain { get; init; }
|
||||||
public required string AccountDomain { get; init; }
|
public required string AccountDomain { get; init; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public static class NoteQueryableExtensions {
|
||||||
) where T : IEntity {
|
) where T : IEntity {
|
||||||
if (pq.Limit is < 1)
|
if (pq.Limit is < 1)
|
||||||
throw GracefulException.BadRequest("Limit cannot be less than 1");
|
throw GracefulException.BadRequest("Limit cannot be less than 1");
|
||||||
|
|
||||||
if (pq is { SinceId: not null, MinId: not null })
|
if (pq is { SinceId: not null, MinId: not null })
|
||||||
throw GracefulException.BadRequest("Can't use sinceId and minId params simultaneously");
|
throw GracefulException.BadRequest("Can't use sinceId and minId params simultaneously");
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ public static class ServiceExtensions {
|
||||||
services.AddSingleton<RequestBufferingMiddleware>();
|
services.AddSingleton<RequestBufferingMiddleware>();
|
||||||
services.AddSingleton<AuthorizationMiddleware>();
|
services.AddSingleton<AuthorizationMiddleware>();
|
||||||
services.AddSingleton<OauthAuthorizationMiddleware>();
|
services.AddSingleton<OauthAuthorizationMiddleware>();
|
||||||
|
services.AddSingleton<RequestVerificationMiddleware>();
|
||||||
|
|
||||||
// Hosted services = long running background tasks
|
// Hosted services = long running background tasks
|
||||||
// Note: These need to be added as a singleton as well to ensure data consistency
|
// Note: These need to be added as a singleton as well to ensure data consistency
|
||||||
|
|
|
@ -10,6 +10,7 @@ public static class WebApplicationExtensions {
|
||||||
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder app) {
|
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder app) {
|
||||||
// Caution: make sure these are in the correct order
|
// Caution: make sure these are in the correct order
|
||||||
return app.UseMiddleware<ErrorHandlerMiddleware>()
|
return app.UseMiddleware<ErrorHandlerMiddleware>()
|
||||||
|
.UseMiddleware<RequestVerificationMiddleware>()
|
||||||
.UseMiddleware<RequestBufferingMiddleware>()
|
.UseMiddleware<RequestBufferingMiddleware>()
|
||||||
.UseMiddleware<AuthenticationMiddleware>()
|
.UseMiddleware<AuthenticationMiddleware>()
|
||||||
.UseMiddleware<AuthorizationMiddleware>()
|
.UseMiddleware<AuthorizationMiddleware>()
|
||||||
|
|
|
@ -25,6 +25,9 @@ public class ErrorHandlerMiddleware(IOptions<Config.SecuritySection> options, IL
|
||||||
var verbosity = options.Value.ExceptionVerbosity;
|
var verbosity = options.Value.ExceptionVerbosity;
|
||||||
|
|
||||||
if (e is GracefulException ce) {
|
if (e is GracefulException ce) {
|
||||||
|
if (verbosity > ExceptionVerbosity.Basic && ce.OverrideBasic)
|
||||||
|
verbosity = ExceptionVerbosity.Basic;
|
||||||
|
|
||||||
ctx.Response.StatusCode = (int)ce.StatusCode;
|
ctx.Response.StatusCode = (int)ce.StatusCode;
|
||||||
await ctx.Response.WriteAsJsonAsync(new ErrorResponse {
|
await ctx.Response.WriteAsJsonAsync(new ErrorResponse {
|
||||||
StatusCode = ctx.Response.StatusCode,
|
StatusCode = ctx.Response.StatusCode,
|
||||||
|
@ -58,11 +61,17 @@ public class ErrorHandlerMiddleware(IOptions<Config.SecuritySection> options, IL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GracefulException(HttpStatusCode statusCode, string error, string message, string? details = null)
|
public class GracefulException(
|
||||||
|
HttpStatusCode statusCode,
|
||||||
|
string error,
|
||||||
|
string message,
|
||||||
|
string? details = null,
|
||||||
|
bool overrideBasic = false)
|
||||||
: Exception(message) {
|
: Exception(message) {
|
||||||
public readonly string? Details = details;
|
public readonly string? Details = details;
|
||||||
public readonly string Error = error;
|
public readonly string Error = error;
|
||||||
public readonly HttpStatusCode StatusCode = statusCode;
|
public readonly bool OverrideBasic = overrideBasic;
|
||||||
|
public readonly HttpStatusCode StatusCode = statusCode;
|
||||||
|
|
||||||
public GracefulException(HttpStatusCode statusCode, string message, string? details = null) :
|
public GracefulException(HttpStatusCode statusCode, string message, string? details = null) :
|
||||||
this(statusCode, statusCode.ToString(), message, details) { }
|
this(statusCode, statusCode.ToString(), message, details) { }
|
||||||
|
@ -85,7 +94,7 @@ public class GracefulException(HttpStatusCode statusCode, string error, string m
|
||||||
public static GracefulException NotFound(string message, string? details = null) {
|
public static GracefulException NotFound(string message, string? details = null) {
|
||||||
return new GracefulException(HttpStatusCode.NotFound, message, details);
|
return new GracefulException(HttpStatusCode.NotFound, message, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GracefulException BadRequest(string message, string? details = null) {
|
public static GracefulException BadRequest(string message, string? details = null) {
|
||||||
return new GracefulException(HttpStatusCode.BadRequest, message, details);
|
return new GracefulException(HttpStatusCode.BadRequest, message, details);
|
||||||
}
|
}
|
||||||
|
@ -93,6 +102,11 @@ public class GracefulException(HttpStatusCode statusCode, string error, string m
|
||||||
public static GracefulException RecordNotFound() {
|
public static GracefulException RecordNotFound() {
|
||||||
return new GracefulException(HttpStatusCode.NotFound, "Record not found");
|
return new GracefulException(HttpStatusCode.NotFound, "Record not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GracefulException MisdirectedRequest() {
|
||||||
|
return new GracefulException(HttpStatusCode.MisdirectedRequest, HttpStatusCode.MisdirectedRequest.ToString(),
|
||||||
|
"This server is not configured to respond to this request.", null, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ExceptionVerbosity {
|
public enum ExceptionVerbosity {
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Iceshrimp.Backend.Core.Middleware;
|
||||||
|
|
||||||
|
public class RequestVerificationMiddleware(IOptions<Config.InstanceSection> config, IHostEnvironment environment)
|
||||||
|
: IMiddleware {
|
||||||
|
private readonly bool _isDevelopment = environment.IsDevelopment();
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) {
|
||||||
|
if (!IsValid(ctx.Request) && !_isDevelopment)
|
||||||
|
throw GracefulException.MisdirectedRequest();
|
||||||
|
|
||||||
|
await next(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid(HttpRequest rq) {
|
||||||
|
if (rq.Host.Host == config.Value.WebDomain) return true;
|
||||||
|
if (rq.Host.Host == config.Value.AccountDomain && rq.Path.StartsWithSegments("/.well-known")) {
|
||||||
|
if (rq.Path == "/.well-known/webfinger") return true;
|
||||||
|
if (rq.Path == "/.well-known/host-meta") return true;
|
||||||
|
if (rq.Path == "/.well-known/nodeinfo") return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,6 +57,6 @@ app.MapFallbackToPage("/Shared/FrontendSPA");
|
||||||
if (app.Environment.IsDevelopment()) app.UseViteDevMiddleware();
|
if (app.Environment.IsDevelopment()) app.UseViteDevMiddleware();
|
||||||
|
|
||||||
app.Urls.Clear();
|
app.Urls.Clear();
|
||||||
app.Urls.Add($"http://{config.WebDomain}:{config.ListenPort}");
|
app.Urls.Add($"http://{config.ListenHost}:{config.ListenPort}");
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
|
@ -1,5 +1,6 @@
|
||||||
[Instance]
|
[Instance]
|
||||||
ListenPort = 3000
|
ListenPort = 3000
|
||||||
|
ListenHost = localhost
|
||||||
|
|
||||||
;; Caution: changing these settings after initial setup *will* break federation
|
;; Caution: changing these settings after initial setup *will* break federation
|
||||||
WebDomain = shrimp.example.org
|
WebDomain = shrimp.example.org
|
||||||
|
|
Loading…
Add table
Reference in a new issue