Skip to content

Commit

Permalink
White lister filter can now white list per host
Browse files Browse the repository at this point in the history
Using this syntax:

    white_listed_ips:
      - host: somehostname.org
        ip_range: 123.123.123.123

The old syntax can still be used:

    white_listed_ips:
      - 127.0.0.1

And the two can be mixed:

    white_listed_ips:
      - 127.0.0.1
      - host: somehostname.org
        ip_range: 123.123.123.123
  • Loading branch information
inetic committed Jul 4, 2017
1 parent 923a837 commit 7069043
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 67 deletions.
3 changes: 2 additions & 1 deletion src/ats_event_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ using namespace std;
#include "swabber_interface.h"
#include "ats_event_handler.h"
#include "util.h"
#include "print.h"

//extern TSMutex Banjax::regex_mutex;

Expand Down Expand Up @@ -255,7 +256,7 @@ ATSEventHandler::handle_response(BanjaxContinuation* cd)
// Standard errors are handled nicely by TS but this one caused
// TS to halt for approx 1 minute causing confusion about whether
// the error was at TS side or at origin.
string error_body = str(
string error_body = print::str(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n"
" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
"<html>\n"
Expand Down
10 changes: 6 additions & 4 deletions src/challenge_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,12 +486,15 @@ bool ChallengeManager::is_globally_white_listed(const std::string& ip) const {
FilterResponse
ChallengeManager::on_http_request(const TransactionParts& transaction_parts)
{
const auto& host = transaction_parts.at(TransactionMuncher::HOST);

// look up if this host is serving captcha's or not
TSDebug(BANJAX_PLUGIN_NAME, "Host to be challenged %s", transaction_parts.at(TransactionMuncher::HOST).c_str());
auto challenges_it = host_challenges.find(transaction_parts.at(TransactionMuncher::HOST));
TSDebug(BANJAX_PLUGIN_NAME, "Host to be challenged %s", host.c_str());

auto challenges_it = host_challenges.find(host);

if (challenges_it == host_challenges.end()) {
DEBUG("No challenge for host: ", transaction_parts.at(TransactionMuncher::HOST));
DEBUG("No challenge for host: ", host);
// No challenge for this host
return FilterResponse(FilterResponse::GO_AHEAD_NO_COMMENT);
}
Expand All @@ -510,7 +513,6 @@ ChallengeManager::on_http_request(const TransactionParts& transaction_parts)

for(const auto& cur_challenge : challenges_it->second) {


for (const auto& ipr : cur_challenge->white_listed_ips) {
if (is_match(ip, ipr)) {
DEBUG("Challenge ", cur_challenge->name, " bypassed for ip: ", ip);
Expand Down
3 changes: 2 additions & 1 deletion src/challenge_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ class ChallengeManager : public BanjaxFilter {
TransactionMuncher::HOST |
TransactionMuncher::UA |
TransactionMuncher::URL_WITH_HOST |
TransactionMuncher::COOKIE;}
TransactionMuncher::COOKIE;
}

/**
needs to be overriden by the filter
Expand Down
35 changes: 31 additions & 4 deletions src/global_white_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,47 @@ class GlobalWhiteList {
public:

void insert(const SubnetRange& ip_range) {
white_list.push_back(ip_range);
white_list.push_back(ip_range);
}

void insert(const std::string& host, const SubnetRange& ip_range) {
per_host_white_list[host].insert(ip_range);
}

boost::optional<SubnetRange> is_white_listed(const std::string& ip) const
{
for (const auto& subnet_range : white_list) {
if (is_match(ip, subnet_range)) {
return subnet_range;
for (const auto& r : white_list) {
if (is_match(ip, r)) {
return r;
}
}

return boost::none;
}

boost::optional<SubnetRange> is_white_listed(const std::string& host, const std::string& ip) const
{
if (auto r = is_white_listed(ip)) {
return r;
}

auto i = per_host_white_list.find(host);

if (i == per_host_white_list.end()) {
return boost::none;
}

for (const auto& r : i->second) {
if (is_match(ip, r)) {
return r;
}
}

return boost::none;
}

private:
std::map<std::string, std::set<SubnetRange>> per_host_white_list;
std::list<SubnetRange> white_list;

};
30 changes: 30 additions & 0 deletions src/print.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,36 @@ std::ostream& operator<<(std::ostream& os, ts_event e) {
return os;
}

inline
std::ostream& operator<<(std::ostream& os, const SubnetRange& r) {
char str[INET_ADDRSTRLEN];
auto tmp = htonl(r.first);
inet_ntop(AF_INET, &tmp, str, INET_ADDRSTRLEN);
os << str << "/" << std::hex << r.second << std::dec;
return os;
}

///////////////////////////////////////////////////////////////////////////////
inline
std::string str_impl(std::stringstream& ss) {
return ss.str();
}

template<class Arg, class... Args>
inline
std::string str_impl(std::stringstream& ss, Arg&& arg, Args&&... args) {
ss << arg;
return str_impl(ss, std::forward<Args>(args)...);
}

template<class... Args>
inline
std::string str(Args&&... args) {
std::stringstream ss;
return str_impl(ss, std::forward<Args>(args)...);
}

///////////////////////////////////////////////////////////////////////////////
template<class... Args>
inline void debug(const Args&...args)
{
Expand Down
46 changes: 0 additions & 46 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,52 +38,6 @@ unsigned int const c_max_enc_length = INT_MAX;

int check_ts_version(const char*);

///////////////////////////////////////////////////////////////////////////////
inline
std::string str_impl(std::stringstream& ss) {
return ss.str();
}

template<class Arg, class... Args>
inline
std::string str_impl(std::stringstream& ss, Arg&& arg, Args&&... args) {
ss << arg;
return str_impl(ss, std::forward<Args>(args)...);
}

template<class... Args>
inline
std::string str(Args&&... args) {
std::stringstream ss;
return str_impl(ss, std::forward<Args>(args)...);
}

///////////////////////////////////////////////////////////////////////////////
template<class F>
struct Defer {
F on_destruct;
~Defer() { on_destruct(); }
};

template<class F>
inline
Defer<F> defer(F&& f) { return Defer<F>{std::forward<F>(f)}; }

///////////////////////////////////////////////////////////////////////////////
struct InOut {
std::string message;

template<class... Args> InOut(Args&&... args)
: message(str(std::forward<Args>(args)...))
{
std::cout << "IN: " << message << std::endl;
}

~InOut() {
std::cout << "OUT: " << message << std::endl;
}
};

///////////////////////////////////////////////////////////////////////////////

/**
Expand Down
30 changes: 20 additions & 10 deletions src/white_lister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using namespace std;
#include "util.h"
#include "white_lister.h"
#include "print.h"

/**
reads all the regular expressions from the database.
Expand All @@ -22,20 +23,28 @@ void
WhiteLister::load_config()
{
TSDebug(BANJAX_PLUGIN_NAME, "Loading white lister manager conf");

try
{
YAML::Node white_listed_ips = cfg["white_listed_ips"];

unsigned int count = white_listed_ips.size();
for (auto entry : white_listed_ips) {

if (entry.IsMap()) {
auto host = entry["host"].as<std::string>();
auto range = make_mask_for_range(entry["ip_range"].as<std::string>());

white_list.insert(host, range);

DEBUG("White listing ip range: ", range, " for ", host);
}
else {
SubnetRange range = make_mask_for_range(entry.as<std::string>());

for(unsigned int i = 0; i < count; i++) {
SubnetRange cur_range = make_mask_for_range(white_listed_ips[i].as<std::string>());
white_list.insert(cur_range);
white_list.insert(range);

TSDebug(BANJAX_PLUGIN_NAME, "White listing ip range: %s as %x, %x",
white_listed_ips[i].as<std::string>().c_str(),
cur_range.first,
cur_range.second);
DEBUG("White listing ip range: ", range);
}
}
}
catch(std::exception& e)
Expand All @@ -49,9 +58,10 @@ WhiteLister::load_config()

FilterResponse WhiteLister::on_http_request(const TransactionParts& transaction_parts)
{
auto ip = transaction_parts.at(TransactionMuncher::IP);
const auto& ip = transaction_parts.at(TransactionMuncher::IP);
const auto& host = transaction_parts.at(TransactionMuncher::HOST);

if (auto hit_range = white_list.is_white_listed(ip)) {
if (auto hit_range = white_list.is_white_listed(host, ip)) {
TSDebug(BANJAX_PLUGIN_NAME, "white listed ip: %s in range %X",
ip.c_str(),
hit_range->first);
Expand Down
3 changes: 2 additions & 1 deletion src/white_lister.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class WhiteLister : public BanjaxFilter
}

uint64_t requested_info() override {
return TransactionMuncher::IP;
return TransactionMuncher::IP |
TransactionMuncher::HOST;
}

FilterResponse on_http_request(const TransactionParts& transaction_parts) override;
Expand Down
43 changes: 43 additions & 0 deletions test/banjax_behavior_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,49 @@ def get(page):
result = get(Test.MAGIC_WORD)
self.assertEqual(result, self.server.body)

def test_white_list_per_host(self):
"""
White listed IPs don't need to go through authorization when
on a page marked with MAGIC_WORD.
"""
config = ("priority:\n"
" white_lister: 1\n"
" challenger: 2\n"
"\n"
"white_lister:\n"
" white_listed_ips:\n"
" - host: "+Test.ATS_HOST+"\n"
" ip_range: 127.0.0.1\n"
"challenger:\n"
" difficulty: 30\n"
" key: 'allwearesayingisgivewarachance'\n"
" challenges:\n"
" - name: 'example.co_auth'\n"
" domains:\n"
" - '"+Test.ATS_HOST+"'\n"
" challenge_type: 'auth'\n"
" challenge: '"+Test.AUTH_PAGE+"'\n"
" # sha256('howisbabbyformed?')\n"
" password_hash: 'BdZitmLkeNx6Pq9vKn6027jMWmp63pJJowigedwEdzM='\n"
" magic_word: '"+Test.MAGIC_WORD+"'\n"
" validity_period: 120\n"
"\n")

config += self.AUTH_CHALLENGE_CONFIG

self.replace_config2(config)

# Tell TS to start caching.
self.set_banjax_config("proxy.config.http.cache.http", "1")
self.set_banjax_config("proxy.config.http.cache.required_headers", "0")

def get(page):
return self.do_curl(Test.ATS_HOST + "/" + page)

self.server.body = "page0"
result = get(Test.MAGIC_WORD)
self.assertEqual(result, self.server.body)

def test_bypass_ips_per_host(self):
config = (
"challenger:\n"
Expand Down
4 changes: 4 additions & 0 deletions test/white_lister_unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ BOOST_AUTO_TEST_CASE(white_listed_ip)
//first we make a mock up request
TransactionParts mock_transaction;
mock_transaction[TransactionMuncher::IP] = "127.0.0.1";
mock_transaction[TransactionMuncher::HOST] = "localhost";

FilterResponse cur_filter_result = test->on_http_request(mock_transaction);

Expand All @@ -114,6 +115,7 @@ BOOST_AUTO_TEST_CASE(white_listed_ip2)
//first we make a mock up request
TransactionParts mock_transaction;
mock_transaction[TransactionMuncher::IP] = "127.0.0.1";
mock_transaction[TransactionMuncher::HOST] = "localhost";

FilterResponse cur_filter_result = test->on_http_request(mock_transaction);

Expand All @@ -135,6 +137,7 @@ BOOST_AUTO_TEST_CASE(ordinary_ip)
//first we make a mock up request
TransactionParts mock_transaction;
mock_transaction[TransactionMuncher::IP] = "123.124.125.126";
mock_transaction[TransactionMuncher::HOST] = "localhost";

FilterResponse cur_filter_result = test->on_http_request(mock_transaction);

Expand All @@ -157,6 +160,7 @@ BOOST_AUTO_TEST_CASE(invalid_white_ip)
//first we make a mock up request
TransactionParts mock_transaction;
mock_transaction[TransactionMuncher::IP] = "x.y.z.w";
mock_transaction[TransactionMuncher::HOST] = "localhost";

FilterResponse cur_filter_result = test->on_http_request(mock_transaction);

Expand Down

0 comments on commit 7069043

Please sign in to comment.