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)
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
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
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)
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)
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)
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