github.com/nginxinc/kubernetes-ingress@v1.12.5/tests/suite/test_app_protect_waf_policies.py (about)

     1  import requests, logging
     2  import pytest, json
     3  
     4  from settings import TEST_DATA, DEPLOYMENTS
     5  from suite.custom_resources_utils import (
     6      create_ap_logconf_from_yaml,
     7      create_ap_policy_from_yaml,
     8      delete_ap_policy,
     9      delete_ap_logconf,
    10      create_ap_waf_policy_from_yaml,
    11  )
    12  from suite.resources_utils import (
    13      wait_before_test,
    14      create_items_from_yaml,
    15      wait_before_test,
    16      get_file_contents,
    17      get_service_endpoint,
    18  )
    19  from suite.custom_resources_utils import (
    20      read_ap_custom_resource,
    21      create_crd_from_yaml,
    22      delete_crd,
    23      create_ap_usersig_from_yaml,
    24      delete_ap_usersig,
    25      delete_and_create_ap_policy_from_yaml,
    26      delete_virtual_server,
    27      create_virtual_server_from_yaml,
    28      patch_virtual_server_from_yaml,
    29      patch_v_s_route_from_yaml,
    30      create_v_s_route_from_yaml,
    31      delete_v_s_route,
    32      create_policy_from_yaml,
    33      delete_policy,
    34      read_policy,
    35  )
    36  from suite.yaml_utils import get_first_ingress_host_from_yaml, get_name_from_yaml
    37  
    38  ap_pol_name = ""
    39  log_name = ""
    40  std_vs_src = f"{TEST_DATA}/ap-waf/standard/virtual-server.yaml"
    41  waf_spec_vs_src = f"{TEST_DATA}/ap-waf/virtual-server-waf-spec.yaml"
    42  waf_route_vs_src = f"{TEST_DATA}/ap-waf/virtual-server-waf-route.yaml"
    43  waf_subroute_vsr_src = f"{TEST_DATA}/ap-waf/virtual-server-route-waf-subroute.yaml"
    44  waf_pol_default_src = f"{TEST_DATA}/ap-waf/policies/waf-default.yaml"
    45  waf_pol_dataguard_src = f"{TEST_DATA}/ap-waf/policies/waf-dataguard.yaml"
    46  ap_policy_uds = "dataguard-alarm-uds"
    47  uds_crd_resource = f"{TEST_DATA}/ap-waf/ap-ic-uds.yaml"
    48  valid_resp_addr = "Server address:"
    49  valid_resp_name = "Server name:"
    50  invalid_resp_title = "Request Rejected"
    51  invalid_resp_body = "The requested URL was rejected. Please consult with your administrator."
    52  
    53  
    54  @pytest.fixture(scope="class")
    55  def appprotect_setup(request, kube_apis, test_namespace) -> None:
    56      """
    57      Deploy simple application and all the AppProtect(dataguard-alarm) resources under test in one namespace.
    58  
    59      :param request: pytest fixture
    60      :param kube_apis: client apis
    61      :param ingress_controller_endpoint: public endpoint
    62      :param test_namespace:
    63      """
    64  
    65      print("------------------------- Deploy logconf -----------------------------")
    66      src_log_yaml = f"{TEST_DATA}/ap-waf/logconf.yaml"
    67      global log_name
    68      log_name = create_ap_logconf_from_yaml(kube_apis.custom_objects, src_log_yaml, test_namespace)
    69  
    70      print("------------------------- Create UserSig CRD resource-----------------------------")
    71      usersig_name = create_ap_usersig_from_yaml(
    72          kube_apis.custom_objects, uds_crd_resource, test_namespace
    73      )
    74  
    75      print(f"------------------------- Deploy dataguard-alarm appolicy ---------------------------")
    76      src_pol_yaml = f"{TEST_DATA}/ap-waf/{ap_policy_uds}.yaml"
    77      global ap_pol_name
    78      ap_pol_name = create_ap_policy_from_yaml(kube_apis.custom_objects, src_pol_yaml, test_namespace)
    79  
    80      def fin():
    81          print("Clean up:")
    82          delete_ap_policy(kube_apis.custom_objects, ap_pol_name, test_namespace)
    83          delete_ap_usersig(kube_apis.custom_objects, usersig_name, test_namespace)
    84          delete_ap_logconf(kube_apis.custom_objects, log_name, test_namespace)
    85  
    86      request.addfinalizer(fin)
    87  
    88  
    89  def assert_ap_crd_info(ap_crd_info, policy_name) -> None:
    90      """
    91      Assert fields in AppProtect policy documents
    92      :param ap_crd_info: CRD output from k8s API
    93      :param policy_name:
    94      """
    95      assert ap_crd_info["kind"] == "APPolicy"
    96      assert ap_crd_info["metadata"]["name"] == policy_name
    97      assert ap_crd_info["spec"]["policy"]["enforcementMode"] == "blocking"
    98      assert (
    99          ap_crd_info["spec"]["policy"]["blocking-settings"]["violations"][0]["name"]
   100          == "VIOL_DATA_GUARD"
   101      )
   102  
   103  
   104  def assert_invalid_responses(response) -> None:
   105      """
   106      Assert responses when policy config is blocking requests
   107      :param response: Response
   108      """
   109      assert invalid_resp_title in response.text
   110      assert invalid_resp_body in response.text
   111      assert response.status_code == 200
   112  
   113  
   114  def assert_valid_responses(response) -> None:
   115      """
   116      Assert responses when policy config is allowing requests
   117      :param response: Response
   118      """
   119      assert valid_resp_name in response.text
   120      assert valid_resp_addr in response.text
   121      assert response.status_code == 200
   122  
   123  
   124  @pytest.mark.skip_for_nginx_oss
   125  @pytest.mark.appprotect
   126  @pytest.mark.parametrize(
   127      "crd_ingress_controller_with_ap, virtual_server_setup",
   128      [
   129          (
   130              {
   131                  "type": "complete",
   132                  "extra_args": [
   133                      f"-enable-custom-resources",
   134                      f"-enable-leader-election=false",
   135                      f"-enable-app-protect",
   136                      f"-enable-preview-policies",
   137                  ],
   138              },
   139              {"example": "ap-waf", "app_type": "simple",},
   140          )
   141      ],
   142      indirect=True,
   143  )
   144  class TestAppProtectWAFPolicyVS:
   145      def restore_default_vs(self, kube_apis, virtual_server_setup) -> None:
   146          """
   147          Restore VirtualServer without policy spec
   148          """
   149          delete_virtual_server(
   150              kube_apis.custom_objects, virtual_server_setup.vs_name, virtual_server_setup.namespace
   151          )
   152          create_virtual_server_from_yaml(
   153              kube_apis.custom_objects, std_vs_src, virtual_server_setup.namespace
   154          )
   155          wait_before_test()
   156  
   157      @pytest.mark.smoke
   158      @pytest.mark.parametrize(
   159          "vs_src, waf",
   160          [
   161              (waf_spec_vs_src, waf_pol_default_src),
   162              (waf_spec_vs_src, waf_pol_dataguard_src),
   163              (waf_route_vs_src, waf_pol_default_src),
   164              (waf_route_vs_src, waf_pol_dataguard_src),
   165          ],
   166      )
   167      def test_ap_waf_policy_block(
   168          self,
   169          kube_apis,
   170          crd_ingress_controller_with_ap,
   171          virtual_server_setup,
   172          appprotect_setup,
   173          test_namespace,
   174          vs_src,
   175          waf,
   176      ):
   177          """
   178          Test waf policy when enabled with default and dataguard-alarm AP Policies
   179          """
   180          print(f"Create waf policy")
   181          if waf == waf_pol_dataguard_src:
   182              create_ap_waf_policy_from_yaml(
   183                  kube_apis.custom_objects,
   184                  waf,
   185                  test_namespace,
   186                  test_namespace,
   187                  True,
   188                  False,
   189                  ap_pol_name,
   190                  log_name,
   191                  "syslog:server=127.0.0.1:514",
   192              )
   193          elif waf == waf_pol_default_src:
   194              pol_name = create_policy_from_yaml(kube_apis.custom_objects, waf, test_namespace)
   195          else:
   196              pytest.fail(f"Invalid argument")
   197  
   198          wait_before_test()
   199          print(f"Patch vs with policy: {vs_src}")
   200          patch_virtual_server_from_yaml(
   201              kube_apis.custom_objects,
   202              virtual_server_setup.vs_name,
   203              vs_src,
   204              virtual_server_setup.namespace,
   205          )
   206          wait_before_test()
   207          ap_crd_info = read_ap_custom_resource(
   208              kube_apis.custom_objects, test_namespace, "appolicies", ap_policy_uds
   209          )
   210          assert_ap_crd_info(ap_crd_info, ap_policy_uds)
   211          wait_before_test(120)
   212  
   213          print(
   214              "----------------------- Send request with embedded malicious script----------------------"
   215          )
   216          response1 = requests.get(
   217              virtual_server_setup.backend_1_url + "</script>",
   218              headers={"host": virtual_server_setup.vs_host},
   219          )
   220          print(response1.text)
   221  
   222          print(
   223              "----------------------- Send request with blocked keyword in UDS----------------------"
   224          )
   225          response2 = requests.get(
   226              virtual_server_setup.backend_1_url,
   227              headers={"host": virtual_server_setup.vs_host},
   228              data="kic",
   229          )
   230          print(response2.text)
   231  
   232          delete_policy(kube_apis.custom_objects, "waf-policy", test_namespace)
   233          self.restore_default_vs(kube_apis, virtual_server_setup)
   234          assert_invalid_responses(response1)
   235          if waf == waf_pol_dataguard_src:
   236              assert_invalid_responses(response2)
   237          elif waf == waf_pol_default_src:
   238              assert_valid_responses(response2)
   239          else:
   240              pytest.fail(f"Invalid arguments")
   241  
   242      @pytest.mark.parametrize(
   243          "vs_src, waf",
   244          [(waf_spec_vs_src, waf_pol_dataguard_src), (waf_route_vs_src, waf_pol_dataguard_src),],
   245      )
   246      def test_ap_waf_policy_allow(
   247          self,
   248          kube_apis,
   249          crd_ingress_controller_with_ap,
   250          virtual_server_setup,
   251          appprotect_setup,
   252          test_namespace,
   253          vs_src,
   254          waf,
   255      ):
   256          """
   257          Test waf policy when disabled
   258          """
   259          print(f"Create waf policy")
   260          create_ap_waf_policy_from_yaml(
   261              kube_apis.custom_objects,
   262              waf,
   263              test_namespace,
   264              test_namespace,
   265              False,
   266              False,
   267              ap_pol_name,
   268              log_name,
   269              "syslog:server=127.0.0.1:514",
   270          )
   271          wait_before_test()
   272          print(f"Patch vs with policy: {vs_src}")
   273          patch_virtual_server_from_yaml(
   274              kube_apis.custom_objects,
   275              virtual_server_setup.vs_name,
   276              vs_src,
   277              virtual_server_setup.namespace,
   278          )
   279          wait_before_test()
   280          ap_crd_info = read_ap_custom_resource(
   281              kube_apis.custom_objects, test_namespace, "appolicies", ap_policy_uds
   282          )
   283          assert_ap_crd_info(ap_crd_info, ap_policy_uds)
   284          wait_before_test(120)
   285  
   286          print(
   287              "----------------------- Send request with embedded malicious script----------------------"
   288          )
   289          response1 = requests.get(
   290              virtual_server_setup.backend_1_url + "</script>",
   291              headers={"host": virtual_server_setup.vs_host},
   292          )
   293          print(response1.text)
   294  
   295          print(
   296              "----------------------- Send request with blocked keyword in UDS----------------------"
   297          )
   298          response2 = requests.get(
   299              virtual_server_setup.backend_1_url,
   300              headers={"host": virtual_server_setup.vs_host},
   301              data="kic",
   302          )
   303          print(response2.text)
   304  
   305          delete_policy(kube_apis.custom_objects, "waf-policy", test_namespace)
   306          self.restore_default_vs(kube_apis, virtual_server_setup)
   307          assert_valid_responses(response1)
   308          assert_valid_responses(response2)
   309  
   310      def test_ap_waf_policy_logs(
   311          self,
   312          kube_apis,
   313          crd_ingress_controller_with_ap,
   314          virtual_server_setup,
   315          appprotect_setup,
   316          test_namespace,
   317      ):
   318          """
   319          Test waf policy logs
   320          """
   321          src_syslog_yaml = f"{TEST_DATA}/ap-waf/syslog.yaml"
   322          log_loc = f"/var/log/messages"
   323          create_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace)
   324          syslog_ep = get_service_endpoint(kube_apis, "syslog-svc", test_namespace)
   325          syslog_pod = kube_apis.v1.list_namespaced_pod(test_namespace).items[-1].metadata.name
   326          print(f"Create waf policy")
   327          create_ap_waf_policy_from_yaml(
   328              kube_apis.custom_objects,
   329              waf_pol_dataguard_src,
   330              test_namespace,
   331              test_namespace,
   332              True,
   333              True,
   334              ap_pol_name,
   335              log_name,
   336              f"syslog:server={syslog_ep}:514",
   337          )
   338          wait_before_test()
   339          print(f"Patch vs with policy: {waf_spec_vs_src}")
   340          patch_virtual_server_from_yaml(
   341              kube_apis.custom_objects,
   342              virtual_server_setup.vs_name,
   343              waf_spec_vs_src,
   344              virtual_server_setup.namespace,
   345          )
   346          wait_before_test()
   347          ap_crd_info = read_ap_custom_resource(
   348              kube_apis.custom_objects, test_namespace, "appolicies", ap_policy_uds
   349          )
   350          assert_ap_crd_info(ap_crd_info, ap_policy_uds)
   351          wait_before_test(120)
   352  
   353          print(
   354              "----------------------- Send request with embedded malicious script----------------------"
   355          )
   356          response = requests.get(
   357              virtual_server_setup.backend_1_url + "</script>",
   358              headers={"host": virtual_server_setup.vs_host},
   359          )
   360          print(response.text)
   361          wait_before_test(5)
   362          log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod, test_namespace)
   363  
   364          delete_policy(kube_apis.custom_objects, "waf-policy", test_namespace)
   365          self.restore_default_vs(kube_apis, virtual_server_setup)
   366  
   367          assert_invalid_responses(response)
   368          assert (
   369              f'ASM:attack_type="Non-browser Client,Abuse of Functionality,Cross Site Scripting (XSS)"'
   370              in log_contents
   371          )
   372          assert f'severity="Critical"' in log_contents
   373          assert f'request_status="blocked"' in log_contents
   374          assert f'outcome="REJECTED"' in log_contents
   375  
   376  
   377  @pytest.mark.skip_for_nginx_oss
   378  @pytest.mark.appprotect
   379  @pytest.mark.parametrize(
   380      "crd_ingress_controller_with_ap, v_s_route_setup",
   381      [
   382          (
   383              {
   384                  "type": "complete",
   385                  "extra_args": [
   386                      f"-enable-custom-resources",
   387                      f"-enable-leader-election=false",
   388                      f"-enable-app-protect",
   389                      f"-enable-preview-policies",
   390                  ],
   391              },
   392              {"example": "virtual-server-route"},
   393          )
   394      ],
   395      indirect=True,
   396  )
   397  class TestAppProtectWAFPolicyVSR:
   398      def restore_default_vsr(self, kube_apis, v_s_route_setup) -> None:
   399          """
   400          Function to revert vsr deployments to standard state
   401          """
   402          patch_src_m = f"{TEST_DATA}/virtual-server-route/route-multiple.yaml"
   403          patch_v_s_route_from_yaml(
   404              kube_apis.custom_objects,
   405              v_s_route_setup.route_m.name,
   406              patch_src_m,
   407              v_s_route_setup.route_m.namespace,
   408          )
   409          wait_before_test()
   410  
   411      @pytest.mark.parametrize(
   412          "ap_enable",
   413          [
   414              True, 
   415              # False
   416          ],
   417      )
   418      def test_ap_waf_policy_block(
   419          self,
   420          kube_apis,
   421          crd_ingress_controller_with_ap,
   422          v_s_route_setup,
   423          appprotect_setup,
   424          test_namespace,
   425          ap_enable,
   426      ):
   427          """
   428          Test if WAF policy is working with VSR deployments
   429          """
   430          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   431  
   432          print(f"Create waf policy")
   433          create_ap_waf_policy_from_yaml(
   434              kube_apis.custom_objects,
   435              waf_pol_dataguard_src,
   436              v_s_route_setup.route_m.namespace,
   437              test_namespace,
   438              ap_enable,
   439              ap_enable,
   440              ap_pol_name,
   441              log_name,
   442              "syslog:server=127.0.0.1:514",
   443          )
   444          wait_before_test()
   445          print(f"Patch vsr with policy: {waf_subroute_vsr_src}")
   446          patch_v_s_route_from_yaml(
   447              kube_apis.custom_objects,
   448              v_s_route_setup.route_m.name,
   449              waf_subroute_vsr_src,
   450              v_s_route_setup.route_m.namespace,
   451          )
   452          wait_before_test()
   453          ap_crd_info = read_ap_custom_resource(
   454              kube_apis.custom_objects, test_namespace, "appolicies", ap_policy_uds
   455          )
   456          assert_ap_crd_info(ap_crd_info, ap_policy_uds)
   457          wait_before_test(120)
   458          response = requests.get(
   459              f"{req_url}{v_s_route_setup.route_m.paths[0]}+'</script>'",
   460              headers={"host": v_s_route_setup.vs_host},
   461          )
   462          print(response.text)
   463          delete_policy(kube_apis.custom_objects, "waf-policy", v_s_route_setup.route_m.namespace)
   464          self.restore_default_vsr(kube_apis, v_s_route_setup)
   465          if ap_enable == True:
   466              assert_invalid_responses(response)
   467          elif ap_enable == False:
   468              assert_valid_responses(response)
   469          else:
   470              pytest.fail(f"Invalid arguments")