Skip to content

Commit d7c2ffa

Browse files
committed
Fixing remap acl rules for @action=allow
If a user specifies an @action=allow remap.config ACL rule, then the implication is that requests with methods not in the allow list would be denied. Before this patch, allow ACL rules would just never deny. This fixes that behavior so that allow ACL rules that match on IP but not on method deny.
1 parent e935f3e commit d7c2ffa

File tree

12 files changed

+630
-42
lines changed

12 files changed

+630
-42
lines changed

doc/admin-guide/files/remap.config.en.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,9 @@ Note that these Acl filters will return a 403 response if the resource is restri
459459

460460
The difference between ``@src_ip`` and ``@in_ip`` is that the ``@src_ip`` is the client
461461
ip and the ``in_ip`` is the ip address the client is connecting to (the incoming address).
462+
If no IP address is specified for either ``@src_ip`` or ``@in_ip``, the filter will
463+
implicitly apply to all incoming IP addresses. This can be explicitly stated with
464+
``@src_ip=all``.
462465

463466
Named Filters
464467
=============

include/proxy/http/HttpTransact.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ class HttpTransact
682682
bool is_upgrade_request = false;
683683
bool is_websocket = false;
684684
bool did_upgrade_succeed = false;
685-
bool client_connection_enabled = true;
685+
bool client_connection_allowed = true;
686686
bool acl_filtering_performed = false;
687687
bool api_cleanup_cache_read = false;
688688
bool api_server_response_no_store = false;

include/proxy/http/remap/AclFiltering.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,27 @@ static int const ACL_FILTER_MAX_IN_IP = 8;
3737
static int const ACL_FILTER_MAX_ARGV = 512;
3838

3939
struct src_ip_info_t {
40-
IpAddr start; ///< Minimum value in range.
41-
IpAddr end; ///< Maximum value in range.
42-
bool invert; ///< Should we "invert" the meaning of this IP range ("not in range")
40+
IpAddr start; ///< Minimum value in range.
41+
IpAddr end; ///< Maximum value in range.
42+
bool invert; ///< Should we "invert" the meaning of this IP range ("not in range")
43+
bool match_all_addresses; ///< This rule should match all IP addresses.
4344

4445
void
4546
reset()
4647
{
4748
start.invalidate();
4849
end.invalidate();
49-
invert = false;
50+
invert = false;
51+
match_all_addresses = false;
5052
}
5153

5254
/// @return @c true if @a ip is inside @a this range.
5355
bool
5456
contains(IpEndpoint const &ip)
5557
{
58+
if (match_all_addresses) {
59+
return true;
60+
}
5661
IpAddr addr{ip};
5762
return addr.cmp(start) >= 0 && addr.cmp(end) <= 0;
5863
}
@@ -70,10 +75,10 @@ class acl_filter_rule
7075
acl_filter_rule *next = nullptr;
7176
char *filter_name = nullptr; // optional filter name
7277
unsigned int allow_flag : 1, // action allow deny
73-
src_ip_valid : 1, // src_ip range valid
74-
in_ip_valid : 1,
75-
active_queue_flag : 1, // filter is in active state (used by .useflt directive)
76-
internal : 1; // filter internal HTTP requests
78+
src_ip_valid : 1, // src_ip (client's src IP) range is specified and valid
79+
in_ip_valid : 1, // in_ip (client's dest IP) range is specified and valid
80+
active_queue_flag : 1, // filter is in active state (used by .useflt directive)
81+
internal : 1; // filter internal HTTP requests
7782

7883
// we need arguments as string array for directive processing
7984
int argc = 0; // argument counter (only for filter defs)

include/proxy/http/remap/UrlMapping.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class url_mapping
116116
referer_info *referer_list = nullptr;
117117
redirect_tag_str *redir_chunk_list = nullptr;
118118
bool ip_allow_check_enabled_p = false;
119-
acl_filter_rule *filter = nullptr; // acl filtering (list of rules)
119+
acl_filter_rule *filter = nullptr; // acl filtering (linked list of rules)
120120
LINK(url_mapping, link); // For use with the main Queue linked list holding all the mapping
121121
std::shared_ptr<NextHopSelectionStrategy> strategy = nullptr;
122122
std::string remapKey;

src/proxy/http/HttpSM.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4301,7 +4301,7 @@ HttpSM::check_sni_host()
43014301
swoc::bwprint(error_bw_buffer, "No SNI for TLS request: connecting to {} for host='{}', returning a 403",
43024302
t_state.client_info.dst_addr, std::string_view{host_name, static_cast<size_t>(host_len)});
43034303
Log::error("%s", error_bw_buffer.c_str());
4304-
this->t_state.client_connection_enabled = false;
4304+
this->t_state.client_connection_allowed = false;
43054305
}
43064306
} else if (strncasecmp(host_name, sni_value, host_len) != 0) { // Name mismatch
43074307
Warning("SNI/hostname mismatch sni=%s host=%.*s action=%s", sni_value, host_len, host_name, action_value);
@@ -4310,7 +4310,7 @@ HttpSM::check_sni_host()
43104310
swoc::bwprint(error_bw_buffer, "SNI/hostname mismatch: connecting to {} for host='{}' sni='{}', returning a 403",
43114311
t_state.client_info.dst_addr, std::string_view{host_name, static_cast<size_t>(host_len)}, sni_value);
43124312
Log::error("%s", error_bw_buffer.c_str());
4313-
this->t_state.client_connection_enabled = false;
4313+
this->t_state.client_connection_allowed = false;
43144314
}
43154315
} else {
43164316
SMDbg(dbg_ctl_ssl_sni, "SNI/hostname successfully match sni=%s host=%.*s", sni_value, host_len, host_name);

src/proxy/http/HttpTransact.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,7 @@ HttpTransact::EndRemapRequest(State *s)
11061106
/////////////////////////////////////////////////////////////////////////
11071107
// We must close this connection if client_connection_enabled == false //
11081108
/////////////////////////////////////////////////////////////////////////
1109-
if (!s->client_connection_enabled) {
1109+
if (!s->client_connection_allowed) {
11101110
build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied");
11111111
s->reverse_proxy = false;
11121112
goto done;
@@ -6491,7 +6491,7 @@ void
64916491
HttpTransact::process_quick_http_filter(State *s, int method)
64926492
{
64936493
// connection already disabled by previous ACL filtering, don't modify it.
6494-
if (!s->client_connection_enabled) {
6494+
if (!s->client_connection_allowed) {
64956495
return;
64966496
}
64976497

@@ -6527,7 +6527,7 @@ HttpTransact::process_quick_http_filter(State *s, int method)
65276527
TxnDbg(dbg_ctl_ip_allow, "Line %d denial for '%.*s' from %s", acl.source_line(), method_str_len, method_str,
65286528
ats_ip_ntop(&s->client_info.src_addr.sa, ipb, sizeof(ipb)));
65296529
}
6530-
s->client_connection_enabled = false;
6530+
s->client_connection_allowed = false;
65316531
}
65326532
}
65336533
}

src/proxy/http/remap/RemapConfig.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
* limitations under the License.
2222
*/
2323

24+
#include "proxy/http/remap/AclFiltering.h"
2425
#include "swoc/swoc_file.h"
2526

2627
#include "proxy/http/remap/RemapConfig.h"
@@ -450,6 +451,7 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg
450451
Debug("url_rewrite", "[validate_filter_args] new acl_filter_rule class was created during remap rule processing");
451452
}
452453

454+
bool ip_is_listed = false;
453455
for (i = 0; i < argc; i++) {
454456
unsigned long ul;
455457
bool hasarg;
@@ -509,7 +511,10 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg
509511
if (ul & REMAP_OPTFLG_INVERT) {
510512
ipi->invert = true;
511513
}
512-
if (ats_ip_range_parse(argptr, ipi->start, ipi->end) != 0) {
514+
std::string_view arg{argptr};
515+
if (arg == "all") {
516+
ipi->match_all_addresses = true;
517+
} else if (ats_ip_range_parse(argptr, ipi->start, ipi->end) != 0) {
513518
Debug("url_rewrite", "[validate_filter_args] Unable to parse IP value in %s", argv[i]);
514519
snprintf(errStrBuf, errStrBufSize, "Unable to parse IP value in %s", argv[i]);
515520
errStrBuf[errStrBufSize - 1] = 0;
@@ -529,6 +534,7 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg
529534
if (ipi) {
530535
rule->src_ip_cnt++;
531536
rule->src_ip_valid = 1;
537+
ip_is_listed = true;
532538
}
533539
}
534540

@@ -568,6 +574,7 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg
568574
if (ipi) {
569575
rule->in_ip_cnt++;
570576
rule->in_ip_valid = 1;
577+
ip_is_listed = true;
571578
}
572579
}
573580

@@ -593,6 +600,15 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg
593600
}
594601
}
595602

603+
if (!ip_is_listed) {
604+
// If no IP addresses are listed, treat that like `@src_ip=all`.
605+
ink_release_assert(rule->src_ip_valid == 0 && rule->src_ip_cnt == 0);
606+
src_ip_info_t *ipi = &rule->src_ip_array[rule->src_ip_cnt];
607+
ipi->match_all_addresses = true;
608+
rule->src_ip_cnt++;
609+
rule->src_ip_valid = 1;
610+
}
611+
596612
if (is_debug_tag_set("url_rewrite")) {
597613
rule->print();
598614
}

src/proxy/http/remap/UrlRewrite.cc

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ UrlRewrite::ReverseMap(HTTPHdr *response_header)
399399
void
400400
UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map)
401401
{
402-
if (unlikely(!s || s->acl_filtering_performed || !s->client_connection_enabled)) {
402+
if (unlikely(!s || s->acl_filtering_performed || !s->client_connection_allowed)) {
403403
return;
404404
}
405405

@@ -411,43 +411,50 @@ UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map)
411411

412412
ink_release_assert(ats_is_ip(&s->client_info.src_addr));
413413

414-
s->client_connection_enabled = true; // Default is that we allow things unless some filter matches
414+
s->client_connection_allowed = true; // Default is that we allow things unless some filter matches
415415

416-
for (acl_filter_rule *rp = map->filter; rp; rp = rp->next) {
417-
bool match = true;
416+
int rule_index = 0;
417+
for (acl_filter_rule *rp = map->filter; rp; rp = rp->next, ++rule_index) {
418+
bool method_matches = true;
418419

419420
if (rp->method_restriction_enabled) {
420421
if (method_wksidx >= 0 && method_wksidx < HTTP_WKSIDX_METHODS_CNT) {
421-
match = rp->standard_method_lookup[method_wksidx];
422+
method_matches = rp->standard_method_lookup[method_wksidx];
422423
} else if (!rp->nonstandard_methods.empty()) {
423-
match = false;
424+
method_matches = false;
424425
} else {
425426
int method_str_len;
426427
const char *method_str = s->hdr_info.client_request.method_get(&method_str_len);
427-
match = rp->nonstandard_methods.count(std::string(method_str, method_str_len));
428+
method_matches = rp->nonstandard_methods.count(std::string(method_str, method_str_len));
428429
}
430+
} else {
431+
// No method specified, therefore all match.
432+
method_matches = true;
429433
}
430434

431-
if (match && rp->src_ip_valid) {
432-
match = false;
433-
for (int j = 0; j < rp->src_ip_cnt && !match; j++) {
435+
// Is there a @src_ip specified? If so, check it.
436+
bool ip_matches = false;
437+
if (rp->src_ip_valid) {
438+
ip_matches = false;
439+
for (int j = 0; j < rp->src_ip_cnt && !ip_matches; j++) {
434440
bool in_range = rp->src_ip_array[j].contains(s->client_info.src_addr);
435441
if (rp->src_ip_array[j].invert) {
436442
if (!in_range) {
437-
match = true;
443+
ip_matches = true;
438444
}
439445
} else {
440446
if (in_range) {
441-
match = true;
447+
ip_matches = true;
442448
}
443449
}
444450
}
445451
}
446452

447-
if (match && rp->in_ip_valid) {
448-
Debug("url_rewrite", "match was true and we have specified a in_ip field");
449-
match = false;
450-
for (int j = 0; j < rp->in_ip_cnt && !match; j++) {
453+
// Is there an @in_ip specified? If so, check it.
454+
if (ip_matches && rp->in_ip_valid) {
455+
Debug("url_rewrite", "src_ip match was true, checking the specified in_ip range.");
456+
ip_matches = false;
457+
for (int j = 0; j < rp->in_ip_cnt && !ip_matches; j++) {
451458
IpEndpoint incoming_addr;
452459
incoming_addr.assign(s->state_machine->get_ua_txn()->get_netvc()->get_local_addr());
453460
if (is_debug_tag_set("url_rewrite")) {
@@ -460,28 +467,36 @@ UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map)
460467
bool in_range = rp->in_ip_array[j].contains(incoming_addr);
461468
if (rp->in_ip_array[j].invert) {
462469
if (!in_range) {
463-
match = true;
470+
ip_matches = true;
464471
}
465472
} else {
466473
if (in_range) {
467-
match = true;
474+
ip_matches = true;
468475
}
469476
}
470477
}
471478
}
472479

473480
if (rp->internal) {
474-
match = s->state_machine->get_ua_txn()->get_netvc()->get_is_internal_request();
475-
Debug("url_rewrite", "%s an internal request", match ? "matched" : "didn't match");
481+
ip_matches = s->state_machine->get_ua_txn()->get_netvc()->get_is_internal_request();
482+
Debug("url_rewrite", "%s an internal request", ip_matches ? "matched" : "didn't match");
476483
}
477484

478-
if (match) {
479-
// We have a match, stop evaluating filters
480-
Debug("url_rewrite", "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying");
481-
s->client_connection_enabled = rp->allow_flag;
485+
Debug("url_rewrite", "%d: ACL filter %s rule matches by ip: %s, by method: %s", rule_index,
486+
(rp->allow_flag ? "allow" : "deny"), (ip_matches ? "true" : "false"), (method_matches ? "true" : "false"));
487+
488+
if (ip_matches) {
489+
// The rule matches. Handle the method according to the rule.
490+
if (method_matches) {
491+
// Did they specify allowing the listed methods, or denying them?
492+
Debug("url_rewrite", "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying");
493+
s->client_connection_allowed = rp->allow_flag;
494+
} else {
495+
Debug("url_rewrite", "ACL rule matched on IP but not on method, action: %s, %s the request",
496+
(rp->allow_flag ? "allow" : "deny"), (rp->allow_flag ? "denying" : "allowing"));
497+
s->client_connection_allowed = !rp->allow_flag;
498+
}
482499
break;
483-
} else {
484-
Debug("url_rewrite", "did NOT match ACL filter rule, %s request", rp->allow_flag ? "denying" : "allowing");
485500
}
486501
}
487502
} /* end of for(rp = map->filter;rp;rp = rp->next) */

0 commit comments

Comments
 (0)