Ejemplo n.º 1
0
        private IPBanConfig(string xml, IDnsLookup dns)
        {
            this.dns = dns;

            // deserialize with XmlDocument, the .net core Configuration class is quite buggy
            XmlDocument doc = new XmlDocument();

            doc.LoadXml(xml);
            foreach (XmlNode node in doc.SelectNodes("//appSettings/add"))
            {
                appSettings[node.Attributes["key"].Value] = node.Attributes["value"].Value;
            }

            GetConfig <int>("FailedLoginAttemptsBeforeBan", ref failedLoginAttemptsBeforeBan, 1, 50);
            GetConfig <bool>("ResetFailedLoginCountForUnbannedIPAddresses", ref resetFailedLoginCountForUnbannedIPAddresses);
            GetConfigArray <TimeSpan>("BanTime", ref banTimes, emptyTimeSpanArray);
            for (int i = 0; i < banTimes.Length; i++)
            {
                banTimes[i] = banTimes[i].Clamp(TimeSpan.FromMinutes(1.0), maxBanTimeSpan);
            }
            GetConfig <bool>("ClearBannedIPAddressesOnRestart", ref clearBannedIPAddressesOnRestart);
            GetConfig <TimeSpan>("ExpireTime", ref expireTime, TimeSpan.FromMinutes(1.0), maxBanTimeSpan);
            GetConfig <TimeSpan>("CycleTime", ref cycleTime, TimeSpan.FromSeconds(5.0), TimeSpan.FromMinutes(1.0), false);
            GetConfig <TimeSpan>("MinimumTimeBetweenFailedLoginAttempts", ref minimumTimeBetweenFailedLoginAttempts, TimeSpan.Zero, TimeSpan.FromSeconds(15.0), false);
            GetConfig <string>("FirewallRulePrefix", ref firewallRulePrefix);

            string whiteListString      = GetConfig <string>("Whitelist", string.Empty);
            string whiteListRegexString = GetConfig <string>("WhitelistRegex", string.Empty);
            string blacklistString      = GetConfig <string>("Blacklist", string.Empty);
            string blacklistRegexString = GetConfig <string>("BlacklistRegex", string.Empty);

            PopulateList(whiteList, whiteListRanges, ref whiteListRegex, whiteListString, whiteListRegexString);
            PopulateList(blackList, blackListRanges, ref blackListRegex, blacklistString, blacklistRegexString);
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                expressionsFailure = new XmlSerializer(typeof(EventViewerExpressionsToBlock)).Deserialize(new XmlNodeReader(doc.SelectSingleNode("//ExpressionsToBlock"))) as EventViewerExpressionsToBlock;
                if (expressionsFailure != null)
                {
                    foreach (EventViewerExpressionGroup group in expressionsFailure.Groups)
                    {
                        foreach (EventViewerExpression expression in group.Expressions)
                        {
                            expression.Regex = (expression.Regex?.ToString() ?? string.Empty).Trim();
                        }
                    }
                }
                expressionsSuccess = new XmlSerializer(typeof(EventViewerExpressionsToNotify)).Deserialize(new XmlNodeReader(doc.SelectSingleNode("//ExpressionsToNotify"))) as EventViewerExpressionsToNotify;
                if (expressionsSuccess != null)
                {
                    foreach (EventViewerExpressionGroup group in expressionsSuccess.Groups)
                    {
                        group.NotifyOnly = true;
                        foreach (EventViewerExpression expression in group.Expressions)
                        {
                            expression.Regex = (expression.Regex?.ToString() ?? string.Empty).Trim();
                        }
                    }
                }
            }
            else
            {
                expressionsFailure = new EventViewerExpressionsToBlock();
                expressionsSuccess = new EventViewerExpressionsToNotify();
            }
            try
            {
                if (new XmlSerializer(typeof(IPBanLogFilesToParse)).Deserialize(new XmlNodeReader(doc.SelectSingleNode("//LogFilesToParse"))) is IPBanLogFilesToParse logFilesToParse)
                {
                    logFiles = logFilesToParse.LogFiles;
                }
                else
                {
                    logFiles = emptyLogFilesToParseArray;
                }
            }
            catch (Exception ex)
            {
                Logger.Error(ex);
                logFiles = new IPBanLogFileToParse[0];
            }
            GetConfig <string>("ProcessToRunOnBan", ref processToRunOnBan);
            GetConfig <bool>("UseDefaultBannedIPAddressHandler", ref useDefaultBannedIPAddressHandler);

            // retrieve firewall configuration
            string[] firewallTypes = GetConfig <string>("FirewallType", string.Empty).Split(',', StringSplitOptions.RemoveEmptyEntries);
            foreach (string firewallOSAndType in firewallTypes)
            {
                string[] pieces = firewallOSAndType.Split(':');
                if (pieces.Length == 2)
                {
                    osAndFirewallType[pieces[0]] = pieces[1];
                }
            }

            string userNameWhiteListString = GetConfig <string>("UserNameWhiteList", string.Empty);

            foreach (string userName in userNameWhiteListString.Split(','))
            {
                string userNameTrimmed = userName.Normalize().ToUpperInvariant().Trim();
                if (userNameTrimmed.Length > 0)
                {
                    userNameWhitelist.Add(userNameTrimmed);
                }
            }
            string userNameWhiteListRegexString = GetConfig <string>("UserNameWhiteListRegex", string.Empty);

            if (!string.IsNullOrWhiteSpace(userNameWhiteListRegexString))
            {
                userNameWhitelistRegex = new Regex(userNameWhiteListRegexString, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
            }
            GetConfig <int>("UserNameWhiteListMinimumEditDistance", ref userNameWhitelistMaximumEditDistance);
            GetConfig <int>("FailedLoginAttemptsBeforeBanUserNameWhitelist", ref failedLoginAttemptsBeforeBanUserNameWhitelist);
            GetConfig <string>("GetUrlUpdate", ref getUrlUpdate);
            GetConfig <string>("GetUrlStart", ref getUrlStart);
            GetConfig <string>("GetUrlStop", ref getUrlStop);
            GetConfig <string>("GetUrlConfig", ref getUrlConfig);
            GetConfig <string>("ExternalIPAddressUrl", ref externalIPAddressUrl);

            // parse firewall block rules, one per line
            ParseFirewallBlockRules();
        }
Ejemplo n.º 2
0
        private Task ProcessPendingLogEvents()
        {
            // get copy of pending log events quickly in a lock and clear list
            List <IPAddressLogEvent> events = null;

            lock (pendingLogEvents)
            {
                events = new List <IPAddressLogEvent>(pendingLogEvents);
                pendingLogEvents.Clear();
            }

            List <IPAddressLogEvent> bannedIPs = new List <IPAddressLogEvent>();
            object transaction = BeginTransaction();

            try
            {
                // loop through events, for failed and successful logins, we want to group / aggregate the same event from the
                // same remote ip address
                foreach (IPAddressLogEvent evt in events)
                {
                    if (!IPBanFirewallUtility.TryNormalizeIPAddress(evt.IPAddress, out string normalizedIPAddress))
                    {
                        continue;
                    }
                    evt.IPAddress = normalizedIPAddress;
                    switch (evt.Type)
                    {
                    case IPAddressEventType.FailedLogin:
                        // if we are not already banned...
                        if (!DB.TryGetIPAddressState(evt.IPAddress, out IPBanDB.IPAddressState? state, transaction) ||
                            state.Value == IPBanDB.IPAddressState.FailedLogin)
                        {
                            ProcessIPAddressEvent(evt, pendingFailedLogins, Config.MinimumTimeBetweenFailedLoginAttempts, "failed");
                        }
                        break;

                    case IPAddressEventType.SuccessfulLogin:
                        ProcessIPAddressEvent(evt, pendingSuccessfulLogins, Config.MinimumTimeBetweenSuccessfulLoginAttempts, "successful");
                        break;

                    case IPAddressEventType.Blocked:
                        // if we are not already banned...
                        if (!DB.TryGetIPAddressState(evt.IPAddress, out IPBanDB.IPAddressState? state2, transaction) ||
                            state2.Value == IPBanDB.IPAddressState.FailedLogin)
                        {
                            // make sure the ip address is ban pending
                            AddBannedIPAddress(evt.IPAddress, evt.Source, evt.UserName, bannedIPs,
                                               evt.Timestamp, false, evt.Count, string.Empty, transaction, evt.External);
                        }
                        break;

                    case IPAddressEventType.Unblocked:
                        DB.SetIPAddressesState(new string[] { evt.IPAddress }, IPBanDB.IPAddressState.RemovePending, transaction);
                        firewallNeedsBlockedIPAddressesUpdate = true;
                        break;
                    }
                }
                CommitTransaction(transaction);
            }
            catch (Exception ex)
            {
                RollbackTransaction(transaction);
                Logger.Error(ex);
            }
            ExecuteExternalProcessForBannedIPAddresses(bannedIPs);
            return(Task.CompletedTask);
        }
Ejemplo n.º 3
0
        protected virtual async Task <bool> GetUrl(UrlType urlType)
        {
            if ((urlType == UrlType.Start && GotStartUrl) || string.IsNullOrWhiteSpace(LocalIPAddressString) || string.IsNullOrWhiteSpace(FQDN))
            {
                return(false);
            }
            else if (urlType == UrlType.Stop)
            {
                GotStartUrl = false;
            }
            string url;

            switch (urlType)
            {
            case UrlType.Start: url = Config.GetUrlStart; break;

            case UrlType.Stop: url = Config.GetUrlStop; break;

            case UrlType.Update: url = Config.GetUrlUpdate; break;

            case UrlType.Config: url = Config.GetUrlConfig; break;

            default: return(false);
            }

            if (!string.IsNullOrWhiteSpace(url))
            {
                url = ReplaceUrl(url);
                try
                {
                    KeyValuePair <string, object>[] headers = (Authorization is null ? null : new KeyValuePair <string, object>[] { new KeyValuePair <string, object>("Authorization", Authorization) });
                    byte[] bytes = await RequestMaker.MakeRequestAsync(new Uri(url), headers : headers);

                    if (urlType == UrlType.Start)
                    {
                        GotStartUrl = true;
                    }
                    else if (urlType == UrlType.Update)
                    {
                        // if the update url sends bytes, we assume a software update, and run the result as an .exe
                        if (bytes.Length != 0)
                        {
                            string tempFile = Path.Combine(OSUtility.TempFolder, "IPBanServiceUpdate.exe");
                            File.WriteAllBytes(tempFile, bytes);

                            // however you are doing the update, you must allow -c and -d parameters
                            // pass -c to tell the update executable to delete itself when done
                            // pass -d for a directory which tells the .exe where this service lives
                            string args = "-c \"-d=" + AppContext.BaseDirectory + "\"";
                            Process.Start(tempFile, args);
                        }
                    }
                    else if (urlType == UrlType.Config && bytes.Length != 0)
                    {
                        await WriteConfigAsync(Encoding.UTF8.GetString(bytes));
                    }
                }
                catch (Exception ex)
                {
                    Logger.Error(ex, "Error getting url of type {0} at {1}", urlType, url);
                }
            }
            return(true);
        }
Ejemplo n.º 4
0
        private async Task ProcessPendingFailedLogins(IReadOnlyList <IPAddressLogEvent> ipAddresses)
        {
            List <IPAddressLogEvent> bannedIpAddresses = new List <IPAddressLogEvent>();
            object transaction = BeginTransaction();

            try
            {
                foreach (IPAddressLogEvent failedLogin in ipAddresses)
                {
                    try
                    {
                        string ipAddress = failedLogin.IPAddress;
                        string userName  = failedLogin.UserName;
                        string source    = failedLogin.Source;
                        if (IsWhitelisted(ipAddress))
                        {
                            Logger.Warn("Login failure, ignoring whitelisted ip address {0}, {1}, {2}", ipAddress, userName, source);
                        }
                        else
                        {
                            int maxFailedLoginAttempts;
                            if (Config.IsWhitelisted(userName))
                            {
                                maxFailedLoginAttempts = Config.FailedLoginAttemptsBeforeBanUserNameWhitelist;
                            }
                            else
                            {
                                // see if there is an override for max failed login attempts
                                maxFailedLoginAttempts = (failedLogin.FailedLoginThreshold > 0 ? failedLogin.FailedLoginThreshold : Config.FailedLoginAttemptsBeforeBan);
                            }

                            DateTime now = failedLogin.Timestamp;

                            // check for the target user name for additional blacklisting checks
                            bool ipBlacklisted           = Config.IsBlackListed(ipAddress);
                            bool userBlacklisted         = (ipBlacklisted ? false : Config.IsBlackListed(userName));
                            bool userFailsWhitelistRegex = (userBlacklisted ? false : Config.UserNameFailsUserNameWhitelistRegex(userName));
                            bool editDistanceBlacklisted = (ipBlacklisted || userBlacklisted || userFailsWhitelistRegex ? false : !Config.IsUserNameWithinMaximumEditDistanceOfUserNameWhitelist(userName));
                            bool configBlacklisted       = ipBlacklisted || userBlacklisted || userFailsWhitelistRegex || editDistanceBlacklisted;

                            // if the event came in with a count of 0 that means it is an automatic ban
                            int incrementCount = (failedLogin.Count < 1 ? maxFailedLoginAttempts : failedLogin.Count);
                            int newCount       = ipDB.IncrementFailedLoginCount(ipAddress, userName, source, UtcNow, incrementCount, transaction);

                            Logger.Warn(now, "Login failure: {0}, {1}, {2}, {3}", ipAddress, userName, source, newCount);

                            // if the ip address is black listed or the ip address has reached the maximum failed login attempts before ban, ban the ip address
                            if (configBlacklisted || newCount >= maxFailedLoginAttempts)
                            {
                                Logger.Info("IP blacklisted: {0}, user name blacklisted: {1}, fails user name white list regex: {2}, user name edit distance blacklisted: {3}",
                                            ipBlacklisted, userBlacklisted, userFailsWhitelistRegex, editDistanceBlacklisted);

                                if (ipDB.TryGetIPAddressState(ipAddress, out IPBanDB.IPAddressState? state, transaction) &&
                                    (state.Value == IPBanDB.IPAddressState.Active || state.Value == IPBanDB.IPAddressState.AddPending))
                                {
                                    Logger.Warn(now, "IP {0}, {1}, {2} ban pending.", ipAddress, userName, source);
                                }
                                else
                                {
                                    Logger.Debug("Failed login count {0} >= ban count {1}{2}", newCount, maxFailedLoginAttempts, (configBlacklisted ? " config blacklisted" : string.Empty));

                                    // if delegate and non-zero count, forward on - count of 0 means it was from external source, like a delegate
                                    if (IPBanDelegate != null && !failedLogin.External)
                                    {
                                        await IPBanDelegate.LoginAttemptFailed(ipAddress, source, userName, MachineGuid, OSName, OSVersion, UtcNow);
                                    }
                                    AddBannedIPAddress(ipAddress, source, userName, bannedIpAddresses, now,
                                                       configBlacklisted, newCount, string.Empty, transaction, failedLogin.External);
                                }
                            }
                            else
                            {
                                Logger.Debug("Failed login count {0} <= ban count {1}", newCount, maxFailedLoginAttempts);
                                if (OSUtility.UserIsActive(userName))
                                {
                                    Logger.Warn("Login failed for known active user {0}", userName);
                                }

                                // if delegate and non-zero count, forward on - count of 0 means it was from external source, like a delegate
                                if (IPBanDelegate != null && !failedLogin.External)
                                {
                                    await IPBanDelegate.LoginAttemptFailed(ipAddress, source, userName, MachineGuid, OSName, OSVersion, UtcNow);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex);
                    }
                }
                CommitTransaction(transaction);
                ExecuteExternalProcessForBannedIPAddresses(bannedIpAddresses);
            }
            catch (Exception ex)
            {
                RollbackTransaction(transaction);
                Logger.Error(ex);
            }
        }
Ejemplo n.º 5
0
        private bool GetOrCreateRule(string ruleName, string remoteIPAddresses, NetFwAction action, IEnumerable <PortRange> allowedPorts = null)
        {
            remoteIPAddresses = (remoteIPAddresses ?? string.Empty).Trim();
            bool emptyIPAddressString = string.IsNullOrWhiteSpace(remoteIPAddresses) || remoteIPAddresses == "*";
            bool ruleNeedsToBeAdded   = false;

            lock (policy)
            {
recreateRule:
                INetFwRule rule = null;
                try
                {
                    rule = policy.Rules.Item(ruleName);
                }
                catch
                {
                    // ignore exception, assume does not exist
                }
                if (rule is null)
                {
                    rule                = Activator.CreateInstance(ruleType) as INetFwRule;
                    rule.Name           = ruleName;
                    rule.Enabled        = true;
                    rule.Action         = action;
                    rule.Description    = "Automatically created by IPBan";
                    rule.Direction      = NetFwRuleDirection.Inbound;
                    rule.EdgeTraversal  = false;
                    rule.Grouping       = "IPBan";
                    rule.LocalAddresses = "*";
                    rule.Profiles       = int.MaxValue; // all
                    ruleNeedsToBeAdded  = true;
                }

                // do not ever set an empty string, Windows treats this as * which means everything
                if (!emptyIPAddressString)
                {
                    try
                    {
                        PortRange[] allowedPortsArray = (allowedPorts?.ToArray());
                        if (allowedPortsArray != null && allowedPortsArray.Length != 0)
                        {
                            rule.Protocol = (int)NetFwIPProtocol.TCP;
                            string localPorts;
                            if (action == NetFwAction.Block)
                            {
                                localPorts = IPBanFirewallUtility.GetBlockPortRangeString(allowedPortsArray);
                            }
                            else
                            {
                                localPorts = IPBanFirewallUtility.GetPortRangeStringAllow(allowedPortsArray);
                            }
                            rule.LocalPorts = localPorts;
                        }
                        else
                        {
                            try
                            {
                                rule.Protocol = (int)NetFwIPProtocol.Any;
                            }
                            catch
                            {
                                // failed to set protocol to any, we are switching from tcp back to any without ports, the only option is to
                                //  recreate the rule
                                if (!ruleNeedsToBeAdded)
                                {
                                    policy.Rules.Remove(ruleName);
                                    goto recreateRule;
                                }
                            }
                        }
                        rule.RemoteAddresses = (remoteIPAddresses == "0.0.0.0/0,::/0" ? "*" : remoteIPAddresses);
                    }
                    catch (Exception ex)
                    {
                        // if something failed, do not create the rule
                        emptyIPAddressString = true;
                        Logger.Error(ex);
                    }
                }

                if (emptyIPAddressString || string.IsNullOrWhiteSpace(rule.RemoteAddresses) || (rule.RemoteAddresses == "*" && remoteIPAddresses != "0.0.0.0/0,::/0"))
                {
                    // if no ip addresses, remove the rule as it will allow or block everything with an empty RemoteAddresses string
                    try
                    {
                        rule = null;
                        policy.Rules.Remove(ruleName);
                    }
                    catch
                    {
                    }
                }
                else if (ruleNeedsToBeAdded)
                {
                    policy.Rules.Add(rule);
                }
                return(rule != null);
            }
        }
Ejemplo n.º 6
0
        private Task <bool> BlockOrAllowIPAddresses(string ruleNamePrefix, bool block, IEnumerable <string> ipAddresses, IEnumerable <PortRange> allowedPorts = null, CancellationToken cancelToken = default)
        {
#if ENABLE_FIREWALL_PROFILING
            Stopwatch timer = Stopwatch.StartNew();
#endif

            int    i      = 0;
            string prefix = ruleNamePrefix.TrimEnd('_') + "_";

            try
            {
                List <string> ipAddressesList = new List <string>();
                foreach (string ipAddress in ipAddresses)
                {
                    if (cancelToken.IsCancellationRequested)
                    {
                        throw new OperationCanceledException(cancelToken);
                    }
                    ipAddressesList.Add(ipAddress);
                    if (ipAddressesList.Count == MaxIpAddressesPerRule)
                    {
                        if (block)
                        {
                            CreateBlockRule(ipAddressesList, 0, MaxIpAddressesPerRule, prefix + i.ToStringInvariant(), allowedPorts);
                        }
                        else
                        {
                            CreateAllowRule(ipAddressesList, 0, MaxIpAddressesPerRule, prefix + i.ToStringInvariant(), allowedPorts);
                        }
                        i += MaxIpAddressesPerRule;
                        ipAddressesList.Clear();
                    }
                }
                if (cancelToken.IsCancellationRequested)
                {
                    throw new OperationCanceledException(cancelToken);
                }
                if (ipAddressesList.Count != 0)
                {
                    if (block)
                    {
                        CreateBlockRule(ipAddressesList, 0, MaxIpAddressesPerRule, prefix + i.ToStringInvariant(), allowedPorts);
                    }
                    else
                    {
                        CreateAllowRule(ipAddressesList, 0, MaxIpAddressesPerRule, prefix + i.ToStringInvariant(), allowedPorts);
                    }
                    i += MaxIpAddressesPerRule;
                }
                DeleteRules(prefix, i);
                return(Task.FromResult(true));
            }
            catch (Exception ex)
            {
                if (!(ex is OperationCanceledException))
                {
                    Logger.Error(ex);
                }
                return(Task.FromResult(false));
            }
            finally
            {
#if ENABLE_FIREWALL_PROFILING
                timer.Stop();
                Logger.Warn("Block ip addresses rule '{0}' took {1:0.00}ms with {2} ips",
                            prefix, timer.Elapsed.TotalMilliseconds, i);
#endif
            }
        }