private void SessionManager_AuthenticationFailed(object sender, GenericEventArgs <AuthenticationRequest> e) { var config = Plugin.Instance.Configuration; if (!config.EnableFirewallBlock) { return; } if (IsLocalNetworkIp(e.Argument.RemoteAddress) && config.IgnoreInternalFailedLoginAttempts) { return; } var connection = CheckConnectionAttempt(e.Argument, config).Result; if (!connection.IsBanned) { return; } if (config.BannedConnections.Exists(c => c == connection)) { return; } connection.BannedDateTime = DateTime.Now; connection.IsBanned = true; connection.RuleName = "Emby_Authentication_Request_Blocked_" + config.RuleNameCount; connection.Id = "Emby_Authentication_Request_Blocked_" + config.RuleNameCount; config.RuleNameCount += 1; config.BannedConnections.Add(connection); Plugin.Instance.UpdateConfiguration(config); var result = FirewallController.AddFirewallRule(connection); Logger.Info($"Firewall Rule {connection.RuleName} added for Ip {connection.Ip} - {result}"); ActivityManager.Create(new ActivityLogEntry() { Date = connection.BannedDateTime, Id = Convert.ToInt64(000 + config.RuleNameCount), Name = "Firewall Blocked Ip", Severity = LogSeverity.Warn, Overview = $"{connection.Ip} blocked: too many failed login attempts on {connection.UserAccountName}'s account, from ISP: {connection.Isp}, on device {connection.DeviceName}", ShortOverview = $"{connection.Ip}: too many failed login attempts.", Type = "Alert" }); //Remove the connection data from our ConnectionAttemptLog list because they are banned. We no longer have to track their attempts FailedAuthenticationAudit.Remove(connection); SessionManager.SendMessageToAdminSessions("FirewallAdded", connection, CancellationToken.None); }
private async Task <Connection> CheckConnectionAttempt(AuthenticationRequest authenticationRequest, PluginConfiguration config) { Connection connection = null; if (FailedAuthenticationAudit.Exists(a => Equals(a.Ip, authenticationRequest.RemoteAddress.ToString()))) { connection = FailedAuthenticationAudit.FirstOrDefault(c => Equals(c.Ip, authenticationRequest.RemoteAddress.ToString())); var connectionLoginAttemptThreshold = config.ConnectionAttemptsBeforeBan != 0 ? config.ConnectionAttemptsBeforeBan : 3; //If this connection has tried and failed, and is not Banned - but has waited over thirty seconds to try again - reset the attempt count and clear FailedAuthDateTimes List. if (DateTime.UtcNow > connection?.FailedAuthDateTimes.LastOrDefault().AddSeconds(30)) { connection.FailedAuthDateTimes.Clear(); connection.LoginAttempts = 0; } //Log the attempt if (connection?.LoginAttempts < connectionLoginAttemptThreshold) { connection.LoginAttempts += 1; connection.FailedAuthDateTimes.Add(DateTime.UtcNow); return(connection); } //Tried to many times in a row, and too quickly -Ban the IP - could be a brute force attack. if (connection?.FailedAuthDateTimes.FirstOrDefault() > DateTime.UtcNow.AddSeconds(-30)) { connection.IsBanned = true; return(connection); } } else { ReverseLookupData targetData = null; if (Plugin.Instance.Configuration.EnableGeoIp && !IsLocalNetworkIp(authenticationRequest.RemoteAddress)) { targetData = await Target.GetLocation(authenticationRequest.RemoteAddress.ToString()); } // ReSharper disable once ComplexConditionExpression connection = new Connection { FlagIconUrl = targetData is null ? string.Empty : targetData.countryFlag, Isp = targetData is null ? string.Empty : targetData.isp, Ip = authenticationRequest.RemoteAddress.ToString(), DeviceName = authenticationRequest.DeviceName, UserAccountName = authenticationRequest.Username, Proxy = targetData?.proxy ?? false, ServiceProvider = targetData is null ? string.Empty : targetData.isp, Longitude = targetData?.lon ?? 0, Latitude = targetData?.lat ?? 0, LoginAttempts = 1, IsBanned = false, Region = targetData?.regionName ?? string.Empty, FailedAuthDateTimes = new List <DateTime> { DateTime.UtcNow } }; FailedAuthenticationAudit.Add(connection); } return(connection); }