private void RunConfigBanTest(string key, string value, string banIP, string noBanIP) { // turn on clear failed logins upon success login using IPBanConfig.TempConfigChanger configChanger = new IPBanConfig.TempConfigChanger(service, xml => { return(IPBanConfig.ChangeConfigAppSetting(xml, key, value)); }, out string newConfig); service.AddIPAddressLogEvents(new IPAddressLogEvent[] { // should be banned new IPAddressLogEvent(banIP, "user1", "RDP", 999, IPAddressEventType.FailedLogin), // whitelisted new IPAddressLogEvent(noBanIP, "user2", "RDP", 999, IPAddressEventType.FailedLogin), }); // process failed logins service.RunCycle().Sync(); Assert.IsTrue(service.Firewall.IsIPAddressBlocked(banIP, out _)); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(noBanIP, out _)); Assert.IsTrue(service.DB.TryGetIPAddress(banIP, out IPBanDB.IPAddressEntry e1)); Assert.IsFalse(service.DB.TryGetIPAddress(noBanIP, out IPBanDB.IPAddressEntry e2)); }
public async Task TestFailedLoginsClearOnSuccessfulLogin() { // turn on clear failed logins upon success login using IPBanConfig.TempConfigChanger configChanger = new IPBanConfig.TempConfigChanger(service, xml => { return(IPBanConfig.ChangeConfigAppSetting(xml, nameof(IPBanConfig.ClearFailedLoginsOnSuccessfulLogin), "true")); }, out string newConfig); string ip = "99.88.77.66"; for (int i = 0; i < 2; i++) { service.AddIPAddressLogEvents(new IPAddressLogEvent[] { // fail login new IPAddressLogEvent(ip, "user1", "RDP", 1, IPAddressEventType.FailedLogin), }); } await service.RunCycle(); service.AddIPAddressLogEvents(new IPAddressLogEvent[] { new IPAddressLogEvent(ip, "user1", "RDP", 1, IPAddressEventType.SuccessfulLogin), }); await service.RunCycle(); Assert.IsFalse(service.DB.TryGetIPAddress(ip, out _)); }
public void TestUserNameWhitelistBan() { using IPBanConfig.TempConfigChanger configChanger = new IPBanConfig.TempConfigChanger(service, xml => { return(IPBanConfig.ChangeConfigAppSetting(xml, "UserNameWhitelist", "OnlyMe")); }, out string newConfig); // TODO: ensure non OnlyMe users are banned immediately // TODO: ensure OnlyMe user gets 20 failed logins before ban }
public async Task TestUserNameWhitelistRegexBan() { using IPBanConfig.TempConfigChanger configChanger = new IPBanConfig.TempConfigChanger(service, xml => { return(IPBanConfig.ChangeConfigAppSetting(xml, "UserNameWhitelistRegex", "ftp_[0-9]+")); }, out string newConfig); service.AddIPAddressLogEvents(new IPAddressLogEvent[] { // a single failed login with a non-blacklisted user name should not get banned new IPAddressLogEvent("99.99.99.99", "ftp_1", "RDP", 1, IPAddressEventType.FailedLogin), // a single failed login with a failed user name whitelist regex should get banned new IPAddressLogEvent("99.99.99.90", "NaughtyUserName", "RDP", 1, IPAddressEventType.FailedLogin) }); await service.RunCycle(); Assert.IsTrue(service.Firewall.IsIPAddressBlocked("99.99.99.90", out _)); Assert.IsFalse(service.Firewall.IsIPAddressBlocked("99.99.99.99", out _)); }
private void RunConfigBanTest(string key, string value, string banIP, string noBanIP, int noBanIPCount = 999) { // turn on clear failed logins upon success login using IPBanConfig.TempConfigChanger configChanger = new IPBanConfig.TempConfigChanger(service, xml => { return(IPBanConfig.ChangeConfigAppSetting(xml, key, value)); }, out string newConfig); List <IPAddressLogEvent> events = new List <IPAddressLogEvent> { new IPAddressLogEvent(banIP, "user1", "RDP", 999, IPAddressEventType.FailedLogin) }; if (!string.IsNullOrWhiteSpace(noBanIP)) { events.Add(new IPAddressLogEvent(noBanIP, "user2", "RDP", noBanIPCount, IPAddressEventType.FailedLogin)); } service.AddIPAddressLogEvents(events); // process failed logins service.RunCycle().Sync(); Assert.IsTrue(service.Firewall.IsIPAddressBlocked(banIP, out _)); Assert.IsTrue(service.DB.TryGetIPAddress(banIP, out IPBanDB.IPAddressEntry e1)); Assert.AreEqual(e1.FailedLoginCount, 999); if (!string.IsNullOrWhiteSpace(noBanIP)) { Assert.IsFalse(service.Firewall.IsIPAddressBlocked(noBanIP, out _)); if (noBanIPCount > 0) { Assert.IsTrue(service.DB.TryGetIPAddress(noBanIP, out IPBanDB.IPAddressEntry e2)); Assert.AreEqual(e2.FailedLoginCount, noBanIPCount); } else { Assert.IsFalse(service.DB.TryGetIPAddress(noBanIP, out _)); } } }
public void TestExtraFirewallRules() { using IPBanConfig.TempConfigChanger configChanger = new IPBanConfig.TempConfigChanger(service, xml => { return(IPBanConfig.ChangeConfigAppSetting(xml, "FirewallRules", @" ReddisAllowIP;allow;10.0.0.1,10.0.0.2,192.168.1.168/24;6379;. WebOnly;block;0.0.0.0/1,128.0.0.0/1,::/1,8000::/1;22,80,443,3389;^(?:(?!Windows).)+$")); }, out string newConfig); List <string> rules = service.Firewall.GetRuleNames().ToList(); string reddisRule = service.Firewall.RulePrefix + "EXTRA_ReddisAllowIP"; string webRule = service.Firewall.RulePrefix + "EXTRA_WebOnly"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // on Windows, block is the default, so only the allow rules should show up Assert.IsTrue(rules.Exists((s) => s.StartsWith(reddisRule))); Assert.IsFalse(rules.Exists((s) => s.StartsWith(webRule))); Assert.AreEqual(1, service.Config.ExtraRules.Count); IPBanFirewallRule rule1 = service.Config.ExtraRules[0]; string regexString = rule1.ToString(); Assert.AreEqual("EXTRA_ReddisAllowIP;allow;10.0.0.1/32,10.0.0.2/32,192.168.1.0/24;6379;.", regexString); } else { // on Linux, both rules are needed Assert.AreEqual(2, service.Config.ExtraRules.Count); Assert.IsTrue(rules.Exists((s) => s.StartsWith(reddisRule))); Assert.IsTrue(rules.Exists((s) => s.StartsWith(webRule))); IPBanFirewallRule rule1 = service.Config.ExtraRules[0]; IPBanFirewallRule rule2 = service.Config.ExtraRules[1]; string regexString1 = rule1.ToString(); string regexString2 = rule2.ToString(); Assert.AreEqual("EXTRA_ReddisAllowIP;allow;10.0.0.1/32,10.0.0.2/32,192.168.1.0/24;6379;.", regexString1); Assert.AreEqual("EXTRA_WebOnly;block;0.0.0.0/1,128.0.0.0/1,::/1,8000::/1;22,80,443,3389;^(?:(?!Windows).)+$", regexString2); } }
private async Task TestMultipleBanTimespansAsync(bool resetFailedLogin) { using IPBanConfig.TempConfigChanger configChanger = new IPBanConfig.TempConfigChanger(service, xml => { xml = IPBanConfig.ChangeConfigAppSetting(xml, "BanTime", "00:00:01:00,00:00:02:00,00:00:03:00"); xml = IPBanConfig.ChangeConfigAppSetting(xml, "ResetFailedLoginCountForUnbannedIPAddresses", resetFailedLogin.ToString()); return(xml); }, out string newConfig); Assert.AreEqual(3, service.Config.BanTimes.Length); Assert.AreEqual(resetFailedLogin, service.Config.ResetFailedLoginCountForUnbannedIPAddresses); for (int i = 1; i <= 3; i++) { Assert.AreEqual(TimeSpan.FromMinutes(i), service.Config.BanTimes[i - 1]); } for (int i = 0; i < 4; i++) { // forget all the bans, but they should still be in the database due to the multiple timespans as failed logins IPBanService.UtcNow += TimeSpan.FromDays(14.0); await service.RunCycle(); if (i < 3) { if (i > 0) { // the ips should exist but not be banned AssertIPAddressesAreNotBanned(true, true); } AddFailedLogins((i == 0 ? -1 : 1)); if (resetFailedLogin) { if (i > 0) { // after one failed login, should not be banned AssertIPAddressesAreNotBanned(true, true); } // add more failed logins AddFailedLogins(); // now they should be banned, failed login counts are reset upon ban AssertIPAddressesAreBanned(0, 0); } else { // should have gotten back in with just a single failed login AssertIPAddressesAreBanned(info1.Count + i, info2.Count + i); } Assert.IsTrue(service.DB.TryGetIPAddress(ip1, out IPBanDB.IPAddressEntry e1)); Assert.IsTrue(service.DB.TryGetIPAddress(ip2, out IPBanDB.IPAddressEntry e2)); // i == 3 means wrap around from 3 minutes back to 1 minute TimeSpan expectedBanDuration = (i < 3 ? expectedBanDuration = TimeSpan.FromMinutes(i + 1) : TimeSpan.FromMinutes(1.0)); Assert.AreEqual(expectedBanDuration, e1.BanEndDate - e1.BanStartDate); Assert.AreEqual(expectedBanDuration, e2.BanEndDate - e2.BanStartDate); if (resetFailedLogin) { Assert.AreEqual(0, e1.FailedLoginCount); Assert.AreEqual(0, e2.FailedLoginCount); } else { Assert.AreNotEqual(0, e1.FailedLoginCount); Assert.AreNotEqual(0, e2.FailedLoginCount); } } else { // the cycle will run and remove the expired ip first as they have finished the loop through the ban times, they should all have a single failed login count AddFailedLogins(1); // ips should exist but not be banned AssertIPAddressesAreNotBanned(true, true); Assert.IsTrue(service.DB.TryGetIPAddress(ip1, out IPBanDB.IPAddressEntry e1)); Assert.IsTrue(service.DB.TryGetIPAddress(ip2, out IPBanDB.IPAddressEntry e2)); Assert.IsNull(e1.BanStartDate); Assert.IsNull(e2.BanStartDate); Assert.IsNull(e1.BanEndDate); Assert.IsNull(e2.BanEndDate); Assert.AreEqual(1, e1.FailedLoginCount); Assert.AreEqual(1, e2.FailedLoginCount); // now add a bunch of fail logins, ip should ban with a time span of 1 minute AddFailedLogins(); if (resetFailedLogin) { AssertIPAddressesAreBanned(0, 0); } else { AssertIPAddressesAreBanned(info1.Count + 1, info2.Count + 1); } Assert.IsTrue(service.DB.TryGetIPAddress(ip1, out e1)); Assert.IsTrue(service.DB.TryGetIPAddress(ip2, out e2)); TimeSpan expectedBanDuration = TimeSpan.FromMinutes(1.0); Assert.AreEqual(expectedBanDuration, e1.BanEndDate - e1.BanStartDate); Assert.AreEqual(expectedBanDuration, e2.BanEndDate - e2.BanStartDate); if (resetFailedLogin) { Assert.AreEqual(0, e1.FailedLoginCount); Assert.AreEqual(0, e2.FailedLoginCount); } else { Assert.AreEqual(info1.Count + 1, e1.FailedLoginCount); Assert.AreEqual(info2.Count + 1, e2.FailedLoginCount); } } } }
private async Task TestMultipleBanTimespansExternalBlockAsync(bool resetFailedLogin) { const string ipAddress = "99.99.99.99"; const string userName = "******"; const string source = "RDP"; const IPAddressEventType type = IPAddressEventType.FailedLogin; KeyValuePair <DateTime?, DateTime?> banDates; service.IPBanDelegate = new ExternalBlocker(service); IPAddressLogEvent[] events = new IPAddressLogEvent[1]; using IPBanConfig.TempConfigChanger configChanger = new IPBanConfig.TempConfigChanger(service, xml => { xml = IPBanConfig.ChangeConfigAppSetting(xml, "BanTime", "00:00:01:00,00:00:05:00,00:00:15:00,89:00:00:00"); xml = IPBanConfig.ChangeConfigAppSetting(xml, "ResetFailedLoginCountForUnbannedIPAddresses", resetFailedLogin.ToString()); return(xml); }, out string newConfig); Assert.AreEqual(4, service.Config.BanTimes.Length); // send a block event, should get banned for 1 minute IPBanService.UtcNow = new DateTime(2020, 1, 1, 1, 1, 1, DateTimeKind.Utc); for (int i = 0; i < 2; i++) { events[0] = new IPAddressLogEvent(ipAddress, userName, source, 1, type, IPBanService.UtcNow); service.AddIPAddressLogEvents(events); await service.RunCycle(); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(ipAddress)); // run cycle again, should get pinged by external blocker and ip should be blocked await service.RunCycle(); Assert.IsTrue(service.Firewall.IsIPAddressBlocked(ipAddress)); Assert.IsTrue(service.DB.TryGetBanDates(ipAddress, out banDates)); Assert.AreEqual(IPBanService.UtcNow, banDates.Key); Assert.AreEqual(IPBanService.UtcNow.AddMinutes(1.0), banDates.Value); // short step, should still be blocked IPBanService.UtcNow += TimeSpan.FromSeconds(1.0); await service.RunCycle(); Assert.IsTrue(service.Firewall.IsIPAddressBlocked(ipAddress)); IPBanService.UtcNow += TimeSpan.FromMinutes(1.0); await service.RunCycle(); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(ipAddress)); // send a fail login event, should get banned for 5 minutes events[0] = new IPAddressLogEvent(ipAddress, userName, source, 1, type, IPBanService.UtcNow); service.AddIPAddressLogEvents(events); await service.RunCycle(); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(ipAddress)); DateTime savedBanDate = IPBanService.UtcNow; // add a failed and blocked login event, should not interfere with the ban cycle events[0] = new IPAddressLogEvent(ipAddress, userName, source, 1, IPAddressEventType.FailedLogin, IPBanService.UtcNow); service.AddIPAddressLogEvents(events); await service.RunCycle(); events[0] = new IPAddressLogEvent(ipAddress, userName, source, 1, IPAddressEventType.Blocked, IPBanService.UtcNow, true); service.AddIPAddressLogEvents(events); await service.RunCycle(); // throw in some chaos IPBanService.UtcNow += TimeSpan.FromSeconds(7.213); // blocker will ban the ip await service.RunCycle(); Assert.IsTrue(service.Firewall.IsIPAddressBlocked(ipAddress)); Assert.IsTrue(service.DB.TryGetBanDates(ipAddress, out banDates)); Assert.AreEqual(savedBanDate, banDates.Key); Assert.AreEqual(savedBanDate.AddMinutes(5.0), banDates.Value); IPBanService.UtcNow += TimeSpan.FromMinutes(20.0); await service.RunCycle(); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(ipAddress)); // send a failed login event, should get banned for 15 minutes events[0] = new IPAddressLogEvent(ipAddress, userName, source, 1, type, IPBanService.UtcNow); service.AddIPAddressLogEvents(events); await service.RunCycle(); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(ipAddress)); // cycle again, blocker will ban await service.RunCycle(); Assert.IsTrue(service.Firewall.IsIPAddressBlocked(ipAddress)); Assert.IsTrue(service.DB.TryGetBanDates(ipAddress, out banDates)); Assert.AreEqual(IPBanService.UtcNow, banDates.Key); Assert.AreEqual(IPBanService.UtcNow.AddMinutes(15.0), banDates.Value); IPBanService.UtcNow += TimeSpan.FromMinutes(30.0); await service.RunCycle(); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(ipAddress)); // send a block event, should get banned for 89 days events[0] = new IPAddressLogEvent(ipAddress, userName, source, 1, type, IPBanService.UtcNow); service.AddIPAddressLogEvents(events); await service.RunCycle(); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(ipAddress)); // cycle again, blocker will ban await service.RunCycle(); Assert.IsTrue(service.Firewall.IsIPAddressBlocked(ipAddress)); Assert.IsTrue(service.DB.TryGetBanDates(ipAddress, out banDates)); Assert.AreEqual(IPBanService.UtcNow, banDates.Key); Assert.AreEqual(IPBanService.UtcNow.AddDays(89.0), banDates.Value); IPBanService.UtcNow += TimeSpan.FromDays(91.0); await service.RunCycle(); Assert.IsFalse(service.Firewall.IsIPAddressBlocked(ipAddress)); } }