mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Move admin UI to separate internal port (5275) for security
- Admin API and static files only accessible on port 5275 - Main proxy port (8080) no longer serves admin endpoints - AdminPortFilter rejects admin requests on wrong port - AdminStaticFilesMiddleware only serves static files on admin port - Port 5275 NOT exposed in Dockerfile or docker-compose by default - Access admin UI via SSH tunnel or by uncommenting port mapping
This commit is contained in:
@@ -24,7 +24,8 @@ RUN mkdir -p /app/downloads
|
|||||||
|
|
||||||
COPY --from=build /app/publish .
|
COPY --from=build /app/publish .
|
||||||
|
|
||||||
|
# Only expose the main proxy port (8080)
|
||||||
|
# Admin UI runs on 5275 but is NOT exposed - access via docker exec or SSH tunnel
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENV ASPNETCORE_URLS=http://+:8080
|
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet", "allstarr.dll"]
|
ENTRYPOINT ["dotnet", "allstarr.dll"]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Options;
|
|||||||
using allstarr.Models.Settings;
|
using allstarr.Models.Settings;
|
||||||
using allstarr.Services.Spotify;
|
using allstarr.Services.Spotify;
|
||||||
using allstarr.Services.Common;
|
using allstarr.Services.Common;
|
||||||
|
using allstarr.Filters;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
@@ -11,9 +12,11 @@ namespace allstarr.Controllers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Admin API controller for the web dashboard.
|
/// Admin API controller for the web dashboard.
|
||||||
/// Provides endpoints for viewing status, playlists, and modifying configuration.
|
/// Provides endpoints for viewing status, playlists, and modifying configuration.
|
||||||
|
/// Only accessible on internal admin port (5275) - not exposed through reverse proxy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/admin")]
|
[Route("api/admin")]
|
||||||
|
[ServiceFilter(typeof(AdminPortFilter))]
|
||||||
public class AdminController : ControllerBase
|
public class AdminController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly ILogger<AdminController> _logger;
|
private readonly ILogger<AdminController> _logger;
|
||||||
|
|||||||
28
allstarr/Filters/AdminPortFilter.cs
Normal file
28
allstarr/Filters/AdminPortFilter.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace allstarr.Filters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Filter that restricts access to admin endpoints to only the admin port (5275).
|
||||||
|
/// This prevents the admin API from being accessed through the main proxy port.
|
||||||
|
/// </summary>
|
||||||
|
public class AdminPortFilter : IActionFilter
|
||||||
|
{
|
||||||
|
private const int AdminPort = 5275;
|
||||||
|
|
||||||
|
public void OnActionExecuting(ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
var requestPort = context.HttpContext.Connection.LocalPort;
|
||||||
|
|
||||||
|
if (requestPort != AdminPort)
|
||||||
|
{
|
||||||
|
context.Result = new NotFoundResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnActionExecuted(ActionExecutedContext context)
|
||||||
|
{
|
||||||
|
// No action needed after execution
|
||||||
|
}
|
||||||
|
}
|
||||||
51
allstarr/Middleware/AdminStaticFilesMiddleware.cs
Normal file
51
allstarr/Middleware/AdminStaticFilesMiddleware.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
namespace allstarr.Middleware;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Middleware that only serves static files on the admin port (5275).
|
||||||
|
/// This keeps the admin UI isolated from the main proxy port.
|
||||||
|
/// </summary>
|
||||||
|
public class AdminStaticFilesMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly StaticFileMiddleware _staticFileMiddleware;
|
||||||
|
private readonly DefaultFilesMiddleware _defaultFilesMiddleware;
|
||||||
|
private const int AdminPort = 5275;
|
||||||
|
|
||||||
|
public AdminStaticFilesMiddleware(
|
||||||
|
RequestDelegate next,
|
||||||
|
IWebHostEnvironment env,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
|
||||||
|
var staticFileOptions = new StaticFileOptions();
|
||||||
|
var defaultFilesOptions = new DefaultFilesOptions();
|
||||||
|
|
||||||
|
_staticFileMiddleware = new StaticFileMiddleware(
|
||||||
|
_next,
|
||||||
|
env,
|
||||||
|
Microsoft.Extensions.Options.Options.Create(staticFileOptions),
|
||||||
|
loggerFactory);
|
||||||
|
|
||||||
|
_defaultFilesMiddleware = new DefaultFilesMiddleware(
|
||||||
|
(ctx) => _staticFileMiddleware.Invoke(ctx),
|
||||||
|
env,
|
||||||
|
Microsoft.Extensions.Options.Options.Create(defaultFilesOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
var port = context.Connection.LocalPort;
|
||||||
|
|
||||||
|
if (port == AdminPort)
|
||||||
|
{
|
||||||
|
// Serve static files on admin port
|
||||||
|
await _defaultFilesMiddleware.Invoke(context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Skip static files on main port
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,11 +43,18 @@ static List<string> DecodeSquidWtfUrls()
|
|||||||
var backendType = builder.Configuration.GetValue<BackendType>("Backend:Type");
|
var backendType = builder.Configuration.GetValue<BackendType>("Backend:Type");
|
||||||
|
|
||||||
// Configure Kestrel for large responses over VPN/Tailscale
|
// Configure Kestrel for large responses over VPN/Tailscale
|
||||||
|
// Also configure admin port on 5275 (internal only, not exposed)
|
||||||
builder.WebHost.ConfigureKestrel(serverOptions =>
|
builder.WebHost.ConfigureKestrel(serverOptions =>
|
||||||
{
|
{
|
||||||
serverOptions.Limits.MaxResponseBufferSize = null; // Disable response buffering limit
|
serverOptions.Limits.MaxResponseBufferSize = null; // Disable response buffering limit
|
||||||
serverOptions.Limits.MaxRequestBodySize = null; // Allow large request bodies
|
serverOptions.Limits.MaxRequestBodySize = null; // Allow large request bodies
|
||||||
serverOptions.Limits.MinResponseDataRate = null; // Disable minimum data rate for slow connections
|
serverOptions.Limits.MinResponseDataRate = null; // Disable minimum data rate for slow connections
|
||||||
|
|
||||||
|
// Main proxy port (exposed)
|
||||||
|
serverOptions.ListenAnyIP(8080);
|
||||||
|
|
||||||
|
// Admin UI port (internal only - do NOT expose through reverse proxy)
|
||||||
|
serverOptions.ListenAnyIP(5275);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add response compression for large JSON responses (helps with Tailscale/VPN MTU issues)
|
// Add response compression for large JSON responses (helps with Tailscale/VPN MTU issues)
|
||||||
@@ -99,6 +106,9 @@ builder.Services.AddHttpContextAccessor();
|
|||||||
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||||
builder.Services.AddProblemDetails();
|
builder.Services.AddProblemDetails();
|
||||||
|
|
||||||
|
// Admin port filter (restricts admin API to port 5275)
|
||||||
|
builder.Services.AddScoped<allstarr.Filters.AdminPortFilter>();
|
||||||
|
|
||||||
// Configuration - register both settings, active one determined by backend type
|
// Configuration - register both settings, active one determined by backend type
|
||||||
builder.Services.Configure<SubsonicSettings>(
|
builder.Services.Configure<SubsonicSettings>(
|
||||||
builder.Configuration.GetSection("Subsonic"));
|
builder.Configuration.GetSection("Subsonic"));
|
||||||
@@ -565,9 +575,8 @@ if (app.Environment.IsDevelopment())
|
|||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
// Serve static files from wwwroot
|
// Serve static files only on admin port (5275)
|
||||||
app.UseDefaultFiles();
|
app.UseMiddleware<allstarr.Middleware.AdminStaticFilesMiddleware>();
|
||||||
app.UseStaticFiles();
|
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
@@ -578,9 +587,6 @@ app.MapControllers();
|
|||||||
// Health check endpoint for monitoring
|
// Health check endpoint for monitoring
|
||||||
app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow }));
|
app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow }));
|
||||||
|
|
||||||
// Admin dashboard redirect
|
|
||||||
app.MapGet("/admin", () => Results.Redirect("/index.html"));
|
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "5274:8080"
|
- "5274:8080"
|
||||||
|
# Admin UI on port 5275 - ONLY expose if you need local access
|
||||||
|
# DO NOT expose through reverse proxy - contains sensitive config
|
||||||
|
# - "5275:5275"
|
||||||
depends_on:
|
depends_on:
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
Reference in New Issue
Block a user