Skip to content

Commit

Permalink
override rate-limiter and multiaccount manager when using jwt token
Browse files Browse the repository at this point in the history
  • Loading branch information
area363 committed Dec 3, 2024
1 parent 23e9524 commit c3cc560
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 16 deletions.
1 change: 1 addition & 0 deletions NineChronicles.Headless.Executable/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ public async Task Run(
try
{
IHostBuilder hostBuilder = Host.CreateDefaultBuilder();
hostBuilder.ConfigureAppConfiguration(builder => builder.AddConfiguration(configuration));

var standaloneContext = new StandaloneContext
{
Expand Down
21 changes: 11 additions & 10 deletions NineChronicles.Headless/GraphQLService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,17 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
}

// Capture requests
app.UseMiddleware<HttpCaptureMiddleware>();

app.UseRouting();
app.UseAuthorization();
if (Convert.ToBoolean(Configuration.GetSection("IpRateLimiting")["EnableEndpointRateLimiting"]))
{
app.UseMiddleware<CustomRateLimitMiddleware>();
app.UseMiddleware<IpBanMiddleware>();
app.UseMvc();
}

if (Convert.ToBoolean(Configuration.GetSection("MultiAccountManaging")["EnableManaging"]))
{
ConcurrentDictionary<string, HashSet<Address>> ipSignerList = new();
Expand All @@ -229,7 +240,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
Publisher);
}

app.UseMiddleware<HttpCaptureMiddleware>();

app.UseMiddleware<LocalAuthenticationMiddleware>();
if (Convert.ToBoolean(Configuration.GetSection("Jwt")["EnableJwtAuthentication"]))
Expand All @@ -246,15 +256,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseCors("AllowAllOrigins");
}

app.UseRouting();
app.UseAuthorization();
if (Convert.ToBoolean(Configuration.GetSection("IpRateLimiting")["EnableEndpointRateLimiting"]))
{
app.UseMiddleware<CustomRateLimitMiddleware>();
app.UseMiddleware<IpBanMiddleware>();
app.UseMvc();
}

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
Expand Down
20 changes: 17 additions & 3 deletions NineChronicles.Headless/Middleware/CustomRateLimitMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,33 @@
using NineChronicles.Headless.Properties;
using Serilog;
using ILogger = Serilog.ILogger;
using System.Linq;
using Microsoft.Extensions.Configuration;

namespace NineChronicles.Headless.Middleware
{

public class CustomRateLimitMiddleware : RateLimitMiddleware<CustomIpRateLimitProcessor>
{
private readonly ILogger _logger;
private readonly IRateLimitConfiguration _config;
private readonly IOptions<CustomIpRateLimitOptions> _options;
private readonly string _whitelistedIp;
private readonly string _jwtKey;

public CustomRateLimitMiddleware(RequestDelegate next,
IProcessingStrategy processingStrategy,
IOptions<CustomIpRateLimitOptions> options,
IIpPolicyStore policyStore,
IRateLimitConfiguration config)
IRateLimitConfiguration config,
Microsoft.Extensions.Configuration.IConfiguration configuration)
: base(next, options?.Value, new CustomIpRateLimitProcessor(options?.Value!, policyStore, processingStrategy), config)
{
_config = config;
_options = options!;
_logger = Log.Logger.ForContext<CustomRateLimitMiddleware>();
_jwtKey = configuration["Jwt:Key"] ?? string.Empty;
_whitelistedIp = configuration.GetSection("IpRateLimiting:IpWhitelist")?.Get<string[]>()?.FirstOrDefault() ?? "127.0.0.1";
}

protected override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
Expand All @@ -45,14 +53,20 @@ public override async Task<ClientRequestIdentity> ResolveIdentityAsync(HttpConte

if (httpContext.Request.Protocol == "HTTP/1.1")
{
var body = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
var body = httpContext.Items["RequestBody"]!.ToString()!;
httpContext.Request.Body.Seek(0, SeekOrigin.Begin);
if (body.Contains("stageTransaction"))
{
identity.Path = "/graphql/stagetransaction";
}
}

return identity;
// Check for JWT secret key in headers
if (httpContext.Request.Headers.TryGetValue("Authorization", out var authHeaderValue) &&
!string.IsNullOrEmpty(_jwtKey) &&
authHeaderValue.ToString().Equals($"Bearer {_jwtKey}", System.StringComparison.OrdinalIgnoreCase))
{
identity.ClientIp = _whitelistedIp;
}

return identity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public async Task InvokeAsync(HttpContext context)
context.Request.EnableBuffering();
var remoteIp = context.Connection.RemoteIpAddress;
var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
context.Items["RequestBody"] = body;
_logger.Information("[GRAPHQL-REQUEST-CAPTURE] IP: {IP} Method: {Method} Endpoint: {Path} {Body}",
remoteIp, context.Request.Method, context.Request.Path, body);
context.Request.Body.Seek(0, SeekOrigin.Begin);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

namespace NineChronicles.Headless.Middleware
{
using Microsoft.Extensions.Configuration;

public class HttpMultiAccountManagementMiddleware
{
private static readonly ConcurrentDictionary<Address, DateTimeOffset> MultiAccountTxIntervalTracker = new();
Expand All @@ -27,20 +29,25 @@ public class HttpMultiAccountManagementMiddleware
private readonly ConcurrentDictionary<string, HashSet<Address>> _ipSignerList;
private readonly IOptions<MultiAccountManagerProperties> _options;
private ActionEvaluationPublisher _publisher;
private readonly string _whitelistedIp;
private readonly string _jwtKey;

public HttpMultiAccountManagementMiddleware(
RequestDelegate next,
StandaloneContext standaloneContext,
ConcurrentDictionary<string, HashSet<Address>> ipSignerList,
IOptions<MultiAccountManagerProperties> options,
ActionEvaluationPublisher publisher)
ActionEvaluationPublisher publisher,
Microsoft.Extensions.Configuration.IConfiguration configuration)
{
_next = next;
_logger = Log.Logger.ForContext<HttpMultiAccountManagementMiddleware>();
_standaloneContext = standaloneContext;
_ipSignerList = ipSignerList;
_options = options;
_publisher = publisher;
_jwtKey = configuration["Jwt:Key"] ?? string.Empty;
_whitelistedIp = configuration.GetSection("IpRateLimiting:IpWhitelist")?.Get<string[]>()?.FirstOrDefault() ?? "127.0.0.1";
}

private static void ManageMultiAccount(Address agent)
Expand All @@ -58,9 +65,18 @@ public async Task InvokeAsync(HttpContext context)
// Prevent to harm HTTP/2 communication.
if (context.Request.Protocol == "HTTP/1.1")
{
context.Request.EnableBuffering();
var remoteIp = context.Connection.RemoteIpAddress!.ToString();
var body = await new StreamReader(context.Request.Body).ReadToEndAsync();

// Skip if the remote IP is whitelisted
if (context.Request.Headers.TryGetValue("Authorization", out var authHeaderValue) &&
!string.IsNullOrEmpty(_jwtKey) &&
authHeaderValue.ToString().Equals($"Bearer {_jwtKey}", System.StringComparison.OrdinalIgnoreCase))
{
await _next(context);
return;
}

var body = context.Items["RequestBody"]!.ToString()!;
context.Request.Body.Seek(0, SeekOrigin.Begin);
if (_options.Value.EnableManaging && body.Contains("stageTransaction"))
{
Expand Down

0 comments on commit c3cc560

Please sign in to comment.