This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 7424734bd470d4dca0dfa76b8e86569a6e6ac880 Author: Masaori Koshiba <[email protected]> AuthorDate: Mon Jul 1 09:48:20 2024 +0900 Change remap ACL matching policy to 'Match on IP and Method' (#11433) * Change remap ACL matching policy to 'FIRST_EXPLICIT_MATCH_WINS' * Add AuTest based on Matty's ACL table test Co-authored-by: mattyw <[email protected]> * Fix format * Do not modify url_mapping by PerformACLFiltering Because it's a global data. * Change test format, added some failing tests * Add more test cases * Fix ip_category AuTest * Add deactivate_ip_allow test cases * Attempting to merge the tests into one * tests passing * all tests passing * Fix old test cases and format * Fix import path * Fix PYTHONPATH in autest.sh.in * Rename matching policies to follow docs --------- Co-authored-by: mattyw <[email protected]> (cherry picked from commit 8e6a297f6d8fdbca64c601ff6960a710ec1ec9ea) --- include/proxy/http/HttpTransact.h | 1 + include/proxy/http/remap/UrlRewrite.h | 10 +- src/proxy/http/HttpTransact.cc | 7 +- src/proxy/http/remap/UrlRewrite.cc | 35 ++++- src/records/RecordsConfig.cc | 2 + tests/autest.sh | 1 + tests/autest.sh.in | 1 + tests/gold_tests/ip_allow/ip_category.test.py | 1 + tests/gold_tests/remap/all_acl_combinations.py | 146 +++++++++++++++++++++ ...deny_head_post.replay.yaml => base.replay.yaml} | 63 +++------ tests/gold_tests/remap/deactivate_ip_allow.py | 96 ++++++++++++++ tests/gold_tests/remap/deny_head_post.replay.yaml | 2 - tests/gold_tests/remap/remap_acl.test.py | 128 ++++++++++++++++-- .../remap/remap_acl_all_allowed.replay.yaml | 2 - .../remap/remap_acl_all_denied.replay.yaml | 2 - .../remap/remap_acl_get_allowed.replay.yaml | 2 - .../remap/remap_acl_get_post_allowed.replay.yaml | 2 - .../remap/remap_acl_get_post_denied.replay.yaml | 2 - 18 files changed, 423 insertions(+), 80 deletions(-) diff --git a/include/proxy/http/HttpTransact.h b/include/proxy/http/HttpTransact.h index 84761f1c53..976999d8e6 100644 --- a/include/proxy/http/HttpTransact.h +++ b/include/proxy/http/HttpTransact.h @@ -725,6 +725,7 @@ public: bool transparent_passthrough = false; bool range_in_cache = false; bool is_method_stats_incremented = false; + bool skip_ip_allow_yaml = false; /// True if the response is cacheable because of negative caching configuration. /// diff --git a/include/proxy/http/remap/UrlRewrite.h b/include/proxy/http/remap/UrlRewrite.h index b36d43b286..1a67ed1253 100644 --- a/include/proxy/http/remap/UrlRewrite.h +++ b/include/proxy/http/remap/UrlRewrite.h @@ -63,6 +63,11 @@ public: UrlRewrite() = default; ~UrlRewrite() override; + enum class ACLMatchingPolicy { + MATCH_ON_IP_AND_METHOD = 0, + MATCH_ON_IP_ONLY, + }; + /** Load the configuration. * * This access data in librecords to obtain the information needed for loading the configuration. @@ -152,7 +157,7 @@ public: } }; - void PerformACLFiltering(HttpTransact::State *s, url_mapping *mapping); + void PerformACLFiltering(HttpTransact::State *s, const url_mapping *const mapping); void PrintStore(const MappingsStore &store) const; std::string PrintRemapHits(); std::string PrintRemapHitsStore(MappingsStore &store); @@ -224,7 +229,8 @@ public: NextHopStrategyFactory *strategyFactory = nullptr; private: - bool _valid = false; + bool _valid = false; + ACLMatchingPolicy _acl_matching_policy = ACLMatchingPolicy::MATCH_ON_IP_AND_METHOD; bool _mappingLookup(MappingsStore &mappings, URL *request_url, int request_port, const char *request_host, int request_host_len, UrlMappingContainer &mapping_container); diff --git a/src/proxy/http/HttpTransact.cc b/src/proxy/http/HttpTransact.cc index 17954af3ba..8f30886d60 100644 --- a/src/proxy/http/HttpTransact.cc +++ b/src/proxy/http/HttpTransact.cc @@ -6495,12 +6495,17 @@ HttpTransact::process_quick_http_filter(State *s, int method) return; } - // if ipallow rules are disabled by remap then don't modify anything + // if the "ip_allow" named filter is deactivated in the remap.config, then don't modify anything url_mapping *mp = s->url_map.getMapping(); if (mp && !mp->ip_allow_check_enabled_p) { return; } + // if a ACL filter in the remap.config forces skipping ip_allow.yaml check, do nothing + if (s->skip_ip_allow_yaml) { + return; + } + if (s->state_machine->get_ua_txn()) { auto &acl = s->state_machine->get_ua_txn()->get_acl(); bool deny_request = !acl.isValid(); diff --git a/src/proxy/http/remap/UrlRewrite.cc b/src/proxy/http/remap/UrlRewrite.cc index 141602caf7..ccda792b69 100644 --- a/src/proxy/http/remap/UrlRewrite.cc +++ b/src/proxy/http/remap/UrlRewrite.cc @@ -126,6 +126,22 @@ UrlRewrite::load() } else { Warning("something failed during BuildTable() -- check your remap plugins!"); } + + // ACL Matching Policy + int matching_policy = 0; + REC_ReadConfigInteger(matching_policy, "proxy.config.url_remap.acl_matching_policy"); + switch (matching_policy) { + case 0: + _acl_matching_policy = ACLMatchingPolicy::MATCH_ON_IP_AND_METHOD; + break; + case 1: + _acl_matching_policy = ACLMatchingPolicy::MATCH_ON_IP_ONLY; + break; + default: + Warning("unkown ACL Matching Policy :%d", matching_policy); + _valid = false; + } + return _valid; } @@ -422,7 +438,7 @@ UrlRewrite::ReverseMap(HTTPHdr *response_header) /** Perform fast ACL filtering. */ void -UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map) +UrlRewrite::PerformACLFiltering(HttpTransact::State *s, const url_mapping *const map) { if (unlikely(!s || s->acl_filtering_performed || !s->client_connection_allowed)) { return; @@ -538,14 +554,23 @@ UrlRewrite::PerformACLFiltering(HttpTransact::State *s, url_mapping *map) // Did they specify allowing the listed methods, or denying them? Dbg(dbg_ctl_url_rewrite, "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying"); s->client_connection_allowed = rp->allow_flag; - } else { + + // Since both the IP and method match, this rule will be applied regardless of ACLMatchingPolicy and no need to process + // other filters nor ip_allow.yaml rules + s->skip_ip_allow_yaml = true; + break; + } + + if (_acl_matching_policy == ACLMatchingPolicy::MATCH_ON_IP_ONLY) { + // Flipping the action for unspecified methods. Dbg(dbg_ctl_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; + + // Since IP match and configured policy is MATCH_ON_IP_ONLY, no need to process other filters nor ip_allow.yaml rules. + s->skip_ip_allow_yaml = true; + break; } - // Since we have a matching ACL, no need to process ip_allow.yaml rules. - map->ip_allow_check_enabled_p = false; - break; } } } /* end of for(rp = map->filter;rp;rp = rp->next) */ diff --git a/src/records/RecordsConfig.cc b/src/records/RecordsConfig.cc index c76d6671af..4955ee7de3 100644 --- a/src/records/RecordsConfig.cc +++ b/src/records/RecordsConfig.cc @@ -1066,6 +1066,8 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.url_remap.min_rules_required", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-9]+", RECA_NULL} , + {RECT_CONFIG, "proxy.config.url_remap.acl_matching_policy", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} + , //############################################################################## //# diff --git a/tests/autest.sh b/tests/autest.sh index 048c160c42..b901601139 100755 --- a/tests/autest.sh +++ b/tests/autest.sh @@ -30,6 +30,7 @@ cd "$SCRIPT_DIR" ./prepare_proxy_verifier.sh || fail "Failed to install Proxy Verifier." export PYTHONPATH=$(pwd):$PYTHONPATH +export PYTHONPATH=$(pwd)/gold_tests/remap:$PYTHONPATH ./test-env-check.sh || fail "Failed Python environment checks." # this is for rhel or centos systems echo "Environment config finished. Running AuTest..." diff --git a/tests/autest.sh.in b/tests/autest.sh.in index 1c71560336..7572680a79 100755 --- a/tests/autest.sh.in +++ b/tests/autest.sh.in @@ -5,6 +5,7 @@ # export LD_LIBRARY_PATH=${CMAKE_INSTALL_PREFIX}/lib +export PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/gold_tests/remap:$PYTHONPATH ${RUNPIPENV} run env autest \ --sandbox ${AUTEST_SANDBOX} \ diff --git a/tests/gold_tests/ip_allow/ip_category.test.py b/tests/gold_tests/ip_allow/ip_category.test.py index 11e8f76d92..c22c266df2 100644 --- a/tests/gold_tests/ip_allow/ip_category.test.py +++ b/tests/gold_tests/ip_allow/ip_category.test.py @@ -230,6 +230,7 @@ class Test_ip_category: 'proxy.config.ssl.server.private_key.path': ts.Variables.SSLDir, 'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE', 'proxy.config.http.connect_ports': Test_ip_category._server.Variables.http_port, + 'proxy.config.url_remap.acl_matching_policy': 1, # TODO: adjust expected_responses with the default config }) ts.Disk.remap_config.AddLine( diff --git a/tests/gold_tests/remap/all_acl_combinations.py b/tests/gold_tests/remap/all_acl_combinations.py new file mode 100644 index 0000000000..34487630ee --- /dev/null +++ b/tests/gold_tests/remap/all_acl_combinations.py @@ -0,0 +1,146 @@ +''' +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. + +ALLOW_GET_AND_POST = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: allow + methods: [GET, POST] +''' + +ALLOW_GET = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: allow + methods: [GET] +''' + +DENY_GET = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: deny + methods: [GET] +''' + +DENY_GET_AND_POST = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: deny + methods: [GET, POST] +''' + +# yapf: disable +keys = ["index", "policy", "inline", "named_acl", "ip_allow", "GET response", "POST response"] +all_acl_combinations = [ + [ 0, "ip_and_method", "", "", ALLOW_GET_AND_POST, 200, 200, ], + [ 1, "ip_and_method", "", "", ALLOW_GET, 200, 403, ], + [ 2, "ip_and_method", "", "", DENY_GET, 403, 200, ], + [ 3, "ip_and_method", "", "", DENY_GET_AND_POST, 403, 403, ], + [ 4, "ip_and_method", "", "@action=allow @method=GET", ALLOW_GET_AND_POST, 200, 200, ], + [ 5, "ip_and_method", "", "@action=allow @method=GET", ALLOW_GET, 200, 403, ], + [ 6, "ip_and_method", "", "@action=allow @method=GET", DENY_GET, 200, 200, ], + [ 7, "ip_and_method", "", "@action=allow @method=GET", DENY_GET_AND_POST, 200, 403, ], + [ 8, "ip_and_method", "", "@action=deny @method=GET", ALLOW_GET_AND_POST, 403, 200, ], + [ 9, "ip_and_method", "", "@action=deny @method=GET", ALLOW_GET, 403, 403, ], + [ 10, "ip_and_method", "", "@action=deny @method=GET", DENY_GET, 403, 200, ], + [ 11, "ip_and_method", "", "@action=deny @method=GET", DENY_GET_AND_POST, 403, 403, ], + [ 12, "ip_and_method", "@action=allow @method=GET", "", ALLOW_GET_AND_POST, 200, 200, ], + [ 13, "ip_and_method", "@action=allow @method=GET", "", ALLOW_GET, 200, 403, ], + [ 14, "ip_and_method", "@action=allow @method=GET", "", DENY_GET, 200, 200, ], + [ 15, "ip_and_method", "@action=allow @method=GET", "", DENY_GET_AND_POST, 200, 403, ], + [ 16, "ip_and_method", "@action=allow @method=GET", "@action=allow @method=GET", ALLOW_GET_AND_POST, 200, 200, ], + [ 17, "ip_and_method", "@action=allow @method=GET", "@action=allow @method=GET", ALLOW_GET, 200, 403, ], + [ 18, "ip_and_method", "@action=allow @method=GET", "@action=allow @method=GET", DENY_GET, 200, 200, ], + [ 19, "ip_and_method", "@action=allow @method=GET", "@action=allow @method=GET", DENY_GET_AND_POST, 200, 403, ], + [ 20, "ip_and_method", "@action=allow @method=GET", "@action=deny @method=GET", ALLOW_GET_AND_POST, 200, 200, ], + [ 21, "ip_and_method", "@action=allow @method=GET", "@action=deny @method=GET", ALLOW_GET, 200, 403, ], + [ 22, "ip_and_method", "@action=allow @method=GET", "@action=deny @method=GET", DENY_GET, 200, 200, ], + [ 23, "ip_and_method", "@action=allow @method=GET", "@action=deny @method=GET", DENY_GET_AND_POST, 200, 403, ], + [ 24, "ip_and_method", "@action=allow @method=GET", "@action=allow @method=POST", ALLOW_GET_AND_POST, 200, 200, ], + [ 25, "ip_and_method", "@action=allow @method=GET", "@action=allow @method=POST", ALLOW_GET, 200, 200, ], + [ 26, "ip_and_method", "@action=allow @method=GET", "@action=allow @method=POST", DENY_GET, 200, 200, ], + [ 27, "ip_and_method", "@action=allow @method=GET", "@action=allow @method=POST", DENY_GET_AND_POST, 200, 200, ], + [ 28, "ip_and_method", "@action=allow @method=GET", "@action=deny @method=POST", ALLOW_GET_AND_POST, 200, 403, ], + [ 29, "ip_and_method", "@action=allow @method=GET", "@action=deny @method=POST", ALLOW_GET, 200, 403, ], + [ 30, "ip_and_method", "@action=allow @method=GET", "@action=deny @method=POST", DENY_GET, 200, 403, ], + [ 31, "ip_and_method", "@action=allow @method=GET", "@action=deny @method=POST", DENY_GET_AND_POST, 200, 403, ], + [ 32, "ip_and_method", "@action=deny @method=GET", "", ALLOW_GET_AND_POST, 403, 200, ], + [ 33, "ip_and_method", "@action=deny @method=GET", "", ALLOW_GET, 403, 403, ], + [ 34, "ip_and_method", "@action=deny @method=GET", "", DENY_GET, 403, 200, ], + [ 35, "ip_and_method", "@action=deny @method=GET", "", DENY_GET_AND_POST, 403, 403, ], + [ 36, "ip_and_method", "@action=deny @method=GET", "@action=allow @method=GET", ALLOW_GET_AND_POST, 403, 200, ], + [ 37, "ip_and_method", "@action=deny @method=GET", "@action=allow @method=GET", ALLOW_GET, 403, 403, ], + [ 38, "ip_and_method", "@action=deny @method=GET", "@action=allow @method=GET", DENY_GET, 403, 200, ], + [ 39, "ip_and_method", "@action=deny @method=GET", "@action=allow @method=GET", DENY_GET_AND_POST, 403, 403, ], + [ 40, "ip_and_method", "@action=deny @method=GET", "@action=deny @method=GET", ALLOW_GET_AND_POST, 403, 200, ], + [ 41, "ip_and_method", "@action=deny @method=GET", "@action=deny @method=GET", ALLOW_GET, 403, 403, ], + [ 42, "ip_and_method", "@action=deny @method=GET", "@action=deny @method=GET", DENY_GET, 403, 200, ], + [ 43, "ip_and_method", "@action=deny @method=GET", "@action=deny @method=GET", DENY_GET_AND_POST, 403, 403, ], + [ 44, "ip_and_method", "@action=deny @method=GET", "@action=allow @method=POST", ALLOW_GET_AND_POST, 403, 200, ], + [ 45, "ip_and_method", "@action=deny @method=GET", "@action=allow @method=POST", ALLOW_GET, 403, 200, ], + [ 46, "ip_and_method", "@action=deny @method=GET", "@action=allow @method=POST", DENY_GET, 403, 200, ], + [ 47, "ip_and_method", "@action=deny @method=GET", "@action=allow @method=POST", DENY_GET_AND_POST, 403, 200, ], + [ 48, "ip_and_method", "@action=deny @method=GET", "@action=deny @method=POST", ALLOW_GET_AND_POST, 403, 403, ], + [ 49, "ip_and_method", "@action=deny @method=GET", "@action=deny @method=POST", ALLOW_GET, 403, 403, ], + [ 50, "ip_and_method", "@action=deny @method=GET", "@action=deny @method=POST", DENY_GET, 403, 403, ], + [ 51, "ip_and_method", "@action=deny @method=GET", "@action=deny @method=POST", DENY_GET_AND_POST, 403, 403, ], + [ 52, "ip_only", "", "", ALLOW_GET_AND_POST, 200, 200, ], + [ 53, "ip_only", "", "", ALLOW_GET, 200, 403, ], + [ 54, "ip_only", "", "", DENY_GET, 403, 200, ], + [ 55, "ip_only", "", "", DENY_GET_AND_POST, 403, 403, ], + [ 56, "ip_only", "", "@action=allow @method=GET", ALLOW_GET_AND_POST, 200, 403, ], + [ 57, "ip_only", "", "@action=allow @method=GET", ALLOW_GET, 200, 403, ], + [ 58, "ip_only", "", "@action=allow @method=GET", DENY_GET, 200, 403, ], + [ 59, "ip_only", "", "@action=allow @method=GET", DENY_GET_AND_POST, 200, 403, ], + [ 60, "ip_only", "", "@action=deny @method=GET", ALLOW_GET_AND_POST, 403, 200, ], + [ 61, "ip_only", "", "@action=deny @method=GET", ALLOW_GET, 403, 200, ], + [ 62, "ip_only", "", "@action=deny @method=GET", DENY_GET, 403, 200, ], + [ 63, "ip_only", "", "@action=deny @method=GET", DENY_GET_AND_POST, 403, 200, ], + [ 64, "ip_only", "@action=allow @method=GET", "", ALLOW_GET_AND_POST, 200, 403, ], + [ 65, "ip_only", "@action=allow @method=GET", "", ALLOW_GET, 200, 403, ], + [ 66, "ip_only", "@action=allow @method=GET", "", DENY_GET, 200, 403, ], + [ 67, "ip_only", "@action=allow @method=GET", "", DENY_GET_AND_POST, 200, 403, ], + [ 68, "ip_only", "@action=allow @method=GET", "@action=allow @method=GET", ALLOW_GET_AND_POST, 200, 403, ], + [ 69, "ip_only", "@action=allow @method=GET", "@action=allow @method=GET", ALLOW_GET, 200, 403, ], + [ 70, "ip_only", "@action=allow @method=GET", "@action=allow @method=GET", DENY_GET, 200, 403, ], + [ 71, "ip_only", "@action=allow @method=GET", "@action=allow @method=GET", DENY_GET_AND_POST, 200, 403, ], + [ 72, "ip_only", "@action=allow @method=GET", "@action=deny @method=GET", ALLOW_GET_AND_POST, 200, 403, ], + [ 73, "ip_only", "@action=allow @method=GET", "@action=deny @method=GET", ALLOW_GET, 200, 403, ], + [ 74, "ip_only", "@action=allow @method=GET", "@action=deny @method=GET", DENY_GET, 200, 403, ], + [ 75, "ip_only", "@action=allow @method=GET", "@action=deny @method=GET", DENY_GET_AND_POST, 200, 403, ], + [ 76, "ip_only", "@action=deny @method=GET", "", ALLOW_GET_AND_POST, 403, 200, ], + [ 77, "ip_only", "@action=deny @method=GET", "", ALLOW_GET, 403, 200, ], + [ 78, "ip_only", "@action=deny @method=GET", "", DENY_GET, 403, 200, ], + [ 79, "ip_only", "@action=deny @method=GET", "", DENY_GET_AND_POST, 403, 200, ], + [ 80, "ip_only", "@action=deny @method=GET", "@action=allow @method=GET", ALLOW_GET_AND_POST, 403, 200, ], + [ 81, "ip_only", "@action=deny @method=GET", "@action=allow @method=GET", ALLOW_GET, 403, 200, ], + [ 82, "ip_only", "@action=deny @method=GET", "@action=allow @method=GET", DENY_GET, 403, 200, ], + [ 83, "ip_only", "@action=deny @method=GET", "@action=allow @method=GET", DENY_GET_AND_POST, 403, 200, ], + [ 84, "ip_only", "@action=deny @method=GET", "@action=deny @method=GET", ALLOW_GET_AND_POST, 403, 200, ], + [ 85, "ip_only", "@action=deny @method=GET", "@action=deny @method=GET", ALLOW_GET, 403, 200, ], + [ 86, "ip_only", "@action=deny @method=GET", "@action=deny @method=GET", DENY_GET, 403, 200, ], + [ 87, "ip_only", "@action=deny @method=GET", "@action=deny @method=GET", DENY_GET_AND_POST, 403, 200, ], +] +# yapf: enable + +all_acl_combination_tests = [dict(zip(keys, test)) for test in all_acl_combinations] diff --git a/tests/gold_tests/remap/deny_head_post.replay.yaml b/tests/gold_tests/remap/base.replay.yaml similarity index 59% copy from tests/gold_tests/remap/deny_head_post.replay.yaml copy to tests/gold_tests/remap/base.replay.yaml index 0d4e91acd8..b236481ffe 100644 --- a/tests/gold_tests/remap/deny_head_post.replay.yaml +++ b/tests/gold_tests/remap/base.replay.yaml @@ -18,28 +18,17 @@ # methods. meta: - version: "1.0" - - blocks: - - standard_response: &standard_response - server-response: - status: 200 - reason: OK - headers: - fields: - - [ Content-Length, 20 ] + version: '1.0' sessions: - protocol: - name: http version: 1 - - name: tls - sni: test_sni transactions: - client-request: - method: "GET" - version: "1.1" + method: GET + version: '1.1' url: /test/ip_allow/test_get headers: fields: @@ -47,54 +36,32 @@ sessions: - [ uuid, get ] - [ X-Request, get ] - <<: *standard_response - proxy-response: status: 200 - - client-request: - method: "HEAD" - version: "1.1" - url: /test/ip_allow/test_head + server-response: + status: 200 + reason: OK headers: fields: - - [ Content-Length, 0 ] - - [ uuid, head ] - - [ X-Request, head ] + - [ Content-Length, 20 ] - <<: *standard_response - - proxy-response: - status: 403 - - # POST rejected - client-request: - method: "POST" - version: "1.1" + method: POST + version: '1.1' url: /test/ip_allow/test_post headers: fields: - - [Content-Length, 10] + - [ Content-Length, 10 ] - [ uuid, post ] - [ X-Request, post ] - <<: *standard_response - proxy-response: - status: 403 + status: 200 - - client-request: - method: "DELETE" - version: "1.1" - url: /test/ip_allow/test_delete + server-response: + status: 200 + reason: OK headers: fields: - - [ Host, example.com ] - - [ uuid, delete ] - - [ X-Request, delete ] - - [ Content-Length, 0 ] - - <<: *standard_response - - proxy-response: - status: 200 + - [ Content-Length, 20 ] diff --git a/tests/gold_tests/remap/deactivate_ip_allow.py b/tests/gold_tests/remap/deactivate_ip_allow.py new file mode 100644 index 0000000000..45093a2332 --- /dev/null +++ b/tests/gold_tests/remap/deactivate_ip_allow.py @@ -0,0 +1,96 @@ +''' +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. + +ALLOW_GET_AND_POST = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: allow + methods: [GET, POST] +''' + +ALLOW_GET = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: allow + methods: [GET] +''' + +DENY_GET = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: deny + methods: [GET] +''' + +DENY_GET_AND_POST = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: deny + methods: [GET, POST] +''' + +# Optimized ACL filter on accept +DENY_ALL = f''' +ip_allow: + - apply: in + ip_addrs: [0/0, ::/0] + action: deny + methods: ALL +''' + +# yapf: disable +keys = ["index", "policy", "inline", "named_acl", "deactivate_ip_allow", "ip_allow", "GET response", "POST response"] +deactivate_ip_allow_combinations = [ + [ 0, "ip_and_method", "", "", False, ALLOW_GET_AND_POST, 200, 200, ], + [ 1, "ip_and_method", "", "", False, ALLOW_GET, 200, 403, ], + [ 2, "ip_and_method", "", "", False, DENY_GET, 403, 200, ], + [ 3, "ip_and_method", "", "", False, DENY_GET_AND_POST, 403, 403, ], + [ 4, "ip_and_method", "", "", False, DENY_ALL, None, None, ], + [ 5, "ip_and_method", "", "", True, ALLOW_GET_AND_POST, 200, 200, ], + [ 6, "ip_and_method", "", "", True, ALLOW_GET, 200, 200, ], + [ 7, "ip_and_method", "", "", True, DENY_GET, 200, 200, ], + [ 8, "ip_and_method", "", "", True, DENY_GET_AND_POST, 200, 200, ], + [ 9, "ip_and_method", "", "", True, DENY_ALL, 200, 200, ], + [ 10, "ip_and_method", "@action=allow @method=GET", "", False, ALLOW_GET_AND_POST, 200, 200, ], + [ 11, "ip_and_method", "@action=allow @method=GET", "", False, ALLOW_GET, 200, 403, ], + [ 12, "ip_and_method", "@action=allow @method=GET", "", False, DENY_GET, 200, 200, ], + [ 13, "ip_and_method", "@action=allow @method=GET", "", False, DENY_GET_AND_POST, 200, 403, ], + [ 14, "ip_and_method", "@action=allow @method=GET", "", False, DENY_ALL, None, None, ], + [ 15, "ip_and_method", "@action=allow @method=GET", "", True, ALLOW_GET_AND_POST, 200, 200, ], + [ 16, "ip_and_method", "@action=allow @method=GET", "", True, ALLOW_GET, 200, 200, ], + [ 17, "ip_and_method", "@action=allow @method=GET", "", True, DENY_GET, 200, 200, ], + [ 18, "ip_and_method", "@action=allow @method=GET", "", True, DENY_GET_AND_POST, 200, 200, ], + [ 19, "ip_and_method", "@action=allow @method=GET", "", True, DENY_ALL, 200, 200, ], + [ 20, "ip_and_method", "@action=deny @method=GET", "", False, ALLOW_GET_AND_POST, 403, 200, ], + [ 21, "ip_and_method", "@action=deny @method=GET", "", False, ALLOW_GET, 403, 403, ], + [ 22, "ip_and_method", "@action=deny @method=GET", "", False, DENY_GET, 403, 200, ], + [ 23, "ip_and_method", "@action=deny @method=GET", "", False, DENY_GET_AND_POST, 403, 403, ], + [ 24, "ip_and_method", "@action=deny @method=GET", "", False, DENY_ALL, None, None, ], + [ 25, "ip_and_method", "@action=deny @method=GET", "", True, ALLOW_GET_AND_POST, 403, 200, ], + [ 26, "ip_and_method", "@action=deny @method=GET", "", True, ALLOW_GET, 403, 200, ], + [ 27, "ip_and_method", "@action=deny @method=GET", "", True, DENY_GET, 403, 200, ], + [ 28, "ip_and_method", "@action=deny @method=GET", "", True, DENY_GET_AND_POST, 403, 200, ], + [ 29, "ip_and_method", "@action=deny @method=GET", "", True, DENY_ALL, 403, 200, ], +] +all_deactivate_ip_allow_tests = [dict(zip(keys, test)) for test in deactivate_ip_allow_combinations] +# yapf: enable diff --git a/tests/gold_tests/remap/deny_head_post.replay.yaml b/tests/gold_tests/remap/deny_head_post.replay.yaml index 0d4e91acd8..f814b53477 100644 --- a/tests/gold_tests/remap/deny_head_post.replay.yaml +++ b/tests/gold_tests/remap/deny_head_post.replay.yaml @@ -33,8 +33,6 @@ sessions: - protocol: - name: http version: 1 - - name: tls - sni: test_sni transactions: - client-request: diff --git a/tests/gold_tests/remap/remap_acl.test.py b/tests/gold_tests/remap/remap_acl.test.py index 5c46f2f1ad..cd718e2b2f 100644 --- a/tests/gold_tests/remap/remap_acl.test.py +++ b/tests/gold_tests/remap/remap_acl.test.py @@ -18,7 +18,13 @@ Verify remap.config acl behavior. # limitations under the License. import os +import io import re +import pathlib +import inspect +import tempfile +from yaml import load, dump +from yaml import CLoader as Loader from typing import List, Tuple Test.Summary = ''' @@ -34,8 +40,8 @@ class Test_remap_acl: _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]): + self, name: str, replay_file: str, ip_allow_content: str, deactivate_ip_allow: bool, acl_matching_policy: int, + acl_configuration: str, named_acls: List[Tuple[str, str]], expected_responses: List[int]): """Initialize the test. :param name: The name of the test. @@ -49,6 +55,7 @@ class Test_remap_acl: self._replay_file = replay_file self._ip_allow_content = ip_allow_content self._deactivate_ip_allow = deactivate_ip_allow + self._acl_matching_policy = acl_matching_policy self._acl_configuration = acl_configuration self._named_acls = named_acls self._expected_responses = expected_responses @@ -79,18 +86,13 @@ class Test_remap_acl: 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|ip_allow', '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, + 'proxy.config.url_remap.acl_matching_policy': self._acl_matching_policy, }) remap_config_lines = [] @@ -115,14 +117,23 @@ class Test_remap_acl: """ name = f"client-{Test_remap_acl._client_counter}" - p = tr.AddVerifierClientProcess(name, self._replay_file, https_ports=[self._ts.Variables.ssl_port]) + p = tr.AddVerifierClientProcess(name, self._replay_file, http_ports=[self._ts.Variables.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) + if self._expected_responses == [None, None]: + # If there are no expected responses, expect the Warning about the rejected ip. + self._ts.Disk.diags_log.Content += Testers.ContainsExpression( + "client '127.0.0.1' prohibited by ip-allow policy", "Verify the client rejection warning message.") + + # Also, the client will complain about the broken connections. + p.ReturnCode = 1 + + else: + 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''' @@ -145,6 +156,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip=127.0.0.1 @method=GET @method=POST', named_acls=[], expected_responses=[200, 200, 403, 403, 403]) @@ -154,6 +166,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip=1.2.3.4 @method=GET @method=POST', named_acls=[], expected_responses=[200, 403, 403, 403, 403]) @@ -163,6 +176,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip=all @method=GET @method=POST', named_acls=[], expected_responses=[200, 200, 403, 403, 403]) @@ -172,6 +186,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip_category=ACME_LOCAL @method=GET @method=POST', named_acls=[], expected_responses=[200, 200, 403, 403, 403]) @@ -181,6 +196,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @method=GET @method=POST', named_acls=[], expected_responses=[200, 200, 403, 403, 403]) @@ -190,6 +206,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_denied.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=deny @src_ip=127.0.0.1 @method=GET @method=POST', named_acls=[], expected_responses=[403, 403, 200, 200, 400]) @@ -199,6 +216,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_all_denied.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip=1.2.3.4 @method=GET @method=POST', named_acls=[('deny', '@action=deny')], expected_responses=[403, 403, 403, 403, 403]) @@ -208,6 +226,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_all_denied.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip=~127.0.0.1 @method=GET @method=POST', named_acls=[('deny', '@action=deny')], expected_responses=[403, 403, 403, 403, 403]) @@ -217,6 +236,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip=~3.4.5.6 @method=GET @method=POST', named_acls=[('deny', '@action=deny')], expected_responses=[200, 200, 403, 403, 403]) @@ -226,6 +246,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_all_denied.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip_category=~ACME_LOCAL @method=GET @method=POST', named_acls=[('deny', '@action=deny')], expected_responses=[403, 403, 403, 403, 403]) @@ -235,6 +256,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip_category=~ACME_EXTERNAL @method=GET @method=POST', named_acls=[('deny', '@action=deny')], expected_responses=[200, 200, 403, 403, 403]) @@ -244,6 +266,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_all_denied.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, # The rule will not match because, while @src_ip matches, @src_ip_category does not. acl_configuration='@action=allow @src_ip=127.0.0.1 @src_ip_category=ACME_EXTERNAL @method=GET @method=POST', # Therefore, this named deny filter will block. @@ -255,6 +278,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip=127.0.0.1 @method=GET @method=POST', named_acls=[('deny', '@action=deny')], expected_responses=[200, 200, 403, 403, 403]) @@ -264,6 +288,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @src_ip=127.0.0.1 @method=GET @method=POST', named_acls=[], expected_responses=[200, 200, 403, 403, 403]) @@ -273,6 +298,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_all_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=True, + acl_matching_policy=1, # This won't match, so nothing will match since ip_allow.yaml is off. acl_configuration='@action=allow @src_ip=1.2.3.4 @method=GET @method=POST', named_acls=[], @@ -284,6 +310,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_post_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @in_ip=127.0.0.1 @method=GET @method=POST', named_acls=[], expected_responses=[200, 200, 403, 403, 403]) @@ -293,6 +320,7 @@ test_ip_allow_optional_methods = Test_remap_acl( replay_file='remap_acl_get_allowed.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='@action=allow @in_ip=3.4.5.6 @method=GET @method=POST', named_acls=[], expected_responses=[200, 403, 403, 403, 403]) @@ -302,6 +330,82 @@ test_named_acl_deny = Test_remap_acl( replay_file='deny_head_post.replay.yaml', ip_allow_content=IP_ALLOW_CONTENT, deactivate_ip_allow=False, + acl_matching_policy=1, acl_configuration='', named_acls=[('deny', '@action=deny @method=HEAD @method=POST')], expected_responses=[200, 403, 403, 403]) + + +def replay_proxy_response(filename, replay_file, get_proxy_response, post_proxy_response): + """ + replay_proxy_response writes the given replay file (which expects a single GET & POST client-request) + with the given proxy_response value. This is only used to support the tests in the combination table. + """ + + current_dir = os.path.dirname(inspect.getfile(inspect.currentframe())) + path = os.path.join(current_dir, filename) + data = None + with open(path) as f: + data = load(f, Loader=Loader) + for session in data["sessions"]: + for transaction in session["transactions"]: + method = transaction["client-request"]["method"] + if method == "GET": + transaction["proxy-response"]["status"] = 403 if get_proxy_response == None else get_proxy_response + elif method == "POST": + transaction["proxy-response"]["status"] = 403 if post_proxy_response == None else post_proxy_response + else: + raise Exception("Expected to find GET or POST request, found %s", method) + with open(replay_file, "w") as f: + f.write(dump(data)) + + +from deactivate_ip_allow import all_deactivate_ip_allow_tests +from all_acl_combinations import all_acl_combination_tests +""" +Test all acl combinations +""" +for idx, test in enumerate(all_acl_combination_tests): + (_, replay_file_name) = tempfile.mkstemp(suffix="acl_table_test_{}.replay".format(idx)) + replay_proxy_response( + "base.replay.yaml", + replay_file_name, + test["GET response"], + test["POST response"], + ) + Test.Summary = "table test {0}".format(idx) + Test_remap_acl( + "{0} {1} {2}".format(test["inline"], test["named_acl"], test["ip_allow"]), + replay_file=replay_file_name, + ip_allow_content=test["ip_allow"], + deactivate_ip_allow=False, + acl_matching_policy=0 if test["policy"] == "ip_and_method" else 1, + acl_configuration=test["inline"], + named_acls=[("acl", test["named_acl"])] if test["named_acl"] != "" else [], + expected_responses=[test["GET response"], test["POST response"]], + ) +""" +Test all ACL combinations +""" +for idx, test in enumerate(all_deactivate_ip_allow_tests): + try: + test["deactivate_ip_allow"] + except: + print(test) + (_, replay_file_name) = tempfile.mkstemp(suffix="deactivate_ip_allow_table_test_{}.replay".format(idx)) + replay_proxy_response( + "base.replay.yaml", + replay_file_name, + test["GET response"], + test["POST response"], + ) + Test.Summary = "table test {0}".format(idx) + Test_remap_acl( + "{0} {1} {2}".format(test["inline"], test["named_acl"], test["ip_allow"]), + replay_file=replay_file_name, + ip_allow_content=test["ip_allow"], + deactivate_ip_allow=test["deactivate_ip_allow"], + acl_matching_policy=0 if test["policy"] == "ip_and_method" else 1, + acl_configuration=test["inline"], + named_acls=[("acl", test["named_acl"])] if test["named_acl"] != "" else [], + expected_responses=[test["GET response"], test["POST response"]]) diff --git a/tests/gold_tests/remap/remap_acl_all_allowed.replay.yaml b/tests/gold_tests/remap/remap_acl_all_allowed.replay.yaml index 52d9517d91..06501c9ed6 100644 --- a/tests/gold_tests/remap/remap_acl_all_allowed.replay.yaml +++ b/tests/gold_tests/remap/remap_acl_all_allowed.replay.yaml @@ -32,8 +32,6 @@ sessions: - protocol: - name: http version: 1 - - name: tls - sni: test_sni transactions: - client-request: diff --git a/tests/gold_tests/remap/remap_acl_all_denied.replay.yaml b/tests/gold_tests/remap/remap_acl_all_denied.replay.yaml index d15782b046..3e727f5bcb 100644 --- a/tests/gold_tests/remap/remap_acl_all_denied.replay.yaml +++ b/tests/gold_tests/remap/remap_acl_all_denied.replay.yaml @@ -32,8 +32,6 @@ sessions: - protocol: - name: http version: 1 - - name: tls - sni: test_sni transactions: - client-request: diff --git a/tests/gold_tests/remap/remap_acl_get_allowed.replay.yaml b/tests/gold_tests/remap/remap_acl_get_allowed.replay.yaml index 3e20822fda..648fdc137a 100644 --- a/tests/gold_tests/remap/remap_acl_get_allowed.replay.yaml +++ b/tests/gold_tests/remap/remap_acl_get_allowed.replay.yaml @@ -33,8 +33,6 @@ sessions: - protocol: - name: http version: 1 - - name: tls - sni: test_sni transactions: - client-request: 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 index cf08a22a8a..26e6297c4c 100644 --- a/tests/gold_tests/remap/remap_acl_get_post_allowed.replay.yaml +++ b/tests/gold_tests/remap/remap_acl_get_post_allowed.replay.yaml @@ -33,8 +33,6 @@ sessions: - protocol: - name: http version: 1 - - name: tls - sni: test_sni transactions: - client-request: 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 index 2a82445648..9093a8bb36 100644 --- a/tests/gold_tests/remap/remap_acl_get_post_denied.replay.yaml +++ b/tests/gold_tests/remap/remap_acl_get_post_denied.replay.yaml @@ -33,8 +33,6 @@ sessions: - protocol: - name: http version: 1 - - name: tls - sni: test_sni transactions: - client-request:
