diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Groups.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Groups.cs index 0d0a18b..5183895 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Groups.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Groups.cs @@ -509,7 +509,7 @@ public partial class MareHub _logger.LogCallInfo(MareHubLogger.Args(gid, uid, "Success")); } - [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] + [Authorize(Policy = "Identified")] public async Task> GroupGetBannedUsers(string gid) { _logger.LogCallInfo(MareHubLogger.Args(gid)); diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs index 892f48f..fcf0e9a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs @@ -38,8 +38,6 @@ public partial class MareHub await Task.Delay(1000).ConfigureAwait(false); } - await _authServiceClient.RemoveAuthAsync(new UidMessage() { Uid = userid }).ConfigureAwait(false); - _dbContext.ClientPairs.RemoveRange(ownPairData); await _dbContext.SaveChangesAsync().ConfigureAwait(false); var otherPairData = await _dbContext.ClientPairs.Include(u => u.User) diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs index 86bb79e..6cf0910 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs @@ -20,7 +20,6 @@ namespace MareSynchronosServer.Hubs; public partial class MareHub : Hub, IMareHub { private readonly MareMetrics _mareMetrics; - private readonly AuthService.AuthServiceClient _authServiceClient; private readonly FileService.FileServiceClient _fileServiceClient; private readonly SystemInfoService _systemInfoService; private readonly IHttpContextAccessor _contextAccessor; @@ -33,12 +32,11 @@ public partial class MareHub : Hub, IMareHub private readonly int _maxJoinedGroupsByUser; private readonly int _maxGroupUserCount; - public MareHub(MareMetrics mareMetrics, AuthService.AuthServiceClient authServiceClient, FileService.FileServiceClient fileServiceClient, + public MareHub(MareMetrics mareMetrics, FileService.FileServiceClient fileServiceClient, MareDbContext mareDbContext, ILogger logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor, GrpcClientIdentificationService clientIdentService) { _mareMetrics = mareMetrics; - _authServiceClient = authServiceClient; _fileServiceClient = fileServiceClient; _systemInfoService = systemInfoService; var config = configuration.GetRequiredSection("MareSynchronos"); diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index 56d810f..77160de 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -90,17 +90,6 @@ public class Startup MetricsAPI.GaugeGroupPairsPaused })); - services.AddGrpcClient(c => - { - c.Address = new Uri(mareConfig.GetValue("ServiceAddress")); - }).ConfigureChannel(c => - { - c.ServiceConfig = new ServiceConfig { MethodConfigs = { noRetryConfig } }; - c.HttpHandler = new SocketsHttpHandler() - { - EnableMultipleHttp2Connections = true - }; - }); services.AddGrpcClient(c => { c.Address = new Uri(mareConfig.GetValue("StaticFileServiceAddress")); @@ -120,10 +109,9 @@ public class Startup }; }); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddTransient(); - services.AddHostedService(p => p.GetService()); services.AddHostedService(p => p.GetService()); services.AddDbContextPool(options => @@ -136,17 +124,17 @@ public class Startup options.EnableThreadSafetyChecks(false); }, mareConfig.GetValue("DbContextPoolSize", 1024)); - services.AddAuthentication(SecretKeyGrpcAuthenticationHandler.AuthScheme) - .AddScheme(SecretKeyGrpcAuthenticationHandler.AuthScheme, options => { options.Validate(); }); + services.AddAuthentication(SecretKeyAuthenticationHandler.AuthScheme) + .AddScheme(SecretKeyAuthenticationHandler.AuthScheme, options => { options.Validate(); }); services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() - .AddAuthenticationSchemes(SecretKeyGrpcAuthenticationHandler.AuthScheme) + .AddAuthenticationSchemes(SecretKeyAuthenticationHandler.AuthScheme) .RequireAuthenticatedUser().Build(); options.AddPolicy("Authenticated", policy => { - policy.AddAuthenticationSchemes(SecretKeyGrpcAuthenticationHandler.AuthScheme); + policy.AddAuthenticationSchemes(SecretKeyAuthenticationHandler.AuthScheme); policy.RequireAuthenticatedUser(); }); options.AddPolicy("Identified", policy => diff --git a/MareSynchronosServer/MareSynchronosServices/Authentication/SecretKeyAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosServices/Authentication/SecretKeyAuthenticationHandler.cs deleted file mode 100644 index 53c3d9a..0000000 --- a/MareSynchronosServer/MareSynchronosServices/Authentication/SecretKeyAuthenticationHandler.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using MareSynchronosShared.Data; -using MareSynchronosShared.Metrics; -using MareSynchronosShared.Protos; -using MareSynchronosShared.Utils; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace MareSynchronosServices.Authentication; - -public class SecretKeyAuthenticationHandler -{ - private readonly ILogger _logger; - private readonly MareMetrics _metrics; - private const string Unauthorized = "Unauthorized"; - private readonly ConcurrentDictionary _cachedAuthorizations = new(); - private readonly ConcurrentDictionary _failedAuthorizations = new(); - private readonly int _failedAttemptsForTempBan; - private readonly int _tempBanMinutes; - private readonly List _whitelistedIps = new(); - - public void ClearUnauthorizedUsers() - { - foreach (var item in _cachedAuthorizations.ToArray()) - { - if (item.Value == Unauthorized) - { - _cachedAuthorizations.TryRemove(item.Key, out _); - } - } - } - - public void RemoveAuthentication(string uid) - { - var authorization = _cachedAuthorizations.Where(u => u.Value == uid); - if (authorization.Any()) - { - _cachedAuthorizations.Remove(authorization.First().Key, out _); - } - } - - public async Task AuthenticateAsync(MareDbContext mareDbContext, string ip, string secretKey) - { - _metrics.IncCounter(MetricsAPI.CounterAuthenticationRequests); - - if (string.IsNullOrEmpty(secretKey)) - { - _metrics.IncCounter(MetricsAPI.CounterAuthenticationFailures); - return new AuthReply() { Success = false, Uid = new UidMessage() { Uid = string.Empty } }; - } - - if (_failedAuthorizations.TryGetValue(ip, out var existingFailedAuthorization) && existingFailedAuthorization.FailedAttempts > _failedAttemptsForTempBan) - { - _metrics.IncCounter(MetricsAPI.CounterAuthenticationCacheHits); - _metrics.IncCounter(MetricsAPI.CounterAuthenticationFailures); - - if (existingFailedAuthorization.ResetTask == null) - { - _logger.LogWarning("TempBan {ip} for authorization spam", ip); - - existingFailedAuthorization.ResetTask = Task.Run(async () => - { - await Task.Delay(TimeSpan.FromMinutes(_tempBanMinutes)).ConfigureAwait(false); - - }).ContinueWith((t) => - { - _failedAuthorizations.Remove(ip, out _); - }); - } - return new AuthReply() { Success = false, Uid = new UidMessage() { Uid = string.Empty } }; - } - - var hashedHeader = StringUtils.Sha256String(secretKey); - - bool fromCache = _cachedAuthorizations.TryGetValue(hashedHeader, out string uid); - - if (fromCache && !string.IsNullOrEmpty(uid)) - { - _metrics.IncCounter(MetricsAPI.CounterAuthenticationCacheHits); - - if (uid == Unauthorized) - { - return AuthenticationFailure(ip); - } - } - else - { - uid = (await mareDbContext.Auth.AsNoTracking() - .FirstOrDefaultAsync(m => m.HashedKey == hashedHeader).ConfigureAwait(false))?.UserUID; - - if (string.IsNullOrEmpty(uid)) - { - _cachedAuthorizations[hashedHeader] = Unauthorized; - - return AuthenticationFailure(ip); - } - - _cachedAuthorizations[hashedHeader] = uid; - } - - _metrics.IncCounter(MetricsAPI.CounterAuthenticationSuccesses); - - return new AuthReply() { Success = true, Uid = new UidMessage() { Uid = uid } }; - } - - private AuthReply AuthenticationFailure(string ip) - { - _metrics.IncCounter(MetricsAPI.CounterAuthenticationFailures); - - _logger.LogWarning("Failed authorization from {ip}", ip); - if (!_whitelistedIps.Any(w => ip.Contains(w))) - { - if (_failedAuthorizations.TryGetValue(ip, out var auth)) - { - auth.IncreaseFailedAttempts(); - } - else - { - _failedAuthorizations[ip] = new FailedAuthorization(); - } - } - - return new AuthReply() { Success = false, Uid = new UidMessage() { Uid = string.Empty } }; - } - - public SecretKeyAuthenticationHandler(IConfiguration configuration, ILogger logger, MareMetrics metrics) - { - _logger = logger; - _metrics = metrics; - var config = configuration.GetRequiredSection("MareSynchronos"); - _failedAttemptsForTempBan = config.GetValue("FailedAuthForTempBan", 5); - logger.LogInformation("FailedAuthForTempBan: {num}", _failedAttemptsForTempBan); - _tempBanMinutes = config.GetValue("TempBanDurationInMinutes", 30); - logger.LogInformation("TempBanMinutes: {num}", _tempBanMinutes); - _whitelistedIps = config.GetSection("WhitelistedIps").Get>(); - foreach (var ip in _whitelistedIps) - { - logger.LogInformation("Whitelisted IP: " + ip); - } - } -} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/CleanupService.cs b/MareSynchronosServer/MareSynchronosServices/CleanupService.cs index f05e7ea..c8cc0a8 100644 --- a/MareSynchronosServer/MareSynchronosServices/CleanupService.cs +++ b/MareSynchronosServer/MareSynchronosServices/CleanupService.cs @@ -1,5 +1,4 @@ -using MareSynchronosServices.Authentication; -using MareSynchronosShared.Data; +using MareSynchronosShared.Data; using MareSynchronosShared.Metrics; using MareSynchronosShared.Models; using MareSynchronosShared.Utils; @@ -19,16 +18,14 @@ namespace MareSynchronosServices; public class CleanupService : IHostedService, IDisposable { private readonly MareMetrics metrics; - private readonly SecretKeyAuthenticationHandler _authService; private readonly ILogger _logger; private readonly IServiceProvider _services; private readonly IConfiguration _configuration; private Timer? _timer; - public CleanupService(MareMetrics metrics, SecretKeyAuthenticationHandler authService, ILogger logger, IServiceProvider services, IConfiguration configuration) + public CleanupService(MareMetrics metrics, ILogger logger, IServiceProvider services, IConfiguration configuration) { this.metrics = metrics; - _authService = authService; _logger = logger; _services = services; _configuration = configuration.GetRequiredSection("MareSynchronos"); @@ -119,8 +116,6 @@ public class CleanupService : IHostedService, IDisposable _logger.LogWarning(ex, "Error during Temp Invite purge"); } - _authService.ClearUnauthorizedUsers(); - _logger.LogInformation($"Cleanup complete"); dbContext.SaveChanges(); @@ -137,8 +132,6 @@ public class CleanupService : IHostedService, IDisposable dbContext.Remove(lodestone); } - _authService.RemoveAuthentication(user.UID); - var auth = dbContext.Auth.Single(a => a.UserUID == user.UID); var userFiles = dbContext.Files.Where(f => f.Uploaded && f.Uploader.UID == user.UID).ToList(); diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/MareModule.cs b/MareSynchronosServer/MareSynchronosServices/Discord/MareModule.cs index 8ba4b8c..564bd3a 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/MareModule.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/MareModule.cs @@ -9,7 +9,6 @@ using Microsoft.EntityFrameworkCore; using Discord.WebSocket; using System.Linq; using Prometheus; -using MareSynchronosServices.Authentication; using MareSynchronosShared.Models; using MareSynchronosServices.Identity; using MareSynchronosShared.Metrics; @@ -304,9 +303,6 @@ public class MareModule : InteractionModuleBase await db.Auth.AddAsync(auth).ConfigureAwait(false); await db.SaveChangesAsync().ConfigureAwait(false); - - var authHandler = scope.ServiceProvider.GetService(); - authHandler.RemoveAuthentication(existingLodestoneAuth.User.UID); } } diff --git a/MareSynchronosServer/MareSynchronosServices/Services/AuthenticationService.cs b/MareSynchronosServer/MareSynchronosServices/Services/AuthenticationService.cs deleted file mode 100644 index d82b22e..0000000 --- a/MareSynchronosServer/MareSynchronosServices/Services/AuthenticationService.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Grpc.Core; -using MareSynchronosServices.Authentication; -using MareSynchronosShared.Data; -using MareSynchronosShared.Protos; -using Microsoft.Extensions.Logging; -using System.Threading.Tasks; - -namespace MareSynchronosServices.Services; - -internal class AuthenticationService : AuthService.AuthServiceBase -{ - private readonly ILogger _logger; - private readonly MareDbContext _dbContext; - private readonly SecretKeyAuthenticationHandler _authHandler; - - public AuthenticationService(ILogger logger, MareDbContext dbContext, SecretKeyAuthenticationHandler authHandler) - { - _logger = logger; - _dbContext = dbContext; - _authHandler = authHandler; - } - - public override async Task Authorize(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) - { - await foreach (var input in requestStream.ReadAllAsync(context.CancellationToken).ConfigureAwait(false)) - { - var response = await _authHandler.AuthenticateAsync(_dbContext, input.Ip, input.SecretKey).ConfigureAwait(false); - await responseStream.WriteAsync(response, context.CancellationToken).ConfigureAwait(false); - } - } - - public override Task RemoveAuth(UidMessage request, ServerCallContext context) - { - _logger.LogInformation("Removing Authentication for {uid}", request.Uid); - _authHandler.RemoveAuthentication(request.Uid); - return Task.FromResult(new Empty()); - } - - public override Task ClearUnauthorized(Empty request, ServerCallContext context) - { - _logger.LogInformation("Clearing unauthorized users"); - _authHandler.ClearUnauthorizedUsers(); - return Task.FromResult(new Empty()); - } -} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/Startup.cs b/MareSynchronosServer/MareSynchronosServices/Startup.cs index 328703a..71691bb 100644 --- a/MareSynchronosServer/MareSynchronosServices/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServices/Startup.cs @@ -1,6 +1,4 @@ -using MareSynchronosServices.Authentication; using MareSynchronosServices.Discord; -using MareSynchronosServices.Services; using MareSynchronosShared.Data; using MareSynchronosShared.Metrics; using Microsoft.AspNetCore.Builder; @@ -45,7 +43,6 @@ public class Startup })); services.AddTransient(_ => Configuration); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -63,7 +60,6 @@ public class Startup app.UseEndpoints(endpoints => { - endpoints.MapGrpcService(); endpoints.MapGrpcService(); }); } diff --git a/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthReply.cs b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthReply.cs new file mode 100644 index 0000000..49adf23 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthReply.cs @@ -0,0 +1,3 @@ +namespace MareSynchronosShared.Authentication; + +internal record SecretKeyAuthReply(bool Success, string? Uid); diff --git a/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyGrpcAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs similarity index 66% rename from MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyGrpcAuthenticationHandler.cs rename to MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs index 719bccb..05b0fc3 100644 --- a/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyGrpcAuthenticationHandler.cs +++ b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticationHandler.cs @@ -1,27 +1,27 @@ using System.Security.Claims; using System.Text.Encodings.Web; using MareSynchronosServer; -using MareSynchronosShared.Services; +using MareSynchronosShared.Data; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using ISystemClock = Microsoft.AspNetCore.Authentication.ISystemClock; namespace MareSynchronosShared.Authentication; -public class SecretKeyGrpcAuthenticationHandler : AuthenticationHandler +public class SecretKeyAuthenticationHandler : AuthenticationHandler { public const string AuthScheme = "SecretKeyGrpcAuth"; - private readonly GrpcAuthenticationService _grpcAuthService; + private readonly MareDbContext _mareDbContext; private readonly IHttpContextAccessor _accessor; + private readonly SecretKeyAuthenticatorService secretKeyAuthenticatorService; - public SecretKeyGrpcAuthenticationHandler(IHttpContextAccessor accessor, GrpcAuthenticationService authClient, + public SecretKeyAuthenticationHandler(IHttpContextAccessor accessor, SecretKeyAuthenticatorService secretKeyAuthenticatorService, IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { - this._grpcAuthService = authClient; _accessor = accessor; + this.secretKeyAuthenticatorService = secretKeyAuthenticatorService; } protected override async Task HandleAuthenticateAsync() @@ -33,22 +33,20 @@ public class SecretKeyGrpcAuthenticationHandler : AuthenticationHandler { - new(ClaimTypes.NameIdentifier, uid.Uid), + new(ClaimTypes.NameIdentifier, authResult.Uid), new(ClaimTypes.Authentication, authHeader) }; - var identity = new ClaimsIdentity(claims, nameof(SecretKeyGrpcAuthenticationHandler)); + var identity = new ClaimsIdentity(claims, nameof(SecretKeyAuthenticationHandler)); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); diff --git a/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticatorService.cs b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticatorService.cs new file mode 100644 index 0000000..e35978b --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyAuthenticatorService.cs @@ -0,0 +1,104 @@ +using System.Collections.Concurrent; +using MareSynchronosShared.Data; +using MareSynchronosShared.Utils; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace MareSynchronosShared.Authentication; + +public class SecretKeyAuthenticatorService +{ + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly ILogger _logger; + private readonly ConcurrentDictionary _cachedPositiveResponses = new(StringComparer.Ordinal); + private readonly ConcurrentDictionary _failedAuthorizations = new(StringComparer.Ordinal); + private readonly int _failedAttemptsForTempBan; + private readonly int _tempBanMinutes; + private readonly List _whitelistedIps; + + public SecretKeyAuthenticatorService(IServiceScopeFactory serviceScopeFactory, IConfiguration configuration, ILogger logger) + { + _logger = logger; + var config = configuration.GetRequiredSection("MareSynchronos"); + _failedAttemptsForTempBan = config.GetValue("FailedAuthForTempBan", 5); + logger.LogInformation("FailedAuthForTempBan: {num}", _failedAttemptsForTempBan); + _tempBanMinutes = config.GetValue("TempBanDurationInMinutes", 30); + logger.LogInformation("TempBanMinutes: {num}", _tempBanMinutes); + _whitelistedIps = config.GetSection("WhitelistedIps").Get>(); + foreach (var ip in _whitelistedIps) + { + logger.LogInformation("Whitelisted IP: " + ip); + } + _serviceScopeFactory = serviceScopeFactory; + } + + internal async Task AuthorizeAsync(string ip, string secretKey) + { + if (_cachedPositiveResponses.TryGetValue(secretKey, out var cachedPositiveResponse)) + { + return cachedPositiveResponse; + } + + if (_failedAuthorizations.TryGetValue(ip, out var existingFailedAuthorization) && existingFailedAuthorization.FailedAttempts > _failedAttemptsForTempBan) + { + if (existingFailedAuthorization.ResetTask == null) + { + _logger.LogWarning("TempBan {ip} for authorization spam", ip); + + existingFailedAuthorization.ResetTask = Task.Run(async () => + { + await Task.Delay(TimeSpan.FromMinutes(_tempBanMinutes)).ConfigureAwait(false); + + }).ContinueWith((t) => + { + _failedAuthorizations.Remove(ip, out _); + }); + } + return new(Success: false, Uid: null); + } + + using var scope = _serviceScopeFactory.CreateScope(); + using var context = scope.ServiceProvider.GetService(); + var hashedHeader = StringUtils.Sha256String(secretKey); + var authReply = await context.Auth.AsNoTracking().SingleOrDefaultAsync(u => u.HashedKey == hashedHeader).ConfigureAwait(false); + + SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID); + + if (reply.Success) + { + _cachedPositiveResponses[secretKey] = reply; + _ = Task.Run(async () => + { + await Task.Delay(TimeSpan.FromMinutes(5)).ConfigureAwait(false); + _cachedPositiveResponses.TryRemove(secretKey, out _); + }); + + } + else + { + return AuthenticationFailure(ip); + } + + return reply; + } + + private SecretKeyAuthReply AuthenticationFailure(string ip) + { + _logger.LogWarning("Failed authorization from {ip}", ip); + if (!_whitelistedIps.Any(w => ip.Contains(w, StringComparison.OrdinalIgnoreCase))) + { + if (_failedAuthorizations.TryGetValue(ip, out var auth)) + { + auth.IncreaseFailedAttempts(); + } + else + { + _failedAuthorizations[ip] = new SecretKeyFailedAuthorization(); + } + } + + return new(Success: false, Uid: null); + } +} diff --git a/MareSynchronosServer/MareSynchronosServices/Authentication/FailedAuthorization.cs b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyFailedAuthorization.cs similarity index 59% rename from MareSynchronosServer/MareSynchronosServices/Authentication/FailedAuthorization.cs rename to MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyFailedAuthorization.cs index c6ad45a..3dc19a2 100644 --- a/MareSynchronosServer/MareSynchronosServices/Authentication/FailedAuthorization.cs +++ b/MareSynchronosServer/MareSynchronosShared/Authentication/SecretKeyFailedAuthorization.cs @@ -1,10 +1,6 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace MareSynchronosShared.Authentication; -namespace MareSynchronosServices.Authentication; - -internal class FailedAuthorization +internal record SecretKeyFailedAuthorization { private int failedAttempts = 1; public int FailedAttempts => failedAttempts; @@ -13,4 +9,4 @@ internal class FailedAuthorization { Interlocked.Increment(ref failedAttempts); } -} \ No newline at end of file +} diff --git a/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto b/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto index 0f5b156..1bb913c 100644 --- a/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto +++ b/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto @@ -4,12 +4,6 @@ option csharp_namespace = "MareSynchronosShared.Protos"; package mareservices; -service AuthService { - rpc Authorize (stream AuthRequest) returns (stream AuthReply); - rpc RemoveAuth (UidMessage) returns (Empty); - rpc ClearUnauthorized (Empty) returns (Empty); -} - service FileService { rpc UploadFile (stream UploadFileRequest) returns (Empty); rpc GetFileSizes (FileSizeRequest) returns (FileSizeResponse); diff --git a/MareSynchronosServer/MareSynchronosShared/Services/GrpcAuthenticationService.cs b/MareSynchronosServer/MareSynchronosShared/Services/GrpcAuthenticationService.cs deleted file mode 100644 index 40cf5b1..0000000 --- a/MareSynchronosServer/MareSynchronosShared/Services/GrpcAuthenticationService.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Collections.Concurrent; -using MareSynchronosShared.Protos; -using Microsoft.Extensions.Logging; - -namespace MareSynchronosShared.Services; - -public class GrpcAuthenticationService : GrpcBaseService -{ - private record AuthRequestInternal - { - public AuthRequest Request { get; set; } - public long Id { get; set; } - } - - private record AuthResponseCache - { - public AuthReply Response { get; set; } - public DateTime WrittenTo { get; set; } - } - - private readonly AuthService.AuthServiceClient _authClient; - private readonly ConcurrentQueue _requestQueue = new(); - private readonly ConcurrentDictionary _authReplies = new(); - private readonly ConcurrentDictionary _cachedPositiveResponses = new(StringComparer.Ordinal); - private long _requestId = 0; - - public GrpcAuthenticationService(ILogger logger, AuthService.AuthServiceClient authClient) : base(logger) - { - _authClient = authClient; - } - - public async Task AuthorizeAsync(string ip, string secretKey) - { - if (_cachedPositiveResponses.TryGetValue(secretKey, out var cachedPositiveResponse)) - { - if (cachedPositiveResponse.WrittenTo.AddMinutes(5) < DateTime.UtcNow) return cachedPositiveResponse.Response; - _cachedPositiveResponses.Remove(secretKey, out _); - } - - var id = Interlocked.Increment(ref _requestId); - _requestQueue.Enqueue(new AuthRequestInternal() - { - Id = id, - Request = new AuthRequest() - { - Ip = ip, - SecretKey = secretKey, - } - }); - - using CancellationTokenSource cts = new(TimeSpan.FromSeconds(30)); - AuthReply response = null; - - while (!GrpcIsFaulty && !cts.IsCancellationRequested && !_authReplies.TryRemove(id, out response)) - { - await Task.Delay(10, cts.Token).ConfigureAwait(false); - } - - if (response?.Success ?? false) - { - _cachedPositiveResponses[secretKey] = new AuthResponseCache - { - Response = response, - WrittenTo = DateTime.UtcNow - }; - } - - return response ?? new AuthReply - { - Success = false, - }; - } - - public async Task GrpcAuthStream(CancellationToken token) - { - try - { - using var stream = _authClient.Authorize(cancellationToken: token); - while (!token.IsCancellationRequested) - { - while (_requestQueue.TryDequeue(out var request)) - { - await stream.RequestStream.WriteAsync(request.Request, token).ConfigureAwait(false); - await stream.ResponseStream.MoveNext(token).ConfigureAwait(false); - _authReplies[request.Id] = stream.ResponseStream.Current; - } - - await Task.Delay(10, token).ConfigureAwait(false); - } - } - catch - { - SetGrpcFaulty(); - } - } - - protected override Task OnGrpcRestore() - { - return Task.CompletedTask; - } - - protected override Task PostStartStream() - { - return Task.CompletedTask; - } - - protected override Task PreStartStream() - { - _requestQueue.Clear(); - _authReplies.Clear(); - return Task.CompletedTask; - } - - protected override Task StartAsyncInternal(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - protected override Task StartStream(CancellationToken ct) - { - _ = GrpcAuthStream(ct); - return Task.CompletedTask; - } - - protected override Task StopAsyncInternal(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } -} diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Program.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Program.cs index 8c8d8f9..8b791bc 100644 --- a/MareSynchronosServer/MareSynchronosStaticFilesServer/Program.cs +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Program.cs @@ -22,5 +22,9 @@ public class Program { webBuilder.UseContentRoot(AppContext.BaseDirectory); webBuilder.UseStartup(); + webBuilder.ConfigureKestrel(opt => + { + opt.Limits.MaxConcurrentConnections = 5000; + }); }); } \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Startup.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Startup.cs index 7eec83a..6baa83b 100644 --- a/MareSynchronosServer/MareSynchronosStaticFilesServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Startup.cs @@ -2,8 +2,6 @@ using Grpc.Net.Client.Configuration; using MareSynchronosShared.Authentication; using MareSynchronosShared.Data; using MareSynchronosShared.Metrics; -using MareSynchronosShared.Protos; -using MareSynchronosShared.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; @@ -50,7 +48,6 @@ public class Startup } }; - if (!isSecondary) { services.AddSingleton(new MareMetrics(new List @@ -63,15 +60,7 @@ public class Startup services.AddHostedService(); } - services.AddSingleton(); - services.AddGrpcClient(c => - { - c.Address = new Uri(mareSettings.GetValue("ServiceAddress")); - }).ConfigureChannel(c => - { - c.ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }; - }); - + services.AddSingleton(); services.AddDbContextPool(options => { options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), builder => @@ -81,13 +70,11 @@ public class Startup options.EnableThreadSafetyChecks(false); }, mareSettings.GetValue("DbContextPoolSize", 1024)); - services.AddHostedService(p => p.GetService()); - services.AddAuthentication(options => { - options.DefaultScheme = SecretKeyGrpcAuthenticationHandler.AuthScheme; + options.DefaultScheme = SecretKeyAuthenticationHandler.AuthScheme; }) - .AddScheme(SecretKeyGrpcAuthenticationHandler.AuthScheme, options => { }); + .AddScheme(SecretKeyAuthenticationHandler.AuthScheme, options => { }); services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); services.AddGrpc(o => @@ -117,7 +104,8 @@ public class Startup { FileProvider = new PhysicalFileProvider(Configuration.GetRequiredSection("MareSynchronos")["CacheDirectory"]), RequestPath = "/cache", - ServeUnknownFileTypes = true + ServeUnknownFileTypes = true, + }); if (!isSecondary)