From 71f820ef2faac430bb1707622798b98be2094787 Mon Sep 17 00:00:00 2001 From: Leif Hedstrom Date: Tue, 5 Oct 2021 15:21:31 -0600 Subject: [PATCH] Adds an IP reputation system to the SNI rate limiter --- doc/admin-guide/plugins/rate_limit.en.rst | 62 +++- doc/static/images/sdk/SieveLRU.png | Bin 0 -> 137860 bytes plugins/experimental/rate_limit/Makefile.inc | 1 + .../experimental/rate_limit/ip_reputation.cc | 323 ++++++++++++++++++ .../experimental/rate_limit/ip_reputation.h | 236 +++++++++++++ plugins/experimental/rate_limit/iprep_simu.cc | 299 ++++++++++++++++ .../experimental/rate_limit/sni_limiter.cc | 104 +++++- plugins/experimental/rate_limit/sni_limiter.h | 22 ++ .../experimental/rate_limit/sni_selector.cc | 15 +- 9 files changed, 1048 insertions(+), 14 deletions(-) create mode 100644 doc/static/images/sdk/SieveLRU.png create mode 100644 plugins/experimental/rate_limit/ip_reputation.cc create mode 100644 plugins/experimental/rate_limit/ip_reputation.h create mode 100644 plugins/experimental/rate_limit/iprep_simu.cc diff --git a/doc/admin-guide/plugins/rate_limit.en.rst b/doc/admin-guide/plugins/rate_limit.en.rst index ef3f3246f7a..5d51ac895c5 100644 --- a/doc/admin-guide/plugins/rate_limit.en.rst +++ b/doc/admin-guide/plugins/rate_limit.en.rst @@ -30,6 +30,17 @@ The limit counters and queues are per remap rule only, i.e. there is (currently) no way to group transaction limits from different remap rules into a single rate limiter. +.. Note:: + This is still work in progress, in particularly the configuration and + the IP reputation system needs some work. In particular: + + * We need a proper YAML configuration overall, allowing us to configure + better per service controls as well as sharing resources between remap + rules or SNI. + * We need reloadable configurations. + * The IP reputation currently only works with the global plugin settings. + * There is no support for adding allow listed IPs to the IP reputation. + Remap Plugin ------------ @@ -96,7 +107,10 @@ Global Plugin ------------- As a global plugin, the rate limiting currently applies only for TLS enabled -connections, based on the SNI from the TLS handshake. The basic use is as:: +connections, based on the SNI from the TLS handshake. As a global plugin we +also have the support of an IP reputation system, see below for configurations. + +The basic use is as:: rate_limit.so SNI=www1.example.com,www2.example.com --limit=2 --queue=2 --maxage=10000 @@ -144,6 +158,37 @@ The following options are available: the plugin will use the FQDN of the SNI associated with each rate limiter instance created during plugin initialization. +.. option:: --iprep_buckets + The number of LRU buckets to use for the IP reputation. A good number here + is 10, but can be configured. The reason for the different buckets is to + account for a pseudo-sorted list of IPs on the frequency seen. Too few buckets + will not be enough to keep such a sorting, rendering the algorithm useless. To + function in our setup, the number of buckets must be less than ``100``. + +.. option:: --iprep_bucketsize + This is the size of the largest LRU bucket (the `entry bucket`), `15` is a good + value. This is a power of 2, so `15` means the largest LRU can hold `32768` entries. + Note that this option must be bigger then the `--iprep_buckets` setting, for the + bucket halfing to function. + +.. option:: --iprep_maxage + This is used for aging out entries out of the LRU, the default is `0` which means + no aging happens. Even with no aging, entries will eventually fall out of buckets + because of the LRU mechanism that kicks in. The aging is here to make sure a spike + in traffic from an IP doesn't keep the entry for too long in the LRUs. + +.. option:: --iprep_permablock_limit + The minimum number of hits an IP must reach to get moved to the permanent bucket. + In this bucket, entries will stay for 2x + +.. option:: --iprep_permablock_pressure + This option specifies from which bucket an IP is allowed to move from into the + perma block bucket. A good value here is likely `0` or `1`, which is very conservative. + +.. option:: --iprep_permablock_maxage + Similar to `--iprep_maxage` above, but only applies to the long term (`perma-block`) + bucket. Default is `0`, which means no aging to this bucket is applied. + Metrics ------- Metric names are generated either using defaults or user-supplied values. In either @@ -189,6 +234,21 @@ A user can specify their own prefixes and tags, but not types or metrics. ``resumed`` Queued connection is resumed. ============== =================================================================== +IP Reputation +------------- + +The goal of the IP reputation system is to simply try to identify IPs which are more +likely to be abusive than others. It's not a perfect system, and it relies heavily on +the notion of pressure. The Sieve LRUs are always filled, so you have to make sure that +you only start using them when the system thinks it's under pressure. + +The Sieve LRU is a chained set of (configurable) LRUs, each with smaller and smaller +capacity. This essentially adds a notion of partially sorted elements; All IPs in +LRU generally are more active than the IPs in LRU . LRU is specially marked +for longer term blocking, only the most abusive elements would end up here. + +.. figure:: /static/images/sdk/SieveLRU.png + Examples -------- diff --git a/doc/static/images/sdk/SieveLRU.png b/doc/static/images/sdk/SieveLRU.png new file mode 100644 index 0000000000000000000000000000000000000000..3e138e46d210e98c85571ffb47d12de09fd5d215 GIT binary patch literal 137860 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

aFo!&r(mPMLnt*r`nzaiv@+f&uuv(-b15mF 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 literal 0 HcmV?d00001 diff --git a/plugins/experimental/rate_limit/Makefile.inc b/plugins/experimental/rate_limit/Makefile.inc index 72469de5c68..95ab01f43b8 100644 --- a/plugins/experimental/rate_limit/Makefile.inc +++ b/plugins/experimental/rate_limit/Makefile.inc @@ -21,4 +21,5 @@ experimental_rate_limit_rate_limit_la_SOURCES = \ experimental/rate_limit/txn_limiter.cc \ experimental/rate_limit/sni_limiter.cc \ experimental/rate_limit/sni_selector.cc \ + experimental/rate_limit/ip_reputation.cc \ experimental/rate_limit/utilities.cc diff --git a/plugins/experimental/rate_limit/ip_reputation.cc b/plugins/experimental/rate_limit/ip_reputation.cc new file mode 100644 index 00000000000..129cd9584a1 --- /dev/null +++ b/plugins/experimental/rate_limit/ip_reputation.cc @@ -0,0 +1,323 @@ +/** @file + + Implementation details for the IP reputation classes. + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include +#include + +#include "ip_reputation.h" + +namespace IpReputation +{ +// These static class members are here to calculate a uint64_t hash of an IP +uint64_t +SieveLru::hasher(const sockaddr *sock) +{ + switch (sock->sa_family) { + case AF_INET: { + const sockaddr_in *sa = reinterpret_cast(sock); + + return (0xffffffff00000000 | sa->sin_addr.s_addr); + } break; + case AF_INET6: { + const sockaddr_in6 *sa6 = reinterpret_cast(sock); + + return (*reinterpret_cast(sa6->sin6_addr.s6_addr) ^ + *reinterpret_cast(sa6->sin6_addr.s6_addr + sizeof(uint64_t))); + } break; + default: + // Clearly shouldn't happen ... + return 0; + break; + } +} + +uint64_t +SieveLru::hasher(const std::string &ip, u_short family) // Mostly a convenience function for testing +{ + switch (family) { + case AF_INET: { + sockaddr_in sa4; + + inet_pton(AF_INET, ip.c_str(), &(sa4.sin_addr)); + sa4.sin_family = AF_INET; + return hasher(reinterpret_cast(&sa4)); + } break; + case AF_INET6: { + sockaddr_in6 sa6; + + inet_pton(AF_INET6, ip.c_str(), &(sa6.sin6_addr)); + sa6.sin6_family = AF_INET6; + return hasher(reinterpret_cast(&sa6)); + } break; + default: + // Really shouldn't happen ... + return 0; + } +} +// Constructor, setting up the pre-sized LRU buckets etc. +SieveLru::SieveLru(uint32_t num_buckets, uint32_t size) : _lock(TSMutexCreate()) +{ + initialize(num_buckets, size); +} + +// Initialize the Sieve LRU object +void +SieveLru::initialize(uint32_t num_buckets, uint32_t size) +{ + TSMutexLock(_lock); + TSAssert(!_initialized); // Don't allow it to be initialized more than once! + TSReleaseAssert(size > num_buckets); // Otherwise we can't half the bucket sizes + + _initialized = true; + _num_buckets = num_buckets; + _size = size; + + uint32_t cur_size = pow(2, 1 + _size - num_buckets); + + _map.reserve(pow(2, size + 2)); // Allow for all the sieve LRUs, and extra room for the allow list + _buckets.reserve(_num_buckets + 2); // Two extra buckets, for the block list and allow list + + // Create the other buckets, in smaller and smaller sizes (power of 2) + for (uint32_t i = lastBucket(); i <= entryBucket(); ++i) { + _buckets[i] = new SieveBucket(cur_size); + cur_size *= 2; + } + + _buckets[blockBucket()] = new SieveBucket(cur_size / 2); // Block LRU, same size as entry bucket + _buckets[allowBucket()] = new SieveBucket(0); // Allow LRU, this is unlimited + TSMutexUnlock(_lock); +} + +// Increment the count for an element (will be created / added if new). +std::tuple +SieveLru::increment(KeyClass key) +{ + TSMutexLock(_lock); + TSAssert(_initialized); + + auto map_it = _map.find(key); + + if (_map.end() == map_it) { + // This is a new entry, this can only be added to the last LRU bucket + SieveBucket *lru = _buckets[entryBucket()]; + + if (lru->full()) { // The LRU is full, replace the last item with a new one + auto last = std::prev(lru->end()); + auto &[l_key, l_count, l_bucket, l_added] = *last; + + lru->moveTop(lru, last); + _map.erase(l_key); + *last = {key, 1, entryBucket(), SystemClock::now()}; + } else { + // Create a new entry, the date is not used now (unless perma blocked), but could be useful for aging out stale elements. + lru->push_front({key, 1, entryBucket(), SystemClock::now()}); + } + _map[key] = lru->begin(); + TSMutexUnlock(_lock); + + return {entryBucket(), 1}; + } else { + auto &[map_key, map_item] = *map_it; + auto &[list_key, count, bucket, added] = *map_item; + auto lru = _buckets[bucket]; + auto max_age = (bucket == blockBucket() ? _perma_max_age : _max_age); + + // Check if the entry is older than max_age (if set), if so just move it to the entry bucket and restart + // Yes, this will move likely abusive IPs but they will earn back a bad reputation; The goal here is to + // not let "spiked" entries sit in small buckets indefinitely. It also cleans up the code. We only check + // the actual system time every 10 request for an IP, if traffic is less frequent than that, the LRU will + // age it out properly. + if ((_max_age > std::chrono::seconds::zero()) && ((count % 10) == 0) && + (std::chrono::duration_cast(SystemClock::now() - added) > max_age)) { + auto last_lru = _buckets[entryBucket()]; + + count >>= 3; // Age the count by a factor of 1/8th + bucket = entryBucket(); + last_lru->moveTop(lru, map_item); + } else { + ++count; + + if (bucket > lastBucket()) { // Not in the smallest bucket, so we may promote + auto p_lru = _buckets[bucket - 1]; // Move to previous bucket + + if (!p_lru->full()) { + p_lru->moveTop(lru, map_item); + --bucket; + } else { + auto p_item = std::prev(p_lru->end()); + auto &[p_key, p_count, p_bucket, p_added] = *p_item; + + if (p_count <= count) { + // Swap places on the two elements, moving both to the top of their respective LRU buckets + p_lru->moveTop(lru, map_item); + lru->moveTop(p_lru, p_item); + --bucket; + ++p_bucket; + } + } + } else { + // Just move it to the top of the current LRU + lru->moveTop(lru, map_item); + } + } + TSMutexUnlock(_lock); + + return {bucket, count}; + } +} + +// Lookup the status of the IP in the current tables, without modifying anything +std::tuple +SieveLru::lookup(KeyClass key) const +{ + TSMutexLock(_lock); + TSAssert(_initialized); + + auto map_it = _map.find(key); + + if (_map.end() == map_it) { + TSMutexUnlock(_lock); + + return {0, entryBucket()}; // Nothing found, return 0 hits and the entry bucket # + } else { + auto &[map_key, map_item] = *map_it; + auto &[list_key, count, bucket, added] = *map_item; + + TSMutexUnlock(_lock); + + return {bucket, count}; + } +} + +// A little helper function, to properly move an IP to one of the two special buckets, +// allow-bucket and block-bucket. +int32_t +SieveLru::move_bucket(KeyClass key, uint32_t to_bucket) +{ + TSMutexLock(_lock); + TSAssert(_initialized); + + auto map_it = _map.find(key); + + if (_map.end() == map_it) { + // This is a new entry, add it directly to the special bucket + SieveBucket *lru = _buckets[to_bucket]; + + if (lru->full()) { // The LRU is full, replace the last item with a new one + auto last = std::prev(lru->end()); + auto &[l_key, l_count, l_bucket, l_added] = *last; + + lru->moveTop(lru, last); + _map.erase(l_key); + *last = {key, 1, to_bucket, SystemClock::now()}; + } else { + // Create a new entry + lru->push_front({key, 1, to_bucket, SystemClock::now()}); + } + _map[key] = lru->begin(); + } else { + auto &[map_key, map_item] = *map_it; + auto &[list_key, count, bucket, added] = *map_item; + auto lru = _buckets[bucket]; + + if (bucket != to_bucket) { // Make sure it's not already blocked + auto move_lru = _buckets[to_bucket]; + + // Free a space for a new entry, if needed + if (move_lru->size() >= move_lru->max_size()) { + auto d_entry = std::prev(move_lru->end()); + auto &[d_key, d_count, d_bucket, d_added] = *d_entry; + + move_lru->erase(d_entry); + _map.erase(d_key); + } + move_lru->moveTop(lru, map_item); // Move the LRU item to the perma-blocks + bucket = to_bucket; + added = SystemClock::now(); + } + } + TSMutexUnlock(_lock); + + return to_bucket; // Just as a convenience, return the destination bucket for this entry +} + +void +SieveLru::dump() +{ + TSMutexLock(_lock); + TSAssert(_initialized); + + for (uint32_t i = 0; i < _num_buckets + 1; ++i) { + long long cnt = 0, sum = 0; + auto lru = _buckets[i]; + + std::cout << std::endl + << "Dumping bucket " << i << " (size=" << lru->size() << ", max_size=" << lru->max_size() << ")" << std::endl; + for (auto &it : *lru) { + auto &[key, count, bucket, added] = it; + + ++cnt; + sum += count; +#if 0 + if (0 == i) { // Also dump the content of the top bucket + std::cout << "\t" << key << "; Count=" << count << ", Bucket=" << bucket << std::endl; + } +#endif + } + + std::cout << "\tAverage count=" << (cnt > 0 ? sum / cnt : 0) << std::endl; + } + TSMutexUnlock(_lock); +} + +// Debugging tools, these memory sizes are best guesses to how much memory the containers will actually use +size_t +SieveBucket::memorySize() const +{ + size_t total = sizeof(SieveBucket); + + total += size() * (2 * sizeof(void *) + sizeof(LruEntry)); // Double linked list + object + + return total; +} + +size_t +SieveLru::memoryUsed() const +{ + TSMutexLock(_lock); + TSAssert(_initialized); + + size_t total = sizeof(SieveLru); + + for (uint32_t i = 0; i <= _num_buckets + 1; ++i) { + total += _buckets[i]->memorySize(); + } + + total += _map.size() * (sizeof(void *) + sizeof(SieveBucket::iterator)); + total += _map.bucket_count() * (sizeof(size_t) + sizeof(void *)); + TSMutexUnlock(_lock); + + return total; +} + +} // namespace IpReputation diff --git a/plugins/experimental/rate_limit/ip_reputation.h b/plugins/experimental/rate_limit/ip_reputation.h new file mode 100644 index 00000000000..4abbbcaa088 --- /dev/null +++ b/plugins/experimental/rate_limit/ip_reputation.h @@ -0,0 +1,236 @@ +/** @file + + Include file for all the IP reputation classes. + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ts/ts.h" + +namespace IpReputation +{ +using KeyClass = uint64_t; +using SystemClock = std::chrono::system_clock; + +// Key / Count / bucket # (rank, 0-) / time added +using LruEntry = std::tuple>; + +// This is a wrapper around a std::list which lets us size limit the list to a +// certain size. +class SieveBucket : public std::list +{ +public: + SieveBucket(uint32_t max_size) : _max_size(max_size) {} + + bool + full() const + { + return (_max_size > 0 ? (size() >= _max_size) : false); + } + + size_t + max_size() const + { + return _max_size; + } + + // Move an element to the top of an LRU. This *can* move it from the current LRU (bucket) + // to another, when promoted to a higher rank. + void + moveTop(SieveBucket *source_lru, SieveBucket::iterator &item) + { + splice(begin(), *source_lru, item); + } + + // Debugging tools + size_t memorySize() const; + +private: + uint32_t _max_size; +}; + +using HashMap = std::unordered_map; // The hash map for finding the entry + +// This is a concept / POC: Ranked LRU buckets +// +// Also, obviously the std::string here is not awesome, rather, we ought to save the +// hashed value from the IP as the key (just like the hashed in cache_promote). +class SieveLru +{ +public: + SieveLru() : _lock(TSMutexCreate()){}; // The unitialized version + SieveLru(uint32_t num_buckets, uint32_t size); + ~SieveLru() + { + for (uint32_t i = 0; i <= _num_buckets + 1; ++i) { // Remember to delete the two special allow/block buckets too + delete _buckets[i]; + } + } + + void initialize(uint32_t num_buckets = 10, uint32_t size = 15); + + // Return value is the bucket (0 .. num_buckets) that the IP is in, and the + // current count of "hits". The lookup version is similar, except it doesn't + // modify the status of the IP (read-only). + std::tuple increment(KeyClass key); + + std::tuple + increment(const sockaddr *sock) + { + return increment(hasher(sock)); + } + + // Move an IP to the perm-block or perma-allow LRUs. A zero (default) maxage is indefinite (no timeout). + uint32_t + block(KeyClass key) + { + return move_bucket(key, blockBucket()); + } + + uint32_t + allow(KeyClass key) + { + return move_bucket(key, allowBucket()); + } + + uint32_t + block(const sockaddr *sock) + { + return move_bucket(hasher(sock), blockBucket()); + } + + uint32_t + allow(const sockaddr *sock) + { + return move_bucket(hasher(sock), allowBucket()); + } + + // Lookup the current state of an IP + std::tuple lookup(KeyClass key) const; + + std::tuple + lookup(const sockaddr *sock) const + { + return lookup(hasher(sock)); + } + + // A helper function to hash an INET or INET6 sockaddr to a 64-bit hash. + static uint64_t hasher(const sockaddr *sock); + static uint64_t hasher(const std::string &ip, u_short family = AF_INET); + + // Identifying some of the special buckets: + // + // entryBucket == the highest bucket, where new IPs enter (also the biggest bucket) + // lastBucket == the last bucket, which is most likely to be abusive + // blockBucket == the bucket where we "permanently" block bad IPs + // allowBucket == the bucket where we "permanently" allow good IPs (can not be blocked) + uint32_t + entryBucket() const + { + return _num_buckets; + } + + constexpr uint32_t + lastBucket() const + { + return 1; + } + + constexpr uint32_t + blockBucket() const + { + return 0; + } + + uint32_t + allowBucket() const + { + return _num_buckets + 1; + } + + size_t + bucketSize(uint32_t bucket) const + { + if (bucket <= (_num_buckets + 1)) { + return _buckets[bucket]->size(); + } else { + return 0; + } + } + + bool + initialized() const + { + return _initialized; + } + + // Aging getters and setters + std::chrono::seconds + maxAge() const + { + return _max_age; + } + + std::chrono::seconds + permaMaxAge() const + { + return _perma_max_age; + } + + void + maxAge(std::chrono::seconds maxage) + { + _max_age = maxage; + } + + void + permaMaxAge(std::chrono::seconds maxage) + { + _perma_max_age = maxage; + } + + // Debugging tool, dumps some info around the buckets + void dump(); + size_t memoryUsed() const; + +protected: + int32_t move_bucket(KeyClass key, uint32_t to_bucket); + +private: + HashMap _map; + std::vector _buckets; + uint32_t _num_buckets = 10; // Leave this at 10 ... + uint32_t _size = 0; // Set this up to initialize + std::chrono::seconds _max_age = std::chrono::seconds::zero(); // Aging time in the SieveLru (default off) + std::chrono::seconds _perma_max_age = std::chrono::seconds::zero(); // Aging time in the SieveLru for perma-blocks + bool _initialized = false; // If this has been properly initialized yet + TSMutex _lock; // The lock around all data access +}; + +} // namespace IpReputation diff --git a/plugins/experimental/rate_limit/iprep_simu.cc b/plugins/experimental/rate_limit/iprep_simu.cc new file mode 100644 index 00000000000..887df68b861 --- /dev/null +++ b/plugins/experimental/rate_limit/iprep_simu.cc @@ -0,0 +1,299 @@ + +/** @file + + Simulator application, for testing the behavior of the SieveLRU. This does + not build as part of the system, but put here for future testing etc. + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +#include + +#include +#include +#include +#include +#include +#include + +// Yeh well, sue me, boost is useful here, and this is not part of the actual core code +#include + +#include "ip_reputation.h" + +// Convenience class declarations +using IpMap = std::unordered_map>; // count / false = good, true = bad +using IpList = std::vector; + +// Holds all command line options +struct CmdConfigs { + uint32_t start_buckets, end_buckets, incr_buckets; + uint32_t start_size, end_size, incr_size; + uint32_t start_threshold, end_threshold, incr_threshold; + uint32_t start_permablock, end_permablock, incr_permablock; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Command line options / parsing, returns the parsed and populate CmdConfig +// structure (from above). +// +std::tuple +splitArg(std::string str) +{ + int32_t start = 0, end = 0, incr = 1; + std::vector results; + + boost::split(results, str, [](char c) { return c == '-' || c == '/'; }); + + if (results.size() > 0) { + start = std::stoi(results[0]); + if (results.size() > 1) { + end = std::stoi(results[1]); + if (results.size() > 2) { + incr = std::stoi(results[2]); + } + } else { + end = start; + } + } else { + std::cerr << "Malformed argument: " << str << std::endl; + } + + return {start, end, incr}; +} + +CmdConfigs +parseArgs(int argc, char **argv) +{ + CmdConfigs options; + int c; + constexpr struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, {"buckets", required_argument, NULL, 'b'}, {"perma", required_argument, NULL, 'p'}, + {"size", required_argument, NULL, 's'}, {"threshold", required_argument, NULL, 't'}, {NULL, 0, NULL, 0}}; + + // Make sure the optional values have been set + + options.start_permablock = 0; + options.end_permablock = 0; + options.incr_permablock = 1; + + while (1) { + int ix = 0; + + c = getopt_long(argc, argv, "b:f:p:s:t:h?", long_options, &ix); + if (c == -1) + break; + + switch (c) { + case 'h': + case '?': + std::cerr << "usage: iprep_simu -b|--buckets [-[/]]" << std::endl; + std::cerr << " -s|--size [-[/]]" << std::endl; + std::cerr << " -t|--threshold [-[/]]" << std::endl; + std::cerr << " [-p|--perma [-[/]]]" << std::endl; + std::cerr << " [-h|--help" << std::endl; + exit(0); + break; + case 'b': + std::tie(options.start_buckets, options.end_buckets, options.incr_buckets) = splitArg(optarg); + break; + case 's': + std::tie(options.start_size, options.end_size, options.incr_size) = splitArg(optarg); + break; + case 'p': + std::tie(options.start_permablock, options.end_permablock, options.incr_permablock) = splitArg(optarg); + break; + case 't': + std::tie(options.start_threshold, options.end_threshold, options.incr_threshold) = splitArg(optarg); + break; + default: + fprintf(stderr, "getopt returned weird stuff: 0%o\n", c); + exit(-1); + break; + } + } + + return options; // RVO +} + +/////////////////////////////////////////////////////////////////////////////// +// Load a configuration file, and populate the two structures with the +// list of IPs (and their status) as well as the full sequence of requests. +// +// Returns a tuple with the number of good requests and bad requests, respectively. +// +std::tuple +loadFile(std::string fname, IpMap &all_ips, IpList &ips) +{ + std::ifstream infile(fname); + + float timestamp; // The timestamp from the request(relative) + std::string ip; // The IP + bool status; // Bad (false) or Good (true) request? + + uint32_t good_ips = 0; + uint32_t bad_ips = 0; + uint32_t good_requests = 0; + uint32_t bad_requests = 0; + + // Load in the entire file, and fill the request vector as well as the IP lookup table (state) + while (infile >> timestamp >> ip >> status) { + auto ip_hash = IpReputation::SieveLru::hasher(ip, ip.find(':') != std::string::npos ? AF_INET6 : AF_INET); + auto it = all_ips.find(ip_hash); + + if (!status) { + ++good_requests; + } else { + ++bad_requests; + } + + if (all_ips.end() != it) { + auto &[key, data] = *it; + auto &[count, d_status] = data; + + ++count; + ips.push_back(it); + } else { + all_ips[ip_hash] = {0, status}; + ips.push_back(all_ips.find(ip_hash)); + if (!status) { + ++good_ips; + } else { + ++bad_ips; + } + } + } + + std::cout << std::setprecision(3); + std::cout << "Total number of requests: " << ips.size() << std::endl; + std::cout << "\tGood requests: " << good_requests << " (" << 100.0 * good_requests / ips.size() << "%)" << std::endl; + std::cout << "\tBad requests: " << bad_requests << " (" << 100.0 * bad_requests / ips.size() << "%)" << std::endl; + std::cout << "Unique IPs in set: " << all_ips.size() << std::endl; + std::cout << "\tGood IPs: " << good_ips << " (" << 100.0 * good_ips / all_ips.size() << "%)" << std::endl; + std::cout << "\tBad IPs: " << bad_ips << " (" << 100.0 * bad_ips / all_ips.size() << "%)" << std::endl; + std::cout << std::endl; + + return {good_requests, bad_requests}; +} + +int +main(int argc, char *argv[]) +{ + auto options = parseArgs(argc, argv); + + // All remaining arguments should be files, so lets process them one by one + for (int file_num = optind; file_num < argc; ++file_num) { + IpMap all_ips; + IpList ips; + + // Load the data from file + auto [good_requests, bad_requests] = loadFile(argv[file_num], all_ips, ips); + + // Here starts the actual simulation, loop through variations + for (uint32_t size = options.start_size; size <= options.end_size; size += options.incr_size) { + for (uint32_t buckets = options.start_buckets; buckets <= options.end_buckets; buckets += options.incr_buckets) { + for (uint32_t threshold = options.start_threshold; threshold <= options.end_threshold; + threshold += options.incr_threshold) { + for (uint32_t permablock = options.start_permablock; permablock <= options.end_permablock; + permablock += options.incr_permablock) { + // Setup the buckets and metrics for this loop + IpReputation::SieveLru ipt(buckets, size); + + auto start = std::chrono::system_clock::now(); + + // Some metrics + uint32_t good_blocked = 0; + uint32_t good_allowed = 0; + uint32_t bad_blocked = 0; + uint32_t bad_allowed = 0; + uint32_t good_perm_blocked = 0; + uint32_t bad_perm_blocked = 0; + + for (auto iter : ips) { + auto &[ip, data] = *iter; + auto &[count, status] = data; + auto [bucket, cur_cnt] = ipt.increment(ip); + + // Currently we only allow perma-blocking on items in bucket 1, so check for that first. + if (cur_cnt > permablock && bucket == ipt.lastBucket()) { + bucket = ipt.block(ip); + } + + if (bucket == ipt.blockBucket()) { + if (!status) { + ++good_perm_blocked; + } else { + ++bad_perm_blocked; + } + } else if (bucket <= threshold) { + if (!status) { + ++good_blocked; + } else { + ++bad_blocked; + } + } else { + if (!status) { + ++good_allowed; + } else { + ++bad_allowed; + } + } + } + + auto end = std::chrono::system_clock::now(); + + uint32_t total_blocked = bad_blocked + good_blocked; + uint32_t total_perm_blocked = bad_perm_blocked + good_perm_blocked; + uint32_t total_allowed = bad_allowed + good_allowed; + + // ipt.dump(); + + std::chrono::duration elapsed_seconds = end - start; + + std::cout << "Running with size=" << size << ", buckets=" << buckets << ", threshold=" << threshold + << ", permablock=" << permablock << std::endl; + std::cout << "Processing time: " << elapsed_seconds.count() << std::endl; + std::cout << "Denied requests: " << total_blocked + total_perm_blocked << std::endl; + std::cout << "\tGood requests denied: " << good_blocked + good_perm_blocked << " (" + << 100.0 * (good_blocked + good_perm_blocked) / good_requests << "%)" << std::endl; + std::cout << "\tBad requests denied: " << bad_blocked + bad_perm_blocked << " (" + << 100.0 * (bad_blocked + bad_perm_blocked) / bad_requests << "%)" << std::endl; + std::cout << "Allowed requests: " << total_allowed << std::endl; + std::cout << "\tGood requests allowed: " << good_allowed << " (" << 100.0 * good_allowed / good_requests << "%)" + << std::endl; + std::cout << "\tBad requests allowed: " << bad_allowed << " (" << 100.0 * bad_allowed / bad_requests << "%)" + << std::endl; + if (permablock) { + std::cout << "Permanently blocked IPs: " << ipt.bucketSize(ipt.blockBucket()) << std::endl; + std::cout << "\tGood requests permanently denied: " << good_perm_blocked << " (" + << 100.0 * good_perm_blocked / good_requests << "%)" << std::endl; + std::cout << "\tBad requests permanently denied: " << bad_perm_blocked << " (" + << 100.0 * bad_perm_blocked / bad_requests << "%)" << std::endl; + } + std::cout << "Estimated score (lower is better): " + << 100.0 * ((100.0 * good_blocked / good_requests + 100.0 * bad_allowed / bad_requests) / + (100.0 * good_allowed / good_requests + 100.0 * bad_blocked / bad_requests)) + << std::endl; + std::cout << "Memory used for IP Reputation data: " << ipt.memoryUsed() / (1024.0 * 1024.0) << "MB" << std::endl + << std::endl; + } + } + } + } + } +} diff --git a/plugins/experimental/rate_limit/sni_limiter.cc b/plugins/experimental/rate_limit/sni_limiter.cc index b63c50b1df9..fcc8fb838f4 100644 --- a/plugins/experimental/rate_limit/sni_limiter.cc +++ b/plugins/experimental/rate_limit/sni_limiter.cc @@ -43,18 +43,58 @@ sni_limit_cont(TSCont contp, TSEvent event, void *edata) int len; const char *server_name = TSVConnSslSniGet(vc, &len); std::string_view sni_name(server_name, len); + SniRateLimiter *limiter = selector->find(sni_name); - if (!sni_name.empty()) { // This should likely always succeed, but without it we can't do anything - SniRateLimiter *limiter = selector->find(sni_name); + if (limiter) { + // Check if we have an IP reputation for this SNI, and if we should block + if (limiter->iprep.initialized()) { + const sockaddr *sock = TSNetVConnRemoteAddrGet(vc); + int pressure = limiter->pressure(); + + TSDebug(PLUGIN_NAME, "CLIENT_HELLO on %.*s, pressure=%d", static_cast(sni_name.length()), sni_name.data(), pressure); + + // TSDebug(PLUGIN_NAME, "IP Reputation: pressure is currently %d", pressure); + + if (pressure >= 0) { // When pressure is < 0, we're not yet at a level of pressure to be concerned about + char client_ip[INET6_ADDRSTRLEN] = "[unknown]"; + auto [bucket, cur_cnt] = limiter->iprep.increment(sock); + + // Get the client IP string if debug is enabled + if (TSIsDebugTagSet(PLUGIN_NAME)) { + if (sock->sa_family == AF_INET) { + inet_ntop(AF_INET, &(((struct sockaddr_in *)sock)->sin_addr), client_ip, INET_ADDRSTRLEN); + } else if (sock->sa_family == AF_INET6) { + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sock)->sin6_addr), client_ip, INET6_ADDRSTRLEN); + } + } + + if (cur_cnt > limiter->iprep_permablock_count && + bucket <= limiter->iprep_permablock_threshold) { // Mark for long-term blocking + TSDebug(PLUGIN_NAME, "Marking IP=%s for perma-blocking", client_ip); + bucket = limiter->iprep.block(sock); + } + + if (static_cast(pressure) > bucket) { // Remember the perma-block bucket is always 0, and we are >=0 already + // Block this IP from finishing the handshake + TSDebug(PLUGIN_NAME, "Rejecting connection from IP=%s, we're at pressure and IP was chosen to be blocked", client_ip); + TSUserArgSet(vc, gVCIdx, nullptr); + TSVConnReenableEx(vc, TS_EVENT_ERROR); + + return TS_ERROR; + } + } + } else { + TSDebug(PLUGIN_NAME, "CLIENT_HELLO on %.*s, no IP reputation", static_cast(sni_name.length()), sni_name.data()); + } - TSDebug(PLUGIN_NAME, "CLIENT_HELLO on %.*s", static_cast(sni_name.length()), sni_name.data()); - if (limiter && !limiter->reserve()) { + // If we passed the IP reputation filter, continue rate limiting these connections + if (!limiter->reserve()) { if (!limiter->max_queue || limiter->full()) { // We are running at limit, and the queue has reached max capacity, give back an error and be done. - TSVConnReenableEx(vc, TS_EVENT_ERROR); TSDebug(PLUGIN_NAME, "Rejecting connection, we're at capacity and queue is full"); TSUserArgSet(vc, gVCIdx, nullptr); limiter->incrementMetric(RATE_LIMITER_METRIC_REJECTED); + TSVConnReenableEx(vc, TS_EVENT_ERROR); return TS_ERROR; } else { @@ -69,11 +109,11 @@ sni_limit_cont(TSCont contp, TSEvent event, void *edata) TSVConnReenable(vc); } } else { + // No limiter for this SNI at all, clear the args etc. just in case + TSUserArgSet(vc, gVCIdx, nullptr); TSVConnReenable(vc); } - - break; - } + } break; case TS_EVENT_VCONN_CLOSE: { SniRateLimiter *limiter = static_cast(TSUserArgGet(vc, gVCIdx)); @@ -107,10 +147,19 @@ SniRateLimiter::initialize(int argc, const char *argv[]) {const_cast("maxage"), required_argument, nullptr, 'm'}, {const_cast("prefix"), required_argument, nullptr, 'p'}, {const_cast("tag"), required_argument, nullptr, 't'}, + // These are all for the IP reputation system. ToDo: These should be global rather than per SNI ? + {const_cast("iprep_maxage"), required_argument, nullptr, 'a'}, + {const_cast("iprep_buckets"), required_argument, nullptr, 'B'}, + {const_cast("iprep_bucketsize"), required_argument, nullptr, 'S'}, + {const_cast("iprep_permablock_limit"), required_argument, nullptr, 'L'}, + {const_cast("iprep_permablock_pressure"), required_argument, nullptr, 'P'}, + {const_cast("iprep_permablock_maxage"), required_argument, nullptr, 'A'}, // EOF {nullptr, no_argument, nullptr, '\0'}, }; + TSDebug(PLUGIN_NAME, "Initializing an SNI Rate Limiter"); + while (true) { int opt = getopt_long(argc, const_cast(argv), "", longopt, nullptr); @@ -130,11 +179,50 @@ SniRateLimiter::initialize(int argc, const char *argv[]) case 't': this->tag = std::string(optarg); break; + case 'a': + this->_iprep_max_age = std::chrono::seconds(strtol(optarg, nullptr, 10)); + break; + case 'B': + this->_iprep_num_buckets = strtol(optarg, nullptr, 10); + if (this->_iprep_num_buckets >= 100) { + TSError("sni_limiter: iprep_num_buckets must be in the range 1 .. 99, IP reputation disabled"); + this->_iprep_num_buckets = 0; + } + break; + case 'S': + this->_iprep_size = strtol(optarg, nullptr, 10); + break; + case 'L': + this->iprep_permablock_count = strtol(optarg, nullptr, 10); + break; + case 'P': + this->iprep_permablock_threshold = strtol(optarg, nullptr, 10); + break; + case 'A': + this->_iprep_perma_max_age = std::chrono::seconds(strtol(optarg, nullptr, 10)); + break; } if (opt == -1) { break; } } + // Enable and initialize the IP reputation if asked for + if (this->_iprep_num_buckets > 0 && this->_iprep_size > 0) { + TSDebug(PLUGIN_NAME, "Calling and _initialized is %d\n", this->iprep.initialized()); + this->iprep.initialize(this->_iprep_num_buckets, this->_iprep_size); + TSDebug(PLUGIN_NAME, "IP-reputation enabled with %u buckets, max size is 2^%u", this->_iprep_num_buckets, this->_iprep_size); + + TSDebug(PLUGIN_NAME, "Called and _initialized is %d\n", this->iprep.initialized()); + + // These settings are optional + if (this->_iprep_max_age != std::chrono::seconds::zero()) { + this->iprep.maxAge(this->_iprep_max_age); + } + if (this->_iprep_perma_max_age != std::chrono::seconds::zero()) { + this->iprep.permaMaxAge(this->_iprep_perma_max_age); + } + } + return true; } diff --git a/plugins/experimental/rate_limit/sni_limiter.h b/plugins/experimental/rate_limit/sni_limiter.h index 3889a0819f4..93b1b5558d0 100644 --- a/plugins/experimental/rate_limit/sni_limiter.h +++ b/plugins/experimental/rate_limit/sni_limiter.h @@ -18,6 +18,7 @@ #pragma once #include "limiter.h" +#include "ip_reputation.h" #include "ts/ts.h" int sni_limit_cont(TSCont contp, TSEvent event, void *edata); @@ -40,4 +41,25 @@ class SniRateLimiter : public RateLimiter } bool initialize(int argc, const char *argv[]); + + // ToDo: this ought to go into some better global IP reputation pool / settings. Waiting for YAML... + IpReputation::SieveLru iprep; + uint32_t iprep_permablock_count = 0; // "Hits" limit for blocking permanently + uint32_t iprep_permablock_threshold = 0; // Pressure threshold for permanent block + + // Calculate the pressure, which is either a negative number (ignore), or a number 0-. + // 0 == block only perma-blocks. + int32_t + pressure() const + { + return ((active() - 1) / static_cast(limit) * 100) - (99 - _iprep_num_buckets); + } + +private: + // ToDo: These should be moved to global configurations to have one shared IP Reputation. + // today the configuration of this is so klunky, that there is no easy way to make it "global". + std::chrono::seconds _iprep_max_age = std::chrono::seconds::zero(); // Max age in the SieveLRUs for regular buckets + std::chrono::seconds _iprep_perma_max_age = std::chrono::seconds::zero(); // Max age in the SieveLRUs for perma-block buckets + uint32_t _iprep_num_buckets = 10; // Number of buckets. ToDo: leave this at 10 always + uint32_t _iprep_size = 15; // Size of the biggest bucket; 15 == 2^15 == 32768 }; diff --git a/plugins/experimental/rate_limit/sni_selector.cc b/plugins/experimental/rate_limit/sni_selector.cc index d41b4df0690..e4c22d02d9d 100644 --- a/plugins/experimental/rate_limit/sni_selector.cc +++ b/plugins/experimental/rate_limit/sni_selector.cc @@ -86,6 +86,10 @@ SniSelector::insert(std::string_view sni, SniRateLimiter *limiter) SniRateLimiter * SniSelector::find(std::string_view sni) { + if (sni.empty()) { // Likely shouldn't happen, but we can shortcircuit + return nullptr; + } + auto limiter = _limiters.find(sni); if (limiter != _limiters.end()) { @@ -105,17 +109,18 @@ SniSelector::factory(const char *sni_list, int argc, const char *argv[]) char *saveptr; char *sni = strdup(sni_list); // We make a copy of the sni list, to not touch the original string char *token = strtok_r(sni, ",", &saveptr); - SniRateLimiter def_limiter; - - def_limiter.initialize(argc, argv); // Creates the template limiter - _needs_queue_cont = (def_limiter.max_queue > 0); + // Todo: We are repeating initializing here with the same configurations, but once we move this to + // YAML, and refactor this, it'll be better. And this is not particularly expensive. while (nullptr != token) { - SniRateLimiter *limiter = new SniRateLimiter(def_limiter); // Make a shallow copy + SniRateLimiter *limiter = new SniRateLimiter(); TSReleaseAssert(limiter); + limiter->initialize(argc, argv); limiter->description = token; + _needs_queue_cont = (limiter->max_queue > 0); + insert(std::string_view(limiter->description), limiter); token = strtok_r(nullptr, ",", &saveptr); }