예제 #1
0
        private void ParseRegex(Regex regex, string text, bool successful, string timestampFormat)
        {
            List <IPAddressLogEvent> events = new();
            IPAddressEventType       type   = (successful ? IPAddressEventType.SuccessfulLogin : IPAddressEventType.FailedLogin);

            foreach (IPAddressLogEvent info in IPBanService.GetIPAddressEventsFromRegex(regex, text, timestampFormat, type, dns))
            {
                info.Source ??= Source; // apply default source only if we don't already have a source
                if (info.FailedLoginThreshold <= 0)
                {
                    info.FailedLoginThreshold = FailedLoginThreshold;
                }
                if (successful)
                {
                    info.LogLevel = SuccessfulLogLevel;
                }
                else
                {
                    info.LogLevel = FailedLogLevel;
                }
                events.Add(info);

                Logger.Debug("Log file found match, ip: {0}, user: {1}, source: {2}, count: {3}, type: {4}",
                             info.IPAddress, info.UserName, info.Source, info.Count, info.Type);
            }
            loginHandler.AddIPAddressLogEvents(events);
        }
예제 #2
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="ipAddress">IP address</param>
 /// <param name="userName">User name</param>
 /// <param name="source">Source</param>
 /// <param name="count">How many messages were aggregated, 1 for no aggregation</param>
 /// <param name="type">Event type</param>
 /// <param name="timestamp">Timestamp of the event, default for current timestamp</param>
 public IPAddressLogEvent(string ipAddress, string userName, string source, int count, IPAddressEventType type, DateTime timestamp = default)
 {
     IPAddress  = ipAddress;
     UserName   = userName;
     Source     = source;
     Count      = count;
     Type       = type;
     Timestamp  = (timestamp == default ? IPBanService.UtcNow : timestamp);
     FoundMatch = true;
 }
예제 #3
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="ipAddress">IP address</param>
 /// <param name="userName">User name</param>
 /// <param name="source">Source</param>
 /// <param name="count">How many messages were aggregated, 1 for no aggregation</param>
 /// <param name="type">Event type</param>
 /// <param name="timestamp">Timestamp of the event, default for current timestamp</param>
 public IPAddressLogEvent(string ipAddress, string userName, string source, int count, IPAddressEventType type, DateTime timestamp = default)
 {
     // normalize ip address if possible
     if (System.Net.IPAddress.TryParse(ipAddress, out System.Net.IPAddress parsedIPAddress))
     {
         IPAddress = parsedIPAddress.ToString();
     }
     else
     {
         IPAddress = ipAddress;
     }
     UserName  = userName;
     Source    = source;
     Count     = count;
     Type      = type;
     Timestamp = (timestamp == default ? IPBanService.UtcNow : timestamp);
 }
예제 #4
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="ipAddress">IP address</param>
 /// <param name="userName">User name</param>
 /// <param name="source">Source</param>
 /// <param name="count">How many messages were aggregated, 1 for no aggregation</param>
 /// <param name="type">Event type</param>
 /// <param name="timestamp">Timestamp of the event, default for current timestamp</param>
 /// <param name="external">Whether this log came from an external source</param>
 /// <param name="failedLoginThreshold">Failed login threshold or 0 for default</param>
 /// <param name="logLevel">Log level when the event is logged</param>
 public IPAddressLogEvent(string ipAddress, string userName, string source,
                          int count, IPAddressEventType type, DateTime timestamp = default, bool external = false,
                          int failedLoginThreshold = 0, LogLevel logLevel = LogLevel.Warning)
 {
     // normalize ip address if possible
     if (System.Net.IPAddress.TryParse(ipAddress, out System.Net.IPAddress parsedIPAddress))
     {
         IPAddress = parsedIPAddress.ToString();
     }
     else
     {
         IPAddress = ipAddress;
     }
     UserName             = userName;
     Source               = source;
     Count                = count;
     Type                 = type;
     Timestamp            = (timestamp == default ? IPBanService.UtcNow : timestamp);
     External             = external;
     FailedLoginThreshold = failedLoginThreshold;
 }
예제 #5
0
파일: IPBanService.cs 프로젝트: fysxm/IPBan
        /// <summary>
        /// Get an ip address and user name out of text using regex. Regex may contain groups named source_[sourcename] to override the source.
        /// </summary>
        /// <param name="regex">Regex</param>
        /// <param name="text">Text</param>
        /// <param name="ipAddress">Found ip address or null if none</param>
        /// <param name="userName">Found user name or null if none</param>
        /// <param name="timestampFormat">Timestamp format</param>
        /// <param name="eventType">Event type</param>
        /// <param name="dns">Dns lookup to resolve ip addresses</param>
        /// <returns>Set of matches from text</returns>
        public static IEnumerable <IPAddressLogEvent> GetIPAddressEventsFromRegex(Regex regex, string text,
                                                                                  string timestampFormat = null, IPAddressEventType eventType = IPAddressEventType.FailedLogin, IDnsLookup dns = null)
        {
            const string customSourcePrefix = "source_";

            // if no regex or no text, we are done
            if (regex is null || string.IsNullOrWhiteSpace(text))
            {
                yield break;
            }

            // remove control chars
            text = new string(text.Where(c => c == '\n' || c == '\t' || !char.IsControl(c)).ToArray()).Trim();

            // go through all the matches and pull out event info
            foreach (Match match in regex.Matches(text))
            {
                string   userName  = null;
                string   ipAddress = null;
                string   source    = null;
                DateTime timestamp = default;

                // check for a user name
                Group userNameGroup = match.Groups["username"];
                if (userNameGroup != null && userNameGroup.Success)
                {
                    userName = (userName ?? userNameGroup.Value.Trim(regexTrimChars));
                }

                // check for source
                Group sourceGroup = match.Groups["source"];
                if (sourceGroup != null && sourceGroup.Success)
                {
                    source = (source ?? sourceGroup.Value.Trim(regexTrimChars));
                }

                // check for groups with a custom source name
                foreach (Group group in match.Groups)
                {
                    if (group.Success && group.Name != null &&
                        string.IsNullOrWhiteSpace(source) && group.Name.StartsWith(customSourcePrefix))
                    {
                        source = group.Name.Substring(customSourcePrefix.Length);
                    }
                }

                // check for timestamp group
                Group timestampGroup = match.Groups["timestamp"];
                if (timestampGroup != null && timestampGroup.Success)
                {
                    string toParse = timestampGroup.Value.Trim(regexTrimChars);
                    if (string.IsNullOrWhiteSpace(timestampFormat) ||
                        !DateTime.TryParseExact(toParse, timestampFormat.Trim(), CultureInfo.InvariantCulture,
                                                DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal, out timestamp))
                    {
                        DateTime.TryParse(toParse, CultureInfo.InvariantCulture,
                                          DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal, out timestamp);
                    }
                }

                // check if the regex had an ipadddress group
                Group ipAddressGroup = match.Groups["ipaddress"];
                if (ipAddressGroup is null)
                {
                    ipAddressGroup = match.Groups["ipaddress_exact"];
                }
                if (ipAddressGroup != null && ipAddressGroup.Success && !string.IsNullOrWhiteSpace(ipAddressGroup.Value))
                {
                    string tempIPAddress = ipAddressGroup.Value.Trim();

                    // in case of IP:PORT format, try a second time, stripping off the :PORT, saves having to do this in all
                    //  the different ip regex.
                    int  lastColon        = tempIPAddress.LastIndexOf(':');
                    bool isValidIPAddress = IPAddress.TryParse(tempIPAddress, out IPAddress tmp);
                    if (isValidIPAddress || (lastColon >= 0 && IPAddress.TryParse(tempIPAddress.Substring(0, lastColon), out tmp)))
                    {
                        ipAddress = tmp.ToString();
                    }

                    // if we are parsing anything as ip address (including dns names)
                    if (ipAddress is null && dns != null && ipAddressGroup.Name == "ipaddress" &&
                        tempIPAddress != Environment.MachineName && tempIPAddress != "-")
                    {
                        // Check Host by name
                        Logger.Info("Parsing as IP failed, checking dns '{0}'", tempIPAddress);
                        try
                        {
                            IPHostEntry entry = dns.GetHostEntryAsync(tempIPAddress).Sync();
                            if (entry != null && entry.AddressList != null && entry.AddressList.Length > 0)
                            {
                                ipAddress = entry.AddressList.FirstOrDefault().ToString();
                                Logger.Info("Dns result '{0}' = '{1}'", tempIPAddress, ipAddress);
                                break;
                            }
                        }
                        catch
                        {
                            Logger.Info("Parsing as dns failed '{0}'", tempIPAddress);
                        }
                    }
                }

                // see if there is a repeat indicator in the message
                int repeatCount = ExtractRepeatCount(match, text);

                // return an event for this match
                yield return(new IPAddressLogEvent(ipAddress, userName, source, repeatCount, eventType, timestamp));
            }
        }
예제 #6
0
        /// <summary>
        /// Get an ip address and user name out of text using regex. Regex may contain groups named source_[sourcename] to override the source.
        /// </summary>
        /// <param name="regex">Regex</param>
        /// <param name="text">Text</param>
        /// <param name="ipAddress">Found ip address or null if none</param>
        /// <param name="userName">Found user name or null if none</param>
        /// <param name="timestampFormat">Timestamp format</param>
        /// <param name="eventType">Event type</param>
        /// <param name="dns">Dns lookup to resolve ip addresses</param>
        /// <returns>Set of matches from text</returns>
        public static IEnumerable <IPAddressLogEvent> GetIPAddressEventsFromRegex(Regex regex, string text,
                                                                                  string timestampFormat = null, IPAddressEventType eventType = IPAddressEventType.FailedLogin, IDnsLookup dns = null)
        {
            const string customSourcePrefix = "source_";

            // if no regex or no text, we are done
            if (regex is null || string.IsNullOrWhiteSpace(text))
            {
                yield break;
            }

            // remove control chars
            text = new string(text.Where(c => c == '\n' || c == '\t' || !char.IsControl(c)).ToArray());

            // go through all the matches and pull out event info
            MatchCollection matches = regex.Matches(text);

            foreach (Match match in matches)
            {
                string   userName  = null;
                string   ipAddress = null;
                string   source    = null;
                DateTime timestamp = default;

                // check for a user name
                Group userNameGroup = match.Groups["username"];
                if (userNameGroup != null && userNameGroup.Success)
                {
                    userName ??= userNameGroup.Value.Trim(regexTrimChars);
                }

                // check for source
                Group sourceGroup = match.Groups["source"];
                if (sourceGroup != null && sourceGroup.Success)
                {
                    source ??= sourceGroup.Value.Trim(regexTrimChars);
                }

                // check for groups with a custom source name
                foreach (Group group in match.Groups)
                {
                    if (group.Success && group.Name != null &&
                        string.IsNullOrWhiteSpace(source) && group.Name.StartsWith(customSourcePrefix))
                    {
                        source = group.Name[customSourcePrefix.Length..];
예제 #7
0
        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));
            }
        }