コード例 #1
0
        // deleteRule will drop the rule and matching set before creating the rule and set, use this is you don't care to update the rule and set in place
        protected bool UpdateRuleDelta(string ruleName, string action, IEnumerable <IPBanFirewallIPAddressDelta> deltas, string hashType,
                                       int maxCount, bool deleteRule, IEnumerable <PortRange> allowPorts, CancellationToken cancelToken)
        {
#if ENABLE_FIREWALL_PROFILING
            Stopwatch timer = Stopwatch.StartNew();
#endif

            string ipFileTemp = OSUtility.Instance.GetTempFileName();
            try
            {
                // add and remove the appropriate ip addresses from the set
                using (StreamWriter writer = File.CreateText(ipFileTemp))
                {
                    if (cancelToken.IsCancellationRequested)
                    {
                        throw new OperationCanceledException(cancelToken);
                    }
                    writer.WriteLine($"create {ruleName} hash:{hashType} family {INetFamily} hashsize {hashSize} maxelem {maxCount} -exist");
                    foreach (IPBanFirewallIPAddressDelta delta in deltas)
                    {
                        if (cancelToken.IsCancellationRequested)
                        {
                            throw new OperationCanceledException(cancelToken);
                        }

                        if (IPAddressRange.TryParse(delta.IPAddress, out IPAddressRange range) &&
                            range.Begin.AddressFamily == addressFamily && range.End.AddressFamily == addressFamily)
                        {
                            try
                            {
                                if (delta.Added)
                                {
                                    if (range.Begin.Equals(range.End))
                                    {
                                        writer.WriteLine($"add {ruleName} {range.Begin} -exist");
                                    }
                                    else
                                    {
                                        writer.WriteLine($"add {ruleName} {range.ToCidrString()} -exist");
                                    }
                                }
                                else
                                {
                                    if (range.Begin.Equals(range.End))
                                    {
                                        writer.WriteLine($"del {ruleName} {range.Begin} -exist");
                                    }
                                    else
                                    {
                                        writer.WriteLine($"del {ruleName} {range.ToCidrString()} -exist");
                                    }
                                }
                            }
                            catch
                            {
                                // ignore invalid cidr ranges
                            }
                        }
                    }
                }

                if (cancelToken.IsCancellationRequested)
                {
                    throw new OperationCanceledException(cancelToken);
                }
                else
                {
                    // restore the deltas into the existing set
                    bool result = (RunProcess("ipset", true, $"restore < \"{ipFileTemp}\"") == 0);
                    CreateOrUpdateRule(ruleName, action, hashType, maxCount, allowPorts, cancelToken);
                    return(result);
                }
            }
            finally
            {
                ExtensionMethods.FileDeleteWithRetry(ipFileTemp);

#if ENABLE_FIREWALL_PROFILING
                timer.Stop();
                Logger.Warn("BlockIPAddressesDelta rule '{0}' took {1:0.00}ms with {2} ips",
                            ruleName, timer.Elapsed.TotalMilliseconds, deltas.Count());
#endif
            }
        }
コード例 #2
0
ファイル: IPBanService_Private.cs プロジェクト: tareq2/IPBan
        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
                            {
                                maxFailedLoginAttempts = 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 == IPBanDB.IPAddressState.Active || state == 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.Count > 0)
                                    {
                                        await IPBanDelegate.LoginAttemptFailed(ipAddress, source, userName, MachineGuid, OSName, OSVersion, UtcNow);
                                    }
                                    AddBannedIPAddress(ipAddress, source, userName, bannedIpAddresses, now, configBlacklisted, newCount, string.Empty, transaction);
                                }
                            }
                            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.Count > 0)
                                {
                                    await IPBanDelegate.LoginAttemptFailed(ipAddress, source, userName, MachineGuid, OSName, OSVersion, UtcNow);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex);
                    }
                }
コード例 #3
0
        public override Task <bool> BlockIPAddressesDelta(string ruleNamePrefix, IEnumerable <IPBanFirewallIPAddressDelta> ipAddresses, IEnumerable <PortRange> allowedPorts = null, CancellationToken cancelToken = default)
        {
#if ENABLE_FIREWALL_PROFILING
            Stopwatch timer = Stopwatch.StartNew();
#endif

            string                   prefix = (string.IsNullOrWhiteSpace(ruleNamePrefix) ? BlockRulePrefix : RulePrefix + ruleNamePrefix).TrimEnd('_') + "_";
            int                      ruleIndex;
            INetFwRule[]             rules             = EnumerateRulesMatchingPrefix(prefix).ToArray();
            List <HashSet <string> > remoteIPAddresses = new List <HashSet <string> >();
            List <bool>              ruleChanges       = new List <bool>();
            for (int i = 0; i < rules.Length; i++)
            {
                string[]         ipList = rules[i].RemoteAddresses.Split(',');
                HashSet <string> ipSet  = new HashSet <string>();
                foreach (string ip in ipList)
                {
                    // trim out submask
                    int pos = ip.IndexOf('/');
                    if (pos >= 0)
                    {
                        ipSet.Add(ip.Substring(0, pos));
                    }
                    else
                    {
                        ipSet.Add(ip);
                    }
                }
                remoteIPAddresses.Add(ipSet);
                ruleChanges.Add(false);
            }
            List <IPBanFirewallIPAddressDelta> deltas = ipAddresses.ToList();
            int deltasCount = deltas.Count;
            for (int deltaIndex = deltas.Count - 1; deltaIndex >= 0; deltaIndex--)
            {
                IPBanFirewallIPAddressDelta delta = deltas[deltaIndex];
                if (delta.Added)
                {
                    if (remoteIPAddresses.Any(set => set.Contains(delta.IPAddress)))
                    {
                        // no change, a set already has the ip
                        deltas.RemoveAt(deltaIndex);
                        continue;
                    }
                    else
                    {
                        // try to find a set with an availble slot
                        for (int setIndex = 0; setIndex < remoteIPAddresses.Count; setIndex++)
                        {
                            if (remoteIPAddresses[setIndex].Count < MaxIpAddressesPerRule)
                            {
                                remoteIPAddresses[setIndex].Add(delta.IPAddress);
                                deltas.RemoveAt(deltaIndex);
                                ruleChanges[setIndex] = true;
                                break;
                            }
                        }
                    }
                }
                else
                {
                    for (int setIndex = 0; setIndex < remoteIPAddresses.Count; setIndex++)
                    {
                        if (remoteIPAddresses[setIndex].Remove(delta.IPAddress))
                        {
                            ruleChanges[setIndex] = true;
                            break;
                        }
                    }
                    deltas.RemoveAt(deltaIndex);
                }
            }

            // any remaining deltas for adding need to go in new rules if they did not fit in the existing rules
            // remaining deltas are guaranteed to be adds
            string[] remainingIPAddresses = deltas.Select(d => d.IPAddress).Where(ip => IPAddress.TryParse(ip, out _)).ToArray();
            for (int i = 0; i < remainingIPAddresses.Length; i += MaxIpAddressesPerRule)
            {
                remoteIPAddresses.Add(new HashSet <string>(remainingIPAddresses.Skip(i).Take(MaxIpAddressesPerRule)));
                ruleChanges.Add(true);
            }

            // update the rules
            ruleIndex = 0;
            for (int i = 0; i < remoteIPAddresses.Count; i++)
            {
                if (ruleChanges[i])
                {
                    string name = (i < rules.Length ? rules[i].Name : prefix + ruleIndex.ToStringInvariant());
                    GetOrCreateRule(name, string.Join(',', remoteIPAddresses[i]), NetFwAction.Block, allowedPorts);
                }
                ruleIndex += MaxIpAddressesPerRule;
            }

#if ENABLE_FIREWALL_PROFILING
            timer.Stop();
            Logger.Warn("BlockIPAddressesDelta rule '{0}' took {1:0.00}ms with {2} ips",
                        prefix, timer.Elapsed.TotalMilliseconds, deltasCount);
#endif

            return(Task.FromResult(true));
        }
コード例 #4
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
            }
        }
コード例 #5
0
        private void LoadFirewall(IPBanConfig oldConfig)
        {
            IIPBanFirewall existing = Firewall;

            Firewall = FirewallCreator.CreateFirewall(Config, Firewall);
            if (existing != Firewall)
            {
                AddUpdater(Firewall);
                Logger.Warn("Loaded firewall type {0}", Firewall.GetType());
                if (existing != null)
                {
                    RemoveUpdater(existing);

                    // transfer banned ip to new firewall
                    Firewall.BlockIPAddresses(null, ipDB.EnumerateBannedIPAddresses()).Sync();
                }
            }

            if (oldConfig is null)
            {
                // clear out all previous custom rules
                foreach (string rule in Firewall.GetRuleNames(Firewall.RulePrefix + "EXTRA_").ToArray())
                {
                    Firewall.DeleteRule(rule);
                }
            }
            else
            {
                // check for updated / new / removed block rules
                List <string> deleteList = new List <string>(oldConfig.ExtraRules.Select(r => r.Name));

                // cleanup rules that are no longer in the config
                foreach (string newRule in Config.ExtraRules.Select(r => r.Name))
                {
                    deleteList.Remove(newRule);
                }
                foreach (string rule in deleteList)
                {
                    foreach (string ruleName in Firewall.GetRuleNames(rule).ToArray())
                    {
                        Firewall.DeleteRule(ruleName);
                    }
                }
            }

            // ensure firewall is cleared out if needed - will only execute once
            UpdateBannedIPAddressesOnStart();

            // ensure windows event viewer is setup if needed - will only execute once
            SetupWindowsEventViewer();

            // add/update global rules
            Firewall.AllowIPAddresses("GlobalWhitelist", Config.Whitelist);
            Firewall.BlockIPAddresses("GlobalBlacklist", Config.BlackList);

            // add/update user specified rules
            foreach (IPBanFirewallRule rule in Config.ExtraRules)
            {
                if (rule.Block)
                {
                    Firewall.BlockIPAddresses(rule.Name, rule.IPAddressRanges, rule.AllowPortRanges);
                }
                else
                {
                    Firewall.AllowIPAddresses(rule.Name, rule.IPAddressRanges, rule.AllowPortRanges);
                }
            }
        }
コード例 #6
0
        private void AddBannedIPAddress(string ipAddress, string source, string userName,
                                        List <IPAddressLogEvent> bannedIpAddresses, DateTime startBanDate, bool configBlacklisted,
                                        int counter, string extraInfo, object transaction, bool external)
        {
            // never ban whitelisted ip addresses
            if (IsWhitelisted(ipAddress))
            {
                Logger.Info("Ignoring ban request for whitelisted ip address {0}", ipAddress);
                return;
            }

            TimeSpan[] banTimes   = Config.BanTimes;
            TimeSpan   banTime    = banTimes.First();
            DateTime   banEndDate = startBanDate + banTime;

            // if we have an ip in the database, use the ban time to move to the next ban slot in the list of ban times
            // if ban times only has one entry, do not do this
            if (banTimes.Length > 1 &&
                ipDB.TryGetIPAddress(ipAddress, out IPBanDB.IPAddressEntry ipEntry, transaction) &&
                ipEntry.BanStartDate != null && ipEntry.BanEndDate != null)
            {
                // find the next ban time in the array
                banTime = ipEntry.BanEndDate.Value - ipEntry.BanStartDate.Value;
                for (int i = 0; i < banTimes.Length; i++)
                {
                    if (banTime < banTimes[i])
                    {
                        // ban for next timespan
                        banTime    = banTimes[i];
                        banEndDate = startBanDate + banTime;
                        Logger.Info("Moving to next ban duration {0} at index {1} for ip {1}", banTimes[i], i, ipAddress);
                        break;
                    }
                }
            }
            int adjustedCount = (counter <= 0 ? Config.FailedLoginAttemptsBeforeBan : counter);

            bannedIpAddresses?.Add(new IPAddressLogEvent(ipAddress, userName, source, adjustedCount, IPAddressEventType.Blocked));
            if (ipDB.SetBanDates(ipAddress, startBanDate, banEndDate, UtcNow, transaction))
            {
                firewallNeedsBlockedIPAddressesUpdate = true;
            }

            Logger.Warn(startBanDate, "Banning ip address: {0}, user name: {1}, config black listed: {2}, count: {3}, extra info: {4}, duration: {5}",
                        ipAddress, userName, configBlacklisted, counter, extraInfo, banTime);

            // if this is a delegate callback (counter of 0), exit out - we don't want to run handlers or processes for shared banned ip addresses
            if (counter <= 0)
            {
                return;
            }
            else if (BannedIPAddressHandler != null &&
                     System.Net.IPAddress.TryParse(ipAddress, out System.Net.IPAddress ipAddressObj) &&
                     !ipAddressObj.IsInternal())
            {
                try
                {
                    ExecuteTask(BannedIPAddressHandler.HandleBannedIPAddress(ipAddress, source, userName, OSName, OSVersion, AssemblyVersion, RequestMaker));
                }
                catch
                {
                    // eat exception, delicious
                }
            }

            if (IPBanDelegate != null && !external)
            {
                try
                {
                    ExecuteTask(IPBanDelegate.IPAddressBanned(ipAddress, source, userName, MachineGuid, OSName, OSVersion, UtcNow, true));
                }
                catch (Exception ex)
                {
                    Logger.Info("Error calling ipban delegate with banned ip address: " + ex.ToString());
                }
            }
        }
コード例 #7
0
ファイル: IPBanServiceRunner.cs プロジェクト: wushian/IPBan
 /// <inheritdoc />
 public override async Task StartAsync(CancellationToken cancellationToken)
 {
     Logger.Warn("Starting service");
     await base.StartAsync(cancellationToken);
 }