def test_keys_in_main_config(self, cli_arguments, kube_apis, ingress_controller_prerequisites,
                                 crd_ingress_controller, virtual_server_setup, clean_up):
        wait_before_test(1)
        ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
        initial_list = get_events(kube_apis.v1, virtual_server_setup.namespace)
        data_file = f"{TEST_DATA}/virtual-server-configmap-keys/configmap-validation-keys.yaml"
        data_file_invalid = f"{TEST_DATA}/virtual-server-configmap-keys/configmap-validation-keys-invalid.yaml"
        config_path = "/etc/nginx/nginx.conf"

        print("Step 5: main config: update ConfigMap with valid keys with validation rules")
        replace_configmap_from_yaml(kube_apis.v1,
                                    ingress_controller_prerequisites.config_map['metadata']['name'],
                                    ingress_controller_prerequisites.namespace,
                                    data_file)
        expected_values = get_configmap_fields_from_yaml(data_file)
        wait_before_test(1)
        step_5_config = get_file_contents(kube_apis.v1,
                                          config_path, ic_pod_name, ingress_controller_prerequisites.namespace)
        step_5_events = get_events(kube_apis.v1, virtual_server_setup.namespace)
        assert_update_event_count_increased(virtual_server_setup, step_5_events, initial_list)
        assert_keys_with_validation_in_main_config(step_5_config, expected_values)

        print("Step 6: main config: update ConfigMap with invalid keys")
        replace_configmap_from_yaml(kube_apis.v1,
                                    ingress_controller_prerequisites.config_map['metadata']['name'],
                                    ingress_controller_prerequisites.namespace,
                                    data_file_invalid)
        unexpected_values = get_configmap_fields_from_yaml(data_file_invalid)
        wait_before_test(1)
        step_6_config = get_file_contents(kube_apis.v1,
                                          config_path, ic_pod_name, ingress_controller_prerequisites.namespace)
        step_6_events = get_events(kube_apis.v1, virtual_server_setup.namespace)
        assert_update_event_count_increased(virtual_server_setup, step_6_events, step_5_events)
        assert_defaults_of_keys_with_validation_in_main_config(step_6_config, unexpected_values)

        print("Step 7: main config: special case for hash variables")
        data_file = f"{TEST_DATA}/virtual-server-configmap-keys/configmap-global-variables.yaml"
        expected_values = get_configmap_fields_from_yaml(data_file)
        replace_configmap_from_yaml(kube_apis.v1,
                                    ingress_controller_prerequisites.config_map['metadata']['name'],
                                    ingress_controller_prerequisites.namespace,
                                    data_file)
        wait_before_test(1)
        step_7_config = get_file_contents(kube_apis.v1,
                                          config_path, ic_pod_name, ingress_controller_prerequisites.namespace)
        step_7_events = get_events(kube_apis.v1, virtual_server_setup.namespace)
        assert_not_applied_event_emitted(virtual_server_setup, step_7_events, step_6_events)
        assert_keys_with_validation_in_main_config(step_7_config, expected_values)
Example #2
0
def find_in_log(kube_apis, log_location, syslog_pod, namespace, time, value):
    log_contents = ""
    retry = 0
    while (value not in log_contents and retry <= time / 10):
        log_contents = get_file_contents(kube_apis.v1, log_location,
                                         syslog_pod, namespace, False)
        retry += 1
        wait_before_test(10)
        print(f"{value} Not in log, retrying... #{retry}")
def get_vs_nginx_template_conf(v1: CoreV1Api, vs_namespace, vs_name, pod_name,
                               pod_namespace) -> str:
    """
    Get contents of /etc/nginx/conf.d/vs_{namespace}_{vs_name}.conf in the pod.

    :param v1: CoreV1Api
    :param vs_namespace:
    :param vs_name:
    :param pod_name:
    :param pod_namespace:
    :return: str
    """
    file_path = f"/etc/nginx/conf.d/vs_{vs_namespace}_{vs_name}.conf"
    return get_file_contents(v1, file_path, pod_name, pod_namespace)
    def test_responses_grpc_block(self, kube_apis,
                                  crd_ingress_controller_with_ap,
                                  backend_setup, test_namespace):
        """
        Test grpc-block-hello AppProtect policy: Blocks /sayhello gRPC method only
        Client sends request to /sayhello
        """
        syslog_pod = kube_apis.v1.list_namespaced_pod(
            test_namespace).items[-1].metadata.name

        # we need to get the cert so that it can be used in credentials in grpc.secure_channel to verify itself.
        # without verification, we will not be able to use the channel
        cert = get_certificate(backend_setup.ip, backend_setup.ingress_host,
                               backend_setup.port_ssl)

        target = f'{backend_setup.ip}:{backend_setup.port_ssl}'
        credentials = grpc.ssl_channel_credentials(
            root_certificates=cert.encode())

        # this option is necessary to set the SNI of a gRPC connection and it only works with grpc.secure_channel.
        # also, the TLS cert for the Ingress must have the CN equal to backend_setup.ingress_host
        options = (('grpc.ssl_target_name_override',
                    backend_setup.ingress_host), )

        with grpc.secure_channel(target, credentials, options) as channel:
            stub = GreeterStub(channel)
            ex = ""
            try:
                stub.SayHello(HelloRequest(name=backend_setup.ip))
                pytest.fail("RPC error was expected during call, exiting...")
            except grpc.RpcError as e:
                # grpc.RpcError is also grpc.Call https://grpc.github.io/grpc/python/grpc.html#client-side-context
                ex = e.details()
                print(ex)

        log_contents = ""
        retry = 0
        while "ASM:attack_type" not in log_contents and retry <= 30:
            log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                             test_namespace)
            retry += 1
            wait_before_test(1)
            print(f"Security log not updated, retrying... #{retry}")

        assert (invalid_resp_text in ex
                and 'ASM:attack_type="Directory Indexing"' in log_contents
                and 'violations="Illegal gRPC method"' in log_contents
                and 'severity="Error"' in log_contents
                and 'outcome="REJECTED"' in log_contents)
    def test_responses_grpc_allow(self, kube_apis,
                                  crd_ingress_controller_with_ap,
                                  backend_setup, test_namespace,
                                  ingress_controller_endpoint):
        """
        Test grpc-block-goodbye AppProtect policy: Blocks /saygoodbye gRPC method only
        Client sends request to /sayhello thus should pass
        """
        syslog_pod = kube_apis.v1.list_namespaced_pod(
            test_namespace).items[-1].metadata.name
        cert = get_certificate(backend_setup.ip, backend_setup.ingress_host,
                               backend_setup.port_ssl)

        target = f'{backend_setup.ip}:{backend_setup.port_ssl}'
        credentials = grpc.ssl_channel_credentials(
            root_certificates=cert.encode())
        options = (('grpc.ssl_target_name_override',
                    backend_setup.ingress_host), )

        with grpc.secure_channel(target, credentials, options) as channel:
            stub = GreeterStub(channel)
            response = ""
            try:
                response = stub.SayHello(HelloRequest(name=backend_setup.ip))
                print(response)
            except grpc.RpcError as e:
                print(e.details())
                pytest.fail(
                    "RPC error was not expected during call, exiting...")

        log_contents = ""
        retry = 0
        while "ASM:attack_type" not in log_contents and retry <= 30:
            log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                             test_namespace)
            retry += 1
            wait_before_test(1)
            print(f"Security log not updated, retrying... #{retry}")

        assert (valid_resp_txt in response.message
                and 'ASM:attack_type="N/A"' in log_contents
                and 'violations="N/A"' in log_contents
                and 'severity="Informational"' in log_contents
                and 'outcome="PASSED"' in log_contents)
 def test_responses_grpc_allow(self, kube_apis,
                               ingress_controller_prerequisites,
                               crd_ingress_controller_with_ap,
                               appprotect_setup, test_namespace):
     """
     Test grpc-block-goodbye AppProtect policy: Blocks /saygoodbye gRPC method only
     Client sends request to /sayhello thus should pass
     """
     grpc_waf_allow(kube_apis, test_namespace,
                    appprotect_setup.public_endpoint.public_ip,
                    appprotect_setup.vs_host,
                    appprotect_setup.public_endpoint.port_ssl)
     syslog_pod = kube_apis.v1.list_namespaced_pod(
         test_namespace).items[-1].metadata.name
     log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                      test_namespace)
     assert ('ASM:attack_type="N/A"' in log_contents
             and 'violations="N/A"' in log_contents
             and 'severity="Informational"' in log_contents
             and 'outcome="PASSED"' in log_contents)
 def test_responses_grpc_block(self, kube_apis,
                               ingress_controller_prerequisites,
                               crd_ingress_controller_with_ap,
                               appprotect_setup, test_namespace):
     """
     Test grpc-block-hello AppProtect policy: Blocks /sayhello gRPC method only
     Client sends request to /sayhello
     """
     grpc_waf_block(kube_apis, test_namespace,
                    appprotect_setup.public_endpoint.public_ip,
                    appprotect_setup.vs_host,
                    appprotect_setup.public_endpoint.port_ssl)
     syslog_pod = kube_apis.v1.list_namespaced_pod(
         test_namespace).items[-1].metadata.name
     log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                      test_namespace)
     assert ('ASM:attack_type="Directory Indexing"' in log_contents
             and 'violations="Illegal gRPC method"' in log_contents
             and 'severity="Error"' in log_contents
             and 'outcome="REJECTED"' in log_contents)
    def test_snippet_annotation_used(self, kube_apis,
                                     ingress_controller_prerequisites,
                                     ingress_controller, test_namespace):
        file_name = f"{TEST_DATA}/annotations/standard/annotations-ingress-snippets.yaml"
        ingress_name = create_ingress_from_yaml(kube_apis.networking_v1,
                                                test_namespace, file_name)
        time.sleep(5)
        pod_namespace = ingress_controller_prerequisites.namespace
        pod_name = get_first_pod_name(
            kube_apis.v1, ingress_controller_prerequisites.namespace)
        file_path = f"/etc/nginx/conf.d/{test_namespace}-{ingress_name}.conf"
        result_conf = get_file_contents(kube_apis.v1, file_path, pod_name,
                                        pod_namespace)
        snippet_annotation = "tcp_nodelay on;"
        assert snippet_annotation in result_conf, f"failed to find snippet ({snippet_annotation}) in nginx conf"

        # Now we assert the status of the ingress is correct
        event_text = f"Configuration for {test_namespace}/{ingress_name} was added or updated"
        events = get_events(kube_apis.v1, test_namespace)
        assert_event(event_text, events)

        delete_ingress(kube_apis.networking_v1, ingress_name, test_namespace)
    def test_ap_waf_policy_logs(
        self,
        kube_apis,
        crd_ingress_controller_with_ap,
        virtual_server_setup,
        appprotect_setup,
        test_namespace,
    ):
        """
        Test waf policy logs
        """
        src_syslog_yaml = f"{TEST_DATA}/ap-waf/syslog.yaml"
        log_loc = f"/var/log/messages"
        create_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace)
        wait_before_test(40)
        syslog_ep = (kube_apis.v1.read_namespaced_endpoints(
            "syslog-svc", test_namespace).subsets[0].addresses[0].ip)
        syslog_pod = kube_apis.v1.list_namespaced_pod(
            test_namespace).items[-1].metadata.name
        print(f"Create waf policy")
        create_ap_waf_policy_from_yaml(
            kube_apis.custom_objects,
            waf_pol_dataguard_src,
            test_namespace,
            test_namespace,
            True,
            True,
            ap_pol_name,
            log_name,
            f"syslog:server={syslog_ep}:514",
        )
        wait_before_test()
        print(f"Patch vs with policy: {waf_spec_vs_src}")
        patch_virtual_server_from_yaml(
            kube_apis.custom_objects,
            virtual_server_setup.vs_name,
            waf_spec_vs_src,
            virtual_server_setup.namespace,
        )
        wait_before_test()
        ap_crd_info = read_ap_custom_resource(kube_apis.custom_objects,
                                              test_namespace, "appolicies",
                                              ap_policy_uds)
        assert_ap_crd_info(ap_crd_info, ap_policy_uds)
        wait_before_test(120)

        print(
            "----------------------- Send request with embedded malicious script----------------------"
        )
        response = requests.get(
            virtual_server_setup.backend_1_url + "</script>",
            headers={"host": virtual_server_setup.vs_host},
        )
        print(response.text)
        wait_before_test(5)
        log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                         test_namespace)

        delete_policy(kube_apis.custom_objects, "waf-policy", test_namespace)
        self.restore_default_vs(kube_apis, virtual_server_setup)

        assert_invalid_responses(response)
        assert (
            f'ASM:attack_type="Non-browser Client,Abuse of Functionality,Cross Site Scripting (XSS)"'
            in log_contents)
        assert f'severity="Critical"' in log_contents
        assert f'request_status="blocked"' in log_contents
        assert f'outcome="REJECTED"' in log_contents
    def test_ap_multi_sec_logs(self, kube_apis, crd_ingress_controller_with_ap,
                               appprotect_setup, test_namespace):
        """
        Test corresponding log entries with multiple log destinations (in this case, two syslog servers)
        """
        src_syslog_yaml = f"{TEST_DATA}/appprotect/syslog.yaml"
        src_syslog2_yaml = f"{TEST_DATA}/appprotect/syslog2.yaml"
        log_loc = f"/var/log/messages"

        print("Create two syslog servers")
        create_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace)
        create_items_from_yaml(kube_apis, src_syslog2_yaml, test_namespace)

        syslog_ep = get_service_endpoint(kube_apis, "syslog-svc",
                                         test_namespace)
        syslog2_ep = get_service_endpoint(kube_apis, "syslog2-svc",
                                          test_namespace)

        syslog_pod = kube_apis.v1.list_namespaced_pod(
            test_namespace).items[-2].metadata.name
        syslog2_pod = kube_apis.v1.list_namespaced_pod(
            test_namespace).items[-1].metadata.name

        with open(src_ing_yaml) as f:
            doc = yaml.safe_load(f)

            doc["metadata"]["annotations"][
                "appprotect.f5.com/app-protect-policy"] = ap_policy
            doc["metadata"]["annotations"][
                "appprotect.f5.com/app-protect-enable"] = "True"
            doc["metadata"]["annotations"][
                "appprotect.f5.com/app-protect-security-log-enable"] = "True"

            # both lists need to be the same length, if one of the referenced configs is invalid/non-existent then no logconfs are applied.
            doc["metadata"]["annotations"][
                "appprotect.f5.com/app-protect-security-log"] = f"{test_namespace}/logconf,{test_namespace}/logconf"

            doc["metadata"]["annotations"][
                "appprotect.f5.com/app-protect-security-log-destination"] = f"syslog:server={syslog_ep}:514,syslog:server={syslog2_ep}:514"

        create_ingress(kube_apis.extensions_v1_beta1, test_namespace, doc)

        ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml)

        ensure_response_from_backend(appprotect_setup.req_url,
                                     ingress_host,
                                     check404=True)

        print("----------------------- Send request ----------------------")
        response = requests.get(appprotect_setup.req_url + "/<script>",
                                headers={"host": ingress_host},
                                verify=False)
        print(response.text)
        log_contents = ""
        log2_contents = ""
        retry = 0
        while ("ASM:attack_type" not in log_contents
               and "ASM:attack_type" not in log2_contents and retry <= 30):
            log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                             test_namespace)
            log2_contents = get_file_contents(kube_apis.v1, log_loc,
                                              syslog2_pod, test_namespace)
            retry += 1
            wait_before_test(1)
            print(f"Security log not updated, retrying... #{retry}")

        delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)
        delete_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace)
        delete_items_from_yaml(kube_apis, src_syslog2_yaml, test_namespace)

        assert_invalid_responses(response)
        # check logs in dest. #1 i.e. syslog server #1
        assert (
            f'ASM:attack_type="Non-browser Client,Abuse of Functionality,Cross Site Scripting (XSS)"'
            in log_contents and f'severity="Critical"' in log_contents
            and f'request_status="blocked"' in log_contents
            and f'outcome="REJECTED"' in log_contents)
        # check logs in dest. #2 i.e. syslog server #2
        assert (
            f'ASM:attack_type="Non-browser Client,Abuse of Functionality,Cross Site Scripting (XSS)"'
            in log2_contents and f'severity="Critical"' in log2_contents
            and f'request_status="blocked"' in log2_contents
            and f'outcome="REJECTED"' in log2_contents)
    def test_ap_sec_logs_on(
        self,
        request,
        kube_apis,
        ingress_controller_prerequisites,
        crd_ingress_controller_with_ap,
        appprotect_setup,
        test_namespace,
    ):
        """
        Test corresponding log entries with correct policy (includes setting up a syslog server as defined in syslog.yaml)
        """
        log_loc = "/var/log/messages"
        syslog_dst = f"syslog-svc.{test_namespace}"
        syslog_pod = get_pod_name_that_contains(kube_apis.v1, test_namespace,
                                                "syslog-")

        create_ingress_with_ap_annotations(kube_apis, src_ing_yaml,
                                           test_namespace, ap_policy, "True",
                                           "True", f"{syslog_dst}:514")
        ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml)

        print(
            "--------- Run test while AppProtect module is enabled with correct policy ---------"
        )

        ensure_response_from_backend(appprotect_setup.req_url,
                                     ingress_host,
                                     check404=True)

        print(
            "----------------------- Send invalid request ----------------------"
        )
        response_block = requests.get(appprotect_setup.req_url + "/<script>",
                                      headers={"host": ingress_host},
                                      verify=False)
        print(response_block.text)
        log_contents_block = ""
        retry = 0
        while "ASM:attack_type" not in log_contents_block and retry <= 30:
            log_contents_block = get_file_contents(kube_apis.v1, log_loc,
                                                   syslog_pod, test_namespace)
            retry += 1
            wait_before_test(1)
            print(f"Security log not updated, retrying... #{retry}")

        print(
            "----------------------- Send valid request ----------------------"
        )
        headers = {
            "Host":
            ingress_host,
            "User-Agent":
            "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0",
        }
        response = requests.get(appprotect_setup.req_url,
                                headers=headers,
                                verify=False)
        print(response.text)
        wait_before_test(10)
        log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                         test_namespace)

        delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)
        clear_file_contents(kube_apis.v1, log_loc, syslog_pod, test_namespace)

        assert_invalid_responses(response_block)
        assert (
            'ASM:attack_type="Non-browser Client,Abuse of Functionality,Cross Site Scripting (XSS)"'
            in log_contents_block)
        assert 'severity="Critical"' in log_contents_block
        assert 'request_status="blocked"' in log_contents_block
        assert 'outcome="REJECTED"' in log_contents_block

        assert_valid_responses(response)
        assert 'ASM:attack_type="N/A"' in log_contents
        assert 'severity="Informational"' in log_contents
        assert 'request_status="passed"' in log_contents
        assert 'outcome="PASSED"' in log_contents
Example #12
0
    def test_dos_sec_logs_on(
        self,
        kube_apis,
        ingress_controller_prerequisites,
        crd_ingress_controller_with_dos,
        dos_setup,
        test_namespace,
    ):
        """
        Test corresponding log entries with correct policy (includes setting up a syslog server as defined in syslog.yaml)
        """
        print(
            "----------------------- Get syslog pod name ----------------------"
        )
        syslog_pod = self.getPodNameThatContains(
            kube_apis, ingress_controller_prerequisites.namespace, "syslog")
        assert "syslog" in syslog_pod

        log_loc = f"/var/log/messages"
        clear_file_contents(kube_apis.v1, log_loc, syslog_pod,
                            ingress_controller_prerequisites.namespace)

        create_ingress_with_dos_annotations(kube_apis, src_ing_yaml,
                                            test_namespace,
                                            test_namespace + "/dos-protected")
        ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml)

        print(
            "--------- Run test while DOS module is enabled with correct policy ---------"
        )

        ensure_response_from_backend(dos_setup.req_url,
                                     ingress_host,
                                     check404=True)
        pod_name = self.getPodNameThatContains(
            kube_apis, ingress_controller_prerequisites.namespace,
            "nginx-ingress")

        get_ingress_nginx_template_conf(kube_apis.v1, test_namespace,
                                        "dos-ingress", pod_name,
                                        "nginx-ingress")

        print("----------------------- Send request ----------------------")
        response = requests.get(dos_setup.req_url,
                                headers={"host": "dos.example.com"},
                                verify=False)
        print(response.text)
        wait_before_test(10)

        print(
            f'log_loc {log_loc} syslog_pod {syslog_pod} namespace {ingress_controller_prerequisites.namespace}'
        )
        log_contents = get_file_contents(
            kube_apis.v1, log_loc, syslog_pod,
            ingress_controller_prerequisites.namespace)

        delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)

        print(log_contents)

        assert 'product="app-protect-dos"' in log_contents
        assert f'vs_name="{test_namespace}/dos-protected/name"' in log_contents
        assert 'bad_actor' in log_contents
Example #13
0
    def test_dos_arbitrator(self, kube_apis, ingress_controller_prerequisites,
                            crd_ingress_controller_with_dos, dos_setup,
                            test_namespace):
        """
        Test App Protect Dos: Check new IC pod get learning info
        """
        print(
            "----------------------- Get syslog pod name ----------------------"
        )
        syslog_pod = self.getPodNameThatContains(
            kube_apis, ingress_controller_prerequisites.namespace, "syslog")
        assert "syslog" in syslog_pod
        log_loc = f"/var/log/messages"
        clear_file_contents(kube_apis.v1, log_loc, syslog_pod,
                            ingress_controller_prerequisites.namespace)

        print(
            "------------------------- Deploy ingress -----------------------------"
        )
        create_ingress_with_dos_annotations(kube_apis, src_ing_yaml,
                                            test_namespace,
                                            test_namespace + "/dos-protected")
        ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml)

        # print("------------------------- Learning Phase -----------------------------")
        print("start good clients requests")
        p_good_client = subprocess.Popen([
            f"exec {TEST_DATA}/dos/good_clients_xff.sh {ingress_host} {dos_setup.req_url}"
        ],
                                         preexec_fn=os.setsid,
                                         shell=True,
                                         stdout=subprocess.DEVNULL,
                                         stderr=subprocess.DEVNULL)

        print("Learning for max 10 minutes")
        find_in_log(kube_apis, log_loc, syslog_pod,
                    ingress_controller_prerequisites.namespace, 600,
                    "learning_confidence=\"Ready\"")

        print(
            "------------------------- Check new IC pod get info from arbitrator -----------------------------"
        )
        ic_ns = ingress_controller_prerequisites.namespace
        scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "nginx-ingress",
                         ic_ns, 2)
        while get_pods_amount_with_name(kube_apis.v1, "nginx-ingress",
                                        "nginx-ingress") is not 2:
            print(f"Number of replicas is not 2, retrying...")
            wait_before_test()

        print(
            "------------------------- Check if new pod receive info from arbitrator -----------------------------"
        )
        print("Wait for 30 seconds")
        wait_before_test(30)

        log_contents = get_file_contents(
            kube_apis.v1, log_loc, syslog_pod,
            ingress_controller_prerequisites.namespace)
        log_info_dic = log_content_to_dic(log_contents)

        print("Stop Good Client")
        p_good_client.terminate()

        learning_units_hostname = []
        for log in log_info_dic:
            if log['unit_hostname'] not in learning_units_hostname and log[
                    'learning_confidence'] == "Ready":
                learning_units_hostname.append(log['unit_hostname'])

        delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)

        assert (len(learning_units_hostname) == 2)
Example #14
0
    def test_dos_under_attack_with_learning(self, kube_apis,
                                            ingress_controller_prerequisites,
                                            crd_ingress_controller_with_dos,
                                            dos_setup, test_namespace):
        """
        Test App Protect Dos: Block bad clients attack with learning
        """
        log_loc = f"/var/log/messages"
        print(
            "----------------------- Get syslog pod name ----------------------"
        )
        syslog_pod = self.getPodNameThatContains(
            kube_apis, ingress_controller_prerequisites.namespace, "syslog")
        assert "syslog" in syslog_pod
        clear_file_contents(kube_apis.v1, log_loc, syslog_pod,
                            ingress_controller_prerequisites.namespace)

        print(
            "------------------------- Deploy ingress -----------------------------"
        )
        create_ingress_with_dos_annotations(kube_apis, src_ing_yaml,
                                            test_namespace,
                                            test_namespace + "/dos-protected")
        ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml)

        print(
            "------------------------- Learning Phase -----------------------------"
        )
        print("start good clients requests")
        p_good_client = subprocess.Popen([
            f"exec {TEST_DATA}/dos/good_clients_xff.sh {ingress_host} {dos_setup.req_url}"
        ],
                                         preexec_fn=os.setsid,
                                         shell=True,
                                         stdout=subprocess.DEVNULL,
                                         stderr=subprocess.DEVNULL)

        print("Learning for max 10 minutes")
        find_in_log(kube_apis, log_loc, syslog_pod,
                    ingress_controller_prerequisites.namespace, 600,
                    "learning_confidence=\"Ready\"")

        print("------------------------- Attack -----------------------------")
        print("start bad clients requests")
        p_attack = subprocess.Popen([
            f"exec {TEST_DATA}/dos/bad_clients_xff.sh {ingress_host} {dos_setup.req_url}"
        ],
                                    preexec_fn=os.setsid,
                                    shell=True,
                                    stdout=subprocess.DEVNULL,
                                    stderr=subprocess.DEVNULL)

        print("Attack for 300 seconds")
        wait_before_test(300)

        print("Stop Attack")
        p_attack.terminate()

        print("wait max 140 seconds after attack stop, to get attack ended")
        find_in_log(kube_apis, log_loc, syslog_pod,
                    ingress_controller_prerequisites.namespace, 140,
                    "attack_event=\"Attack ended\"")

        print("Stop Good Client")
        p_good_client.terminate()

        log_contents = get_file_contents(
            kube_apis.v1, log_loc, syslog_pod,
            ingress_controller_prerequisites.namespace)
        log_info_dic = log_content_to_dic(log_contents)

        # Analyze the log
        no_attack = False
        attack_started = False
        under_attack = False
        attack_ended = False
        bad_actor_detected = False
        signature_detected = False
        health_ok = False
        bad_ip = ['1.1.1.1', '1.1.1.2', '1.1.1.3']
        fmt = '%b %d %Y %H:%M:%S'
        for log in log_info_dic:
            if log['attack_event'] == 'No Attack':
                if int(log['dos_attack_id']) == 0 and not no_attack:
                    no_attack = True
            elif log['attack_event'] == 'Attack started':
                if int(log['dos_attack_id']) > 0 and not attack_started:
                    attack_started = True
                    start_attack_time = datetime.strptime(
                        log['date_time'], fmt)
            elif log['attack_event'] == 'Under Attack':
                under_attack = True
                if not health_ok and float(log['stress_level']) < 0.6:
                    health_ok = True
                    health_ok_time = datetime.strptime(log['date_time'], fmt)
            elif log['attack_event'] == 'Attack signature detected':
                signature_detected = True
            elif log['attack_event'] == 'Bad actors detected':
                if under_attack:
                    bad_actor_detected = True
            elif log['attack_event'] == 'Bad actor detection':
                if under_attack and log['source_ip'] in bad_ip:
                    bad_ip.remove(log['source_ip'])
            elif log['attack_event'] == 'Attack ended':
                attack_ended = True

        delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)

        assert (no_attack and attack_started and under_attack and attack_ended
                and health_ok
                and (health_ok_time - start_attack_time).total_seconds() < 150
                and signature_detected and bad_actor_detected
                and len(bad_ip) == 0)
Example #15
0
    def test_dos_under_attack_no_learning(self, kube_apis,
                                          ingress_controller_prerequisites,
                                          crd_ingress_controller_with_dos,
                                          dos_setup, test_namespace):
        """
        Test App Protect Dos: Block bad clients attack
        """
        log_loc = f"/var/log/messages"
        print(
            "----------------------- Get syslog pod name ----------------------"
        )
        syslog_pod = self.getPodNameThatContains(
            kube_apis, ingress_controller_prerequisites.namespace, "syslog")
        assert "syslog" in syslog_pod
        clear_file_contents(kube_apis.v1, log_loc, syslog_pod,
                            ingress_controller_prerequisites.namespace)

        print(
            "------------------------- Deploy ingress -----------------------------"
        )
        create_ingress_with_dos_annotations(kube_apis, src_ing_yaml,
                                            test_namespace,
                                            test_namespace + "/dos-protected")
        ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml)

        print("------------------------- Attack -----------------------------")
        wait_before_test(10)
        print("start bad clients requests")
        p_attack = subprocess.Popen([
            f"exec {TEST_DATA}/dos/bad_clients_xff.sh {ingress_host} {dos_setup.req_url}"
        ],
                                    shell=True,
                                    stdout=subprocess.DEVNULL,
                                    stderr=subprocess.DEVNULL)

        print("Attack for 30 seconds")
        wait_before_test(30)

        print("Stop Attack")
        p_attack.terminate()

        print("wait max 140 seconds after attack stop, to get attack ended")
        find_in_log(kube_apis, log_loc, syslog_pod,
                    ingress_controller_prerequisites.namespace, 140,
                    "attack_event=\"Attack ended\"")

        log_contents = get_file_contents(
            kube_apis.v1, log_loc, syslog_pod,
            ingress_controller_prerequisites.namespace)
        log_info_dic = log_content_to_dic(log_contents)

        # Analyze the log
        no_attack = False
        attack_started = False
        under_attack = False
        attack_ended = False
        for log in log_info_dic:
            # Start with no attack
            if log['attack_event'] == "No Attack" and int(
                    log['dos_attack_id']) == 0 and not no_attack:
                no_attack = True
            # Attack started
            elif log['attack_event'] == "Attack started" and int(
                    log['dos_attack_id']) > 0 and not attack_started:
                attack_started = True
            # Under attack
            elif log['attack_event'] == "Under Attack" and int(
                    log['dos_attack_id']) > 0 and not under_attack:
                under_attack = True
            # Attack ended
            elif log['attack_event'] == "Attack ended" and int(
                    log['dos_attack_id']) > 0 and not attack_ended:
                attack_ended = True

        delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)

        assert (no_attack and attack_started and under_attack and attack_ended)
Example #16
0
    def test_ap_sec_logs_on(self, kube_apis, crd_ingress_controller_with_ap,
                            appprotect_setup, test_namespace):
        """
        Test corresponding log entries with correct policy (includes setting up a syslog server as defined in syslog.yaml)
        """
        src_syslog_yaml = f"{TEST_DATA}/appprotect/syslog.yaml"
        log_loc = f"/var/log/messages"

        create_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace)

        wait_before_test(40)
        syslog_ep = (kube_apis.v1.read_namespaced_endpoints(
            "syslog-svc", test_namespace).subsets[0].addresses[0].ip)

        # items[-1] because syslog pod is last one to spin-up
        syslog_pod = kube_apis.v1.list_namespaced_pod(
            test_namespace).items[-1].metadata.name

        create_ingress_with_ap_annotations(kube_apis, src_ing_yaml,
                                           test_namespace, ap_policy, "True",
                                           "True", f"{syslog_ep}:514")
        ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml)

        print(
            "--------- Run test while AppProtect module is enabled with correct policy ---------"
        )

        wait_before_test(40)
        ensure_response_from_backend(appprotect_setup.req_url, ingress_host)

        print(
            "----------------------- Send invalid request ----------------------"
        )
        response = requests.get(appprotect_setup.req_url + "/<script>",
                                headers={"host": ingress_host},
                                verify=False)
        print(response.text)
        wait_before_test(5)
        log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                         test_namespace)

        assert_invalid_responses(response)
        assert (
            f'ASM:attack_type="Non-browser Client,Abuse of Functionality,Cross Site Scripting (XSS)"'
            in log_contents)
        assert f'severity="Critical"' in log_contents
        assert f'request_status="blocked"' in log_contents
        assert f'outcome="REJECTED"' in log_contents

        print(
            "----------------------- Send valid request ----------------------"
        )
        headers = {
            "Host":
            ingress_host,
            "User-Agent":
            "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0",
        }
        response = requests.get(appprotect_setup.req_url,
                                headers=headers,
                                verify=False)
        print(response.text)
        wait_before_test(5)
        log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod,
                                         test_namespace)

        delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)
        delete_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace)

        assert_valid_responses(response)
        assert f'ASM:attack_type="N/A"' in log_contents
        assert f'severity="Informational"' in log_contents
        assert f'request_status="passed"' in log_contents
        assert f'outcome="PASSED"' in log_contents
    def test_ap_waf_policy_multi_logs(
        self,
        kube_apis,
        crd_ingress_controller_with_ap,
        virtual_server_setup,
        appprotect_setup,
        test_namespace,
    ):
        """
        Test waf policy logs
        """
        src_syslog_yaml = f"{TEST_DATA}/ap-waf/syslog.yaml"
        src_syslog_yaml_additional = f"{TEST_DATA}/ap-waf/syslog-1.yaml"
        log_loc = f"/var/log/messages"
        src_log_yaml_escape = f"{TEST_DATA}/ap-waf/logconf-esc.yaml"
        log_esc_name = create_ap_logconf_from_yaml(kube_apis.custom_objects,
                                                   src_log_yaml_escape,
                                                   test_namespace)
        create_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace)
        create_items_from_yaml(kube_apis, src_syslog_yaml_additional,
                               test_namespace)
        syslog_dst1 = f"syslog-svc.{test_namespace}"
        syslog_dst2 = f"syslog-svc-1.{test_namespace}"
        syslog_pod = kube_apis.v1.list_namespaced_pod(
            test_namespace, label_selector="app=syslog").items
        syslog_esc_pod = kube_apis.v1.list_namespaced_pod(
            test_namespace, label_selector="app=syslog-1").items
        print(f"Create waf policy")
        create_ap_multilog_waf_policy_from_yaml(
            kube_apis.custom_objects, waf_pol_dataguard_src, test_namespace,
            test_namespace, True, True, ap_pol_name, [log_name, log_esc_name],
            [
                f"syslog:server={syslog_dst1}:514",
                f"syslog:server={syslog_dst2}:514"
            ])
        wait_before_test()
        print(f"Patch vs with policy: {waf_spec_vs_src}")
        patch_virtual_server_from_yaml(
            kube_apis.custom_objects,
            virtual_server_setup.vs_name,
            waf_spec_vs_src,
            virtual_server_setup.namespace,
        )
        wait_before_test()
        ap_crd_info = read_ap_custom_resource(kube_apis.custom_objects,
                                              test_namespace, "appolicies",
                                              ap_policy_uds)
        assert_ap_crd_info(ap_crd_info, ap_policy_uds)
        wait_before_test(120)

        print(
            "----------------------- Send request with embedded malicious script----------------------"
        )
        response = requests.get(
            virtual_server_setup.backend_1_url + "</script>",
            headers={"host": virtual_server_setup.vs_host},
        )
        print(response.text)
        log_contents = ""
        retry = 0
        while "ASM:attack_type" not in log_contents and retry <= 30:
            log_contents = get_file_contents(kube_apis.v1, log_loc,
                                             syslog_pod[0].metadata.name,
                                             test_namespace)
            retry += 1
            wait_before_test(1)
            print(log_contents)
            print(f"Security log not updated, retrying... #{retry}")

        log_esc_contents = ""
        retry = 0
        while "attack_type" not in log_esc_contents and retry <= 30:
            log_esc_contents = get_file_contents(
                kube_apis.v1, log_loc, syslog_esc_pod[0].metadata.name,
                test_namespace)
            retry += 1
            wait_before_test(1)
            print(log_esc_contents)
            print(f"Security log not updated, retrying... #{retry}")

        delete_policy(kube_apis.custom_objects, "waf-policy", test_namespace)
        self.restore_default_vs(kube_apis, virtual_server_setup)

        assert_invalid_responses(response)

        assert f'ASM:attack_type="Non-browser Client,Abuse of Functionality,Cross Site Scripting (XSS)"' in log_contents
        assert f'severity="Critical"' in log_contents
        assert f'request_status="blocked"' in log_contents
        assert f'outcome="REJECTED"' in log_contents
        assert f'"my_attack_type": "[Non-browser Client' in log_esc_contents