From d7c2ffa59ff9a91ca92bf4752eb56d512bae3917 Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Wed, 31 Jan 2024 23:25:34 +0000 Subject: [PATCH] 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. --- doc/admin-guide/files/remap.config.en.rst | 3 + include/proxy/http/HttpTransact.h | 2 +- include/proxy/http/remap/AclFiltering.h | 21 ++- include/proxy/http/remap/UrlMapping.h | 2 +- src/proxy/http/HttpSM.cc | 4 +- src/proxy/http/HttpTransact.cc | 6 +- src/proxy/http/remap/RemapConfig.cc | 18 +- src/proxy/http/remap/UrlRewrite.cc | 67 ++++--- tests/gold_tests/remap/remap_acl.test.py | 169 ++++++++++++++++++ .../remap/remap_acl_all_denied.replay.yaml | 126 +++++++++++++ .../remap_acl_get_post_allowed.replay.yaml | 130 ++++++++++++++ .../remap_acl_get_post_denied.replay.yaml | 124 +++++++++++++ 12 files changed, 630 insertions(+), 42 deletions(-) create mode 100644 tests/gold_tests/remap/remap_acl.test.py create mode 100644 tests/gold_tests/remap/remap_acl_all_denied.replay.yaml create mode 100644 tests/gold_tests/remap/remap_acl_get_post_allowed.replay.yaml create mode 100644 tests/gold_tests/remap/remap_acl_get_post_denied.replay.yaml diff --git a/doc/admin-guide/files/remap.config.en.rst b/doc/admin-guide/files/remap.config.en.rst index 407e4fde033..8215b6795df 100644 --- a/doc/admin-guide/files/remap.config.en.rst +++ b/doc/admin-guide/files/remap.config.en.rst @@ -459,6 +459,9 @@ Note that these Acl filters will return a 403 response if the resource is restri The difference between ``@src_ip`` and ``@in_ip`` is that the ``@src_ip`` is the client ip and the ``in_ip`` is the ip address the client is connecting to (the incoming address). +If no IP address is specified for either ``@src_ip`` or ``@in_ip``, the filter will +implicitly apply to all incoming IP addresses. This can be explicitly stated with +``@src_ip=all``. Named Filters ============= diff --git a/include/proxy/http/HttpTransact.h b/include/proxy/http/HttpTransact.h index c089dde2ce5..88f114a23c9 100644 --- a/include/proxy/http/HttpTransact.h +++ b/include/proxy/http/HttpTransact.h @@ -682,7 +682,7 @@ class HttpTransact bool is_upgrade_request = false; bool is_websocket = false; bool did_upgrade_succeed = false; - bool client_connection_enabled = true; + bool client_connection_allowed = true; bool acl_filtering_performed = false; bool api_cleanup_cache_read = false; bool api_server_response_no_store = false; diff --git a/include/proxy/http/remap/AclFiltering.h b/include/proxy/http/remap/AclFiltering.h index 53d2a91e636..b7ff143e0fa 100644 --- a/include/proxy/http/remap/AclFiltering.h +++ b/include/proxy/http/remap/AclFiltering.h @@ -37,22 +37,27 @@ static int const ACL_FILTER_MAX_IN_IP = 8; static int const ACL_FILTER_MAX_ARGV = 512; struct src_ip_info_t { - IpAddr start; ///< Minimum value in range. - IpAddr end; ///< Maximum value in range. - bool invert; ///< Should we "invert" the meaning of this IP range ("not in range") + IpAddr start; ///< Minimum value in range. + IpAddr end; ///< Maximum value in range. + bool invert; ///< Should we "invert" the meaning of this IP range ("not in range") + bool match_all_addresses; ///< This rule should match all IP addresses. void reset() { start.invalidate(); end.invalidate(); - invert = false; + invert = false; + match_all_addresses = false; } /// @return @c true if @a ip is inside @a this range. bool contains(IpEndpoint const &ip) { + if (match_all_addresses) { + return true; + } IpAddr addr{ip}; return addr.cmp(start) >= 0 && addr.cmp(end) <= 0; } @@ -70,10 +75,10 @@ class acl_filter_rule acl_filter_rule *next = nullptr; char *filter_name = nullptr; // optional filter name unsigned int allow_flag : 1, // action allow deny - src_ip_valid : 1, // src_ip range valid - in_ip_valid : 1, - active_queue_flag : 1, // filter is in active state (used by .useflt directive) - internal : 1; // filter internal HTTP requests + src_ip_valid : 1, // src_ip (client's src IP) range is specified and valid + in_ip_valid : 1, // in_ip (client's dest IP) range is specified and valid + active_queue_flag : 1, // filter is in active state (used by .useflt directive) + internal : 1; // filter internal HTTP requests // we need arguments as string array for directive processing int argc = 0; // argument counter (only for filter defs) diff --git a/include/proxy/http/remap/UrlMapping.h b/include/proxy/http/remap/UrlMapping.h index 511e949f424..2779ac9563d 100644 --- a/include/proxy/http/remap/UrlMapping.h +++ b/include/proxy/http/remap/UrlMapping.h @@ -116,7 +116,7 @@ class url_mapping referer_info *referer_list = nullptr; redirect_tag_str *redir_chunk_list = nullptr; bool ip_allow_check_enabled_p = false; - acl_filter_rule *filter = nullptr; // acl filtering (list of rules) + acl_filter_rule *filter = nullptr; // acl filtering (linked list of rules) LINK(url_mapping, link); // For use with the main Queue linked list holding all the mapping std::shared_ptr strategy = nullptr; std::string remapKey; diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc index 2fbacdacdf1..c4eadf0ccb7 100644 --- a/src/proxy/http/HttpSM.cc +++ b/src/proxy/http/HttpSM.cc @@ -4301,7 +4301,7 @@ HttpSM::check_sni_host() swoc::bwprint(error_bw_buffer, "No SNI for TLS request: connecting to {} for host='{}', returning a 403", t_state.client_info.dst_addr, std::string_view{host_name, static_cast(host_len)}); Log::error("%s", error_bw_buffer.c_str()); - this->t_state.client_connection_enabled = false; + this->t_state.client_connection_allowed = false; } } else if (strncasecmp(host_name, sni_value, host_len) != 0) { // Name mismatch 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() swoc::bwprint(error_bw_buffer, "SNI/hostname mismatch: connecting to {} for host='{}' sni='{}', returning a 403", t_state.client_info.dst_addr, std::string_view{host_name, static_cast(host_len)}, sni_value); Log::error("%s", error_bw_buffer.c_str()); - this->t_state.client_connection_enabled = false; + this->t_state.client_connection_allowed = false; } } else { SMDbg(dbg_ctl_ssl_sni, "SNI/hostname successfully match sni=%s host=%.*s", sni_value, host_len, host_name); diff --git a/src/proxy/http/HttpTransact.cc b/src/proxy/http/HttpTransact.cc index f4723134873..5a520587f60 100644 --- a/src/proxy/http/HttpTransact.cc +++ b/src/proxy/http/HttpTransact.cc @@ -1106,7 +1106,7 @@ HttpTransact::EndRemapRequest(State *s) ///////////////////////////////////////////////////////////////////////// // We must close this connection if client_connection_enabled == false // ///////////////////////////////////////////////////////////////////////// - if (!s->client_connection_enabled) { + if (!s->client_connection_allowed) { build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied"); s->reverse_proxy = false; goto done; @@ -6491,7 +6491,7 @@ void HttpTransact::process_quick_http_filter(State *s, int method) { // connection already disabled by previous ACL filtering, don't modify it. - if (!s->client_connection_enabled) { + if (!s->client_connection_allowed) { return; } @@ -6527,7 +6527,7 @@ HttpTransact::process_quick_http_filter(State *s, int method) TxnDbg(dbg_ctl_ip_allow, "Line %d denial for '%.*s' from %s", acl.source_line(), method_str_len, method_str, ats_ip_ntop(&s->client_info.src_addr.sa, ipb, sizeof(ipb))); } - s->client_connection_enabled = false; + s->client_connection_allowed = false; } } } diff --git a/src/proxy/http/remap/RemapConfig.cc b/src/proxy/http/remap/RemapConfig.cc index e76dd7dca6f..a958284acc1 100644 --- a/src/proxy/http/remap/RemapConfig.cc +++ b/src/proxy/http/remap/RemapConfig.cc @@ -21,6 +21,7 @@ * limitations under the License. */ +#include "proxy/http/remap/AclFiltering.h" #include "swoc/swoc_file.h" #include "proxy/http/remap/RemapConfig.h" @@ -450,6 +451,7 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg Debug("url_rewrite", "[validate_filter_args] new acl_filter_rule class was created during remap rule processing"); } + bool ip_is_listed = false; for (i = 0; i < argc; i++) { unsigned long ul; bool hasarg; @@ -509,7 +511,10 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg if (ul & REMAP_OPTFLG_INVERT) { ipi->invert = true; } - if (ats_ip_range_parse(argptr, ipi->start, ipi->end) != 0) { + std::string_view arg{argptr}; + if (arg == "all") { + ipi->match_all_addresses = true; + } else if (ats_ip_range_parse(argptr, ipi->start, ipi->end) != 0) { Debug("url_rewrite", "[validate_filter_args] Unable to parse IP value in %s", argv[i]); snprintf(errStrBuf, errStrBufSize, "Unable to parse IP value in %s", argv[i]); errStrBuf[errStrBufSize - 1] = 0; @@ -529,6 +534,7 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg if (ipi) { rule->src_ip_cnt++; rule->src_ip_valid = 1; + ip_is_listed = true; } } @@ -568,6 +574,7 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg if (ipi) { rule->in_ip_cnt++; rule->in_ip_valid = 1; + ip_is_listed = true; } } @@ -593,6 +600,15 @@ remap_validate_filter_args(acl_filter_rule **rule_pp, const char **argv, int arg } } + if (!ip_is_listed) { + // If no IP addresses are listed, treat that like `@src_ip=all`. + ink_release_assert(rule->src_ip_valid == 0 && rule->src_ip_cnt == 0); + src_ip_info_t *ipi = &rule->src_ip_array[rule->src_ip_cnt]; + ipi->match_all_addresses = true; + rule->src_ip_cnt++; + rule->src_ip_valid = 1; + } + if (is_debug_tag_set("url_rewrite")) { rule->print(); } diff --git a/src/proxy/http/remap/UrlRewrite.cc b/src/proxy/http/remap/UrlRewrite.cc index 79645ae19cc..63fd302002a 100644 --- a/src/proxy/http/remap/UrlRewrite.cc +++ b/src/proxy/http/remap/UrlRewrite.cc @@ -399,7 +399,7 @@ UrlRewrite::ReverseMap(HTTPHdr *response_header) void UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map) { - if (unlikely(!s || s->acl_filtering_performed || !s->client_connection_enabled)) { + if (unlikely(!s || s->acl_filtering_performed || !s->client_connection_allowed)) { return; } @@ -411,43 +411,50 @@ UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map) ink_release_assert(ats_is_ip(&s->client_info.src_addr)); - s->client_connection_enabled = true; // Default is that we allow things unless some filter matches + s->client_connection_allowed = true; // Default is that we allow things unless some filter matches - for (acl_filter_rule *rp = map->filter; rp; rp = rp->next) { - bool match = true; + int rule_index = 0; + for (acl_filter_rule *rp = map->filter; rp; rp = rp->next, ++rule_index) { + bool method_matches = true; if (rp->method_restriction_enabled) { if (method_wksidx >= 0 && method_wksidx < HTTP_WKSIDX_METHODS_CNT) { - match = rp->standard_method_lookup[method_wksidx]; + method_matches = rp->standard_method_lookup[method_wksidx]; } else if (!rp->nonstandard_methods.empty()) { - match = false; + method_matches = false; } else { int method_str_len; const char *method_str = s->hdr_info.client_request.method_get(&method_str_len); - match = rp->nonstandard_methods.count(std::string(method_str, method_str_len)); + method_matches = rp->nonstandard_methods.count(std::string(method_str, method_str_len)); } + } else { + // No method specified, therefore all match. + method_matches = true; } - if (match && rp->src_ip_valid) { - match = false; - for (int j = 0; j < rp->src_ip_cnt && !match; j++) { + // Is there a @src_ip specified? If so, check it. + bool ip_matches = false; + if (rp->src_ip_valid) { + ip_matches = false; + for (int j = 0; j < rp->src_ip_cnt && !ip_matches; j++) { bool in_range = rp->src_ip_array[j].contains(s->client_info.src_addr); if (rp->src_ip_array[j].invert) { if (!in_range) { - match = true; + ip_matches = true; } } else { if (in_range) { - match = true; + ip_matches = true; } } } } - if (match && rp->in_ip_valid) { - Debug("url_rewrite", "match was true and we have specified a in_ip field"); - match = false; - for (int j = 0; j < rp->in_ip_cnt && !match; j++) { + // Is there an @in_ip specified? If so, check it. + if (ip_matches && rp->in_ip_valid) { + Debug("url_rewrite", "src_ip match was true, checking the specified in_ip range."); + ip_matches = false; + for (int j = 0; j < rp->in_ip_cnt && !ip_matches; j++) { IpEndpoint incoming_addr; incoming_addr.assign(s->state_machine->get_ua_txn()->get_netvc()->get_local_addr()); if (is_debug_tag_set("url_rewrite")) { @@ -460,28 +467,36 @@ UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map) bool in_range = rp->in_ip_array[j].contains(incoming_addr); if (rp->in_ip_array[j].invert) { if (!in_range) { - match = true; + ip_matches = true; } } else { if (in_range) { - match = true; + ip_matches = true; } } } } if (rp->internal) { - match = s->state_machine->get_ua_txn()->get_netvc()->get_is_internal_request(); - Debug("url_rewrite", "%s an internal request", match ? "matched" : "didn't match"); + ip_matches = s->state_machine->get_ua_txn()->get_netvc()->get_is_internal_request(); + Debug("url_rewrite", "%s an internal request", ip_matches ? "matched" : "didn't match"); } - if (match) { - // We have a match, stop evaluating filters - Debug("url_rewrite", "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying"); - s->client_connection_enabled = rp->allow_flag; + Debug("url_rewrite", "%d: ACL filter %s rule matches by ip: %s, by method: %s", rule_index, + (rp->allow_flag ? "allow" : "deny"), (ip_matches ? "true" : "false"), (method_matches ? "true" : "false")); + + if (ip_matches) { + // The rule matches. Handle the method according to the rule. + if (method_matches) { + // Did they specify allowing the listed methods, or denying them? + Debug("url_rewrite", "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying"); + s->client_connection_allowed = rp->allow_flag; + } else { + Debug("url_rewrite", "ACL rule matched on IP but not on method, action: %s, %s the request", + (rp->allow_flag ? "allow" : "deny"), (rp->allow_flag ? "denying" : "allowing")); + s->client_connection_allowed = !rp->allow_flag; + } break; - } else { - Debug("url_rewrite", "did NOT match ACL filter rule, %s request", rp->allow_flag ? "denying" : "allowing"); } } } /* end of for(rp = map->filter;rp;rp = rp->next) */ diff --git a/tests/gold_tests/remap/remap_acl.test.py b/tests/gold_tests/remap/remap_acl.test.py new file mode 100644 index 00000000000..3da753eb8e1 --- /dev/null +++ b/tests/gold_tests/remap/remap_acl.test.py @@ -0,0 +1,169 @@ +''' +Verify remap.config acl behavior. +''' +# 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. + +import os +import re +from typing import List, Tuple + +Test.Summary = ''' +Verify remap.config acl behavior. +''' + + +class Test_remap_acl: + """Configure a test to verify remap.config acl behavior.""" + + _ts_counter: int = 0 + _server_counter: int = 0 + _client_counter: int = 0 + + def __init__( + self, name: str, replay_file: str, ip_allow_content: str, deactivate_ip_allow: bool, acl_configuration: str, + named_acls: List[Tuple[str, str]], expected_responses: list[int]): + """Initialize the test. + + :param name: The name of the test. + :param replay_file: The replay file to be used. + :param ip_allow_content: The ip_allow configuration to be used. + :param deactivate_ip_allow: Whether to deactivate the ip_allow filter. + :param acl_configuration: The ACL configuration to be used. + :param named_acls: The set of named ACLs to configure and use. + :param expect_responses: The in-order expected responses from the proxy. + """ + self._replay_file = replay_file + self._ip_allow_content = ip_allow_content + self._deactivate_ip_allow = deactivate_ip_allow + self._acl_configuration = acl_configuration + self._named_acls = named_acls + self._expected_responses = expected_responses + + tr = Test.AddTestRun(name) + self._configure_server(tr) + self._configure_traffic_server(tr) + self._configure_client(tr) + + def _configure_server(self, tr: 'TestRun') -> None: + """Configure the server. + + :param tr: The TestRun object to associate the server process with. + """ + name = f"server-{Test_remap_acl._server_counter}" + server = tr.AddVerifierServerProcess(name, self._replay_file) + Test_remap_acl._server_counter += 1 + self._server = server + + def _configure_traffic_server(self, tr: 'TestRun') -> None: + """Configure Traffic Server. + + :param tr: The TestRun object to associate the Traffic Server process with. + """ + + name = f"ts-{Test_remap_acl._ts_counter}" + ts = tr.MakeATSProcess(name, enable_cache=False, enable_tls=True) + Test_remap_acl._ts_counter += 1 + self._ts = ts + + ts.addDefaultSSLFiles() + ts.Disk.ssl_multicert_config.AddLine('dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key') + ts.Disk.records_config.update( + { + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'http|url|remap', + 'proxy.config.http.push_method_enabled': 1, + 'proxy.config.ssl.server.cert.path': ts.Variables.SSLDir, + 'proxy.config.quic.no_activity_timeout_in': 0, + 'proxy.config.ssl.server.private_key.path': ts.Variables.SSLDir, + 'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE', + 'proxy.config.http.connect_ports': self._server.Variables.http_port, + }) + + remap_config_lines = [] + if self._deactivate_ip_allow: + remap_config_lines.append('.deactivatefilter ip_allow') + + # First, define the name ACLs (filters). + for name, definition in self._named_acls: + remap_config_lines.append(f'.definefilter {name} {definition}') + # Now activate them. + for name, _ in self._named_acls: + remap_config_lines.append(f'.activatefilter {name}') + + remap_config_lines.append(f'map / http://127.0.0.1:{self._server.Variables.http_port} {self._acl_configuration}') + ts.Disk.remap_config.AddLines(remap_config_lines) + ts.Disk.ip_allow_yaml.AddLines(self._ip_allow_content.split("\n")) + + def _configure_client(self, tr: 'TestRun') -> None: + """Run the test. + + :param tr: The TestRun object to associate the client process with. + """ + + name = f"client-{Test_remap_acl._client_counter}" + p = tr.AddVerifierClientProcess(name, self._replay_file, https_ports=[self._ts.Variables.ssl_port]) + Test_remap_acl._client_counter += 1 + p.StartBefore(self._server) + p.StartBefore(self._ts) + + codes = [str(code) for code in self._expected_responses] + p.Streams.stdout += Testers.ContainsExpression( + '.*'.join(codes), "Verifying the expected order of responses", reflags=re.DOTALL | re.MULTILINE) + + +IP_ALLOW_CONTENT = f''' +ip_allow: + - apply: in + ip_addrs: 0/0 + action: allow +''' + +test_ip_allow_optional_methods = Test_remap_acl( + "Verify non-allowed methods are blocked.", + replay_file='remap_acl_get_post_allowed.replay.yaml', + ip_allow_content=IP_ALLOW_CONTENT, + deactivate_ip_allow=True, + acl_configuration='@action=allow @src_ip=127.0.0.1 @method=GET @method=POST', + named_acls=[], + expected_responses=[200, 200, 403, 403, 403]) + +test_ip_allow_optional_methods = Test_remap_acl( + "Verify denied methods are blocked.", + replay_file='remap_acl_get_post_denied.replay.yaml', + ip_allow_content=IP_ALLOW_CONTENT, + deactivate_ip_allow=True, + acl_configuration='@action=deny @src_ip=127.0.0.1 @method=GET @method=POST', + named_acls=[], + expected_responses=[403, 403, 200, 200, 400]) + +test_ip_allow_optional_methods = Test_remap_acl( + "Verify defined filters are evaluated before remap lines.", + replay_file='remap_acl_all_denied.replay.yaml', + ip_allow_content=IP_ALLOW_CONTENT, + deactivate_ip_allow=True, + acl_configuration='@action=allow @src_ip=127.0.0.1 @method=GET @method=POST', + named_acls=[('deny', '@action=deny @src_ip=0.0.0.0-255.255.255.255')], + expected_responses=[403, 403, 403, 403, 403]) + +test_ip_allow_optional_methods = Test_remap_acl( + "Verify a default deny filter rule works.", + replay_file='remap_acl_all_denied.replay.yaml', + ip_allow_content=IP_ALLOW_CONTENT, + deactivate_ip_allow=True, + acl_configuration='@action=allow @src_ip=1.2.3.4 @method=GET @method=POST', + named_acls=[('deny', '@action=deny @src_ip=0.0.0.0-255.255.255.255')], + expected_responses=[403, 403, 403, 403, 403]) diff --git a/tests/gold_tests/remap/remap_acl_all_denied.replay.yaml b/tests/gold_tests/remap/remap_acl_all_denied.replay.yaml new file mode 100644 index 00000000000..d15782b0463 --- /dev/null +++ b/tests/gold_tests/remap/remap_acl_all_denied.replay.yaml @@ -0,0 +1,126 @@ +# 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. + +# This expects a remap.config that denies all request methods. + +meta: + version: "1.0" + + blocks: + - standard_response: &standard_response + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Content-Length, 20 ] + +sessions: +- protocol: + - name: http + version: 1 + - name: tls + sni: test_sni + transactions: + + - client-request: + method: "GET" + version: "1.1" + url: /test/ip_allow/test_get + headers: + fields: + - [ Content-Length, 0 ] + - [ uuid, get ] + - [ X-Request, get ] + + # Not received. + <<: *standard_response + + proxy-response: + status: 403 + + - client-request: + method: "POST" + version: "1.1" + url: /test/ip_allow/test_post + headers: + fields: + - [Content-Length, 10] + - [ uuid, post ] + - [ X-Request, post ] + + # Not received. + <<: *standard_response + + proxy-response: + status: 403 + + - client-request: + method: "PUT" + version: "1.1" + url: /test/ip_allow/test_put + headers: + fields: + - [ Host, example.com ] + - [ uuid, put ] + - [ X-Request, put ] + - [ Content-Length, 113 ] + content: + encoding: plain + data: "HTTP/1.1 200 OK\nServer: ATS/10.0.0\nAccept-Ranges: bytes\nContent-Length: 6\nCache-Control: public,max-age=2\n\nCACHED" + + # Not received. + <<: *standard_response + + proxy-response: + status: 403 + + # DELETE rejected + - client-request: + method: "DELETE" + version: "1.1" + url: /test/ip_allow/test_delete + headers: + fields: + - [ Host, example.com ] + - [ uuid, delete ] + - [ X-Request, delete ] + - [ Content-Length, 0 ] + + <<: *standard_response + + proxy-response: + status: 403 + + # PUSH rejected + - client-request: + method: "PUSH" + version: "1.1" + url: /test/ip_allow/test_push + headers: + fields: + - [ Host, example.com ] + - [ uuid, push ] + - [ X-Request, push ] + - [ Content-Length, 113 ] + content: + encoding: plain + data: "HTTP/1.1 200 OK\nServer: ATS/10.0.0\nAccept-Ranges: bytes\nContent-Length: 6\nCache-Control: public,max-age=2\n\nCACHED" + + <<: *standard_response + + proxy-response: + status: 403 diff --git a/tests/gold_tests/remap/remap_acl_get_post_allowed.replay.yaml b/tests/gold_tests/remap/remap_acl_get_post_allowed.replay.yaml new file mode 100644 index 00000000000..cf08a22a8ae --- /dev/null +++ b/tests/gold_tests/remap/remap_acl_get_post_allowed.replay.yaml @@ -0,0 +1,130 @@ +# 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. + +# This expects a remap.config that allows GET and POST, but denies all other +# methods. + +meta: + version: "1.0" + + blocks: + - standard_response: &standard_response + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Content-Length, 20 ] + +sessions: +- protocol: + - name: http + version: 1 + - name: tls + sni: test_sni + transactions: + + - client-request: + method: "GET" + version: "1.1" + url: /test/ip_allow/test_get + headers: + fields: + - [ Content-Length, 0 ] + - [ uuid, get ] + - [ X-Request, get ] + + <<: *standard_response + + proxy-response: + status: 200 + + # POST also is in the allow list. + - client-request: + method: "POST" + version: "1.1" + url: /test/ip_allow/test_post + headers: + fields: + - [Content-Length, 10] + - [ uuid, post ] + - [ X-Request, post ] + + <<: *standard_response + + proxy-response: + status: 200 + + # PUT rejected + - client-request: + method: "PUT" + version: "1.1" + url: /test/ip_allow/test_put + headers: + fields: + - [ Host, example.com ] + - [ uuid, put ] + - [ X-Request, put ] + - [ Content-Length, 113 ] + content: + encoding: plain + data: "HTTP/1.1 200 OK\nServer: ATS/10.0.0\nAccept-Ranges: bytes\nContent-Length: 6\nCache-Control: public,max-age=2\n\nCACHED" + + # Not received. + <<: *standard_response + + # Verify that ATS rejected the PUSH. + proxy-response: + status: 403 + + # DELETE rejected + - client-request: + method: "DELETE" + version: "1.1" + url: /test/ip_allow/test_delete + headers: + fields: + - [ Host, example.com ] + - [ uuid, delete ] + - [ X-Request, delete ] + - [ Content-Length, 0 ] + + <<: *standard_response + + # Verify that ATS rejects the DELETE. + proxy-response: + status: 403 + + # PUSH rejected + - client-request: + method: "PUSH" + version: "1.1" + url: /test/ip_allow/test_push + headers: + fields: + - [ Host, example.com ] + - [ uuid, push ] + - [ X-Request, push ] + - [ Content-Length, 113 ] + content: + encoding: plain + data: "HTTP/1.1 200 OK\nServer: ATS/10.0.0\nAccept-Ranges: bytes\nContent-Length: 6\nCache-Control: public,max-age=2\n\nCACHED" + + <<: *standard_response + + # Verify that ATS rejected the PUSH. + proxy-response: + status: 403 diff --git a/tests/gold_tests/remap/remap_acl_get_post_denied.replay.yaml b/tests/gold_tests/remap/remap_acl_get_post_denied.replay.yaml new file mode 100644 index 00000000000..2a824456481 --- /dev/null +++ b/tests/gold_tests/remap/remap_acl_get_post_denied.replay.yaml @@ -0,0 +1,124 @@ +# 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. + +# This expects a remap.config that denies GET and POST, but allows all other +# methods. + +meta: + version: "1.0" + + blocks: + - standard_response: &standard_response + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Content-Length, 20 ] + +sessions: +- protocol: + - name: http + version: 1 + - name: tls + sni: test_sni + transactions: + + - client-request: + method: "GET" + version: "1.1" + url: /test/ip_allow/test_get + headers: + fields: + - [ Content-Length, 0 ] + - [ uuid, get ] + - [ X-Request, get ] + + <<: *standard_response + + proxy-response: + status: 403 + + - client-request: + method: "POST" + version: "1.1" + url: /test/ip_allow/test_post + headers: + fields: + - [Content-Length, 10] + - [ uuid, post ] + - [ X-Request, post ] + + <<: *standard_response + + proxy-response: + status: 403 + + # All other methods are allowed. + - client-request: + method: "PUT" + version: "1.1" + url: /test/ip_allow/test_put + headers: + fields: + - [ Host, example.com ] + - [ uuid, put ] + - [ X-Request, put ] + - [ Content-Length, 113 ] + content: + encoding: plain + data: "HTTP/1.1 200 OK\nServer: ATS/10.0.0\nAccept-Ranges: bytes\nContent-Length: 6\nCache-Control: public,max-age=2\n\nCACHED" + + <<: *standard_response + + proxy-response: + status: 200 + + - client-request: + method: "DELETE" + version: "1.1" + url: /test/ip_allow/test_delete + headers: + fields: + - [ Host, example.com ] + - [ uuid, delete ] + - [ X-Request, delete ] + - [ Content-Length, 0 ] + + <<: *standard_response + + proxy-response: + status: 200 + + - client-request: + method: "PUSH" + version: "1.1" + url: /test/ip_allow/test_push + headers: + fields: + - [ Host, example.com ] + - [ uuid, push ] + - [ X-Request, push ] + - [ Content-Length, 113 ] + content: + encoding: plain + data: "HTTP/1.1 200 OK\nServer: ATS/10.0.0\nAccept-Ranges: bytes\nContent-Length: 6\nCache-Control: public,max-age=2\n\nCACHED" + + <<: *standard_response + + # ATS will allow the PUSH, but issue a 400 saying that it can't cache it. + proxy-response: + status: 400