Skip to content

Commit

Permalink
fix: unreasonable rate limiting strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
GZTimeWalker committed May 6, 2023
1 parent 7806dde commit 27c8ffc
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 18 deletions.
46 changes: 33 additions & 13 deletions src/GZCTF/Middlewares/RateLimiter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Globalization;
using System.Net;
using System.Security.Claims;
using System.Threading.RateLimiting;
using CTFServer.Utils;
using Microsoft.AspNetCore.RateLimiting;
Expand Down Expand Up @@ -39,13 +40,13 @@ public static RateLimiterOptions GetRateLimiterOptions()
=> new RateLimiterOptions()
{
RejectionStatusCode = StatusCodes.Status429TooManyRequests,
GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(context =>
GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
{
IPAddress? remoteIPaddress = context?.Connection?.RemoteIpAddress;
string? userId = context?.User.FindFirstValue(ClaimTypes.NameIdentifier);

if (remoteIPaddress is not null && !IPAddress.IsLoopback(remoteIPaddress))
if (userId is not null)
{
return RateLimitPartition.GetSlidingWindowLimiter<IPAddress>(remoteIPaddress, key => new()
return RateLimitPartition.GetSlidingWindowLimiter(userId, key => new()
{
PermitLimit = 150,
Window = TimeSpan.FromMinutes(1),
Expand All @@ -54,10 +55,29 @@ public static RateLimiterOptions GetRateLimiterOptions()
SegmentsPerWindow = 6,
});
}
else

string? remoteIPaddress = context?.Connection?.RemoteIpAddress?.ToString();

if (context is not null && context.Request.Headers.TryGetValue("X-Forwarded-For", out var value))
{
// note that we consider the previous reverse proxy server credible
remoteIPaddress = value.ToString().Split(',', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
}

if (remoteIPaddress is not null && IPAddress.TryParse(remoteIPaddress, out IPAddress? address))
{
return RateLimitPartition.GetNoLimiter<IPAddress>(IPAddress.Loopback);
if (!IPAddress.IsLoopback(address))
return RateLimitPartition.GetSlidingWindowLimiter(remoteIPaddress, key => new()
{
PermitLimit = 150,
Window = TimeSpan.FromMinutes(1),
QueueLimit = 60,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
SegmentsPerWindow = 6,
});
}

return RateLimitPartition.GetNoLimiter(IPAddress.Loopback.ToString());
}),
OnRejected = (context, cancellationToken) =>
{
Expand All @@ -79,24 +99,24 @@ public static RateLimiterOptions GetRateLimiterOptions()
.AddConcurrencyLimiter(nameof(LimitPolicy.Concurrency), options =>
{
options.PermitLimit = 1;
options.QueueLimit = 5;
options.QueueLimit = 20;
})
.AddFixedWindowLimiter(nameof(LimitPolicy.Register), options =>
{
options.PermitLimit = 10;
options.PermitLimit = 20;
options.Window = TimeSpan.FromSeconds(150);
})
.AddTokenBucketLimiter(nameof(LimitPolicy.Container), options =>
{
options.TokenLimit = 4;
options.TokensPerPeriod = 2;
options.TokenLimit = 120;
options.TokensPerPeriod = 30;
options.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
})
.AddTokenBucketLimiter(nameof(LimitPolicy.Submit), options =>
{
options.TokenLimit = 3;
options.TokensPerPeriod = 1;
options.ReplenishmentPeriod = TimeSpan.FromSeconds(20);
options.TokenLimit = 60;
options.TokensPerPeriod = 30;
options.ReplenishmentPeriod = TimeSpan.FromSeconds(5);
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/Services/ConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ private static void MapConfigsInternal(string key, HashSet<Config> configs, Type
TypeConverter converter = TypeDescriptor.GetConverter(type);
if (type == typeof(string) || type.IsValueType)
{
configs.Add(new(key, converter.ConvertToString(value) ?? String.Empty));
configs.Add(new(key, converter.ConvertToString(value) ?? string.Empty));
}
else if (type.IsClass)
{
Expand Down
8 changes: 4 additions & 4 deletions src/GZCTF/Services/DockerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public DockerService(IOptions<ContainerProvider> _options, IOptions<RegistryConf
options = _options.Value.DockerConfig ?? new DockerConfig();
publicEntry = _options.Value.PublicEntry;
logger = _logger;
DockerClientConfiguration cfg = string.IsNullOrEmpty(this.options.Uri) ? new() : new(new Uri(this.options.Uri));
DockerClientConfiguration cfg = string.IsNullOrEmpty(options.Uri) ? new() : new(new Uri(options.Uri));

// TODO: Docker Auth Required
dockerClient = cfg.CreateClient();
Expand All @@ -36,7 +36,7 @@ public DockerService(IOptions<ContainerProvider> _options, IOptions<RegistryConf
};
}

logger.SystemLog($"Docker 服务已启动 ({(string.IsNullOrEmpty(this.options.Uri) ? "localhost" : this.options.Uri)})", TaskStatus.Success, LogLevel.Debug);
logger.SystemLog($"Docker 服务已启动 ({(string.IsNullOrEmpty(options.Uri) ? "localhost" : options.Uri)})", TaskStatus.Success, LogLevel.Debug);
}

public Task<Container?> CreateContainerAsync(ContainerConfig config, CancellationToken token = default)
Expand All @@ -63,7 +63,7 @@ public async Task DestroyContainerAsync(Container container, CancellationToken t
}
else
{
logger.SystemLog($"容器 {container.ContainerId} 删除失败, 状态:{e.StatusCode.ToString()}", TaskStatus.Failed, LogLevel.Warning);
logger.SystemLog($"容器 {container.ContainerId} 删除失败, 状态:{e.StatusCode}", TaskStatus.Failed, LogLevel.Warning);
logger.SystemLog($"容器 {container.ContainerId} 删除失败, 响应:{e.ResponseBody}", TaskStatus.Failed, LogLevel.Error);
return;
}
Expand Down Expand Up @@ -139,8 +139,8 @@ private ServiceCreateParameters GetServiceCreateParameters(ContainerConfig confi
public async Task<Container?> CreateContainerWithSwarm(ContainerConfig config, CancellationToken token = default)
{
var parameters = GetServiceCreateParameters(config);
ServiceCreateResponse? serviceRes = null;
int retry = 0;
ServiceCreateResponse? serviceRes;
CreateContainer:
try
{
Expand Down

0 comments on commit 27c8ffc

Please sign in to comment.