def test_connect_grpc_backend(self, kube_apis, ingress_controller_prerequisites, crd_ingress_controller, backend_setup, virtual_server_setup) -> None: cert = get_certificate(virtual_server_setup.public_endpoint.public_ip, virtual_server_setup.vs_host, virtual_server_setup.public_endpoint.port_ssl) target = f'{virtual_server_setup.public_endpoint.public_ip}:{virtual_server_setup.public_endpoint.port_ssl}' credentials = grpc.ssl_channel_credentials( root_certificates=cert.encode()) options = (('grpc.ssl_target_name_override', virtual_server_setup.vs_host), ) with grpc.secure_channel(target, credentials, options) as channel: stub = GreeterStub(channel) response = "" try: response = stub.SayHello( HelloRequest( name=virtual_server_setup.public_endpoint.public_ip)) valid_message = "Hello {}".format( virtual_server_setup.public_endpoint.public_ip) assert valid_message in response.message except grpc.RpcError as e: print(e.details()) pytest.fail( "RPC error was not expected during call, exiting...")
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 grpc_waf_allow(kube_apis, test_namespace, public_ip, vs_host, port_ssl): cert = get_certificate(public_ip, vs_host, port_ssl) target = f'{public_ip}:{port_ssl}' credentials = grpc.ssl_channel_credentials(root_certificates=cert.encode()) options = (('grpc.ssl_target_name_override', vs_host), ) with grpc.secure_channel(target, credentials, options) as channel: stub = GreeterStub(channel) response = "" try: response = stub.SayHello(HelloRequest(name=public_ip)) print(response) except grpc.RpcError as e: print(e.details()) pytest.fail("RPC error was not expected during call, exiting...") assert valid_resp_txt in response.message
def test_config_after_setup(self, kube_apis, ingress_controller_prerequisites, crd_ingress_controller, backend_setup, virtual_server_setup): print("\nStep 1: assert config") ic_pod_name = get_first_pod_name( kube_apis.v1, ingress_controller_prerequisites.namespace) config = get_vs_nginx_template_conf( kube_apis.v1, virtual_server_setup.namespace, virtual_server_setup.vs_name, ic_pod_name, ingress_controller_prerequisites.namespace) assert_grpc_entries_exist(config) assert_proxy_entries_exist(config) print("\nStep 2: check connection to http backend") print("\nrequest URL: ", virtual_server_setup.backend_2_url) resp = requests.get(virtual_server_setup.backend_2_url, headers={"host": virtual_server_setup.vs_host}) print("Response from http backend: {}".format(resp)) assert resp.status_code == 200 print("\nStep 3: Check connection to app") cert = get_certificate(virtual_server_setup.public_endpoint.public_ip, virtual_server_setup.vs_host, virtual_server_setup.public_endpoint.port_ssl) target = f'{virtual_server_setup.public_endpoint.public_ip}:{virtual_server_setup.public_endpoint.port_ssl}' credentials = grpc.ssl_channel_credentials( root_certificates=cert.encode()) options = (('grpc.ssl_target_name_override', virtual_server_setup.vs_host), ) with grpc.secure_channel(target, credentials, options) as channel: stub = GreeterStub(channel) response = "" try: response = stub.SayHello( HelloRequest( name=virtual_server_setup.public_endpoint.public_ip)) valid_message = "Hello {}".format( virtual_server_setup.public_endpoint.public_ip) assert valid_message in response.message except grpc.RpcError as e: print(e.details()) pytest.fail( "RPC error was not expected during call, exiting...")
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_grpc_error_intercept(self, kube_apis, ingress_controller_prerequisites, crd_ingress_controller, backend_setup, virtual_server_setup): cert = get_certificate(virtual_server_setup.public_endpoint.public_ip, virtual_server_setup.vs_host, virtual_server_setup.public_endpoint.port_ssl) target = f'{virtual_server_setup.public_endpoint.public_ip}:{virtual_server_setup.public_endpoint.port_ssl}' credentials = grpc.ssl_channel_credentials(root_certificates=cert.encode()) options = (('grpc.ssl_target_name_override', virtual_server_setup.vs_host),) with grpc.secure_channel(target, credentials, options) as channel: stub = GreeterStub(channel) response = "" try: response = stub.SayHello(HelloRequest(name=virtual_server_setup.public_endpoint.public_ip)) valid_message = "Hello {}".format(virtual_server_setup.public_endpoint.public_ip) # no status has been returned in the response assert valid_message in response.message except grpc.RpcError as e: print(e.details()) pytest.fail("RPC error was not expected during call, exiting...") # Assert grpc_status is in the logs. The gRPC response in a successful call is 0. ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace) log_contents = kube_apis.v1.read_namespaced_pod_log(ic_pod_name, ingress_controller_prerequisites.namespace) retry = 0 while '"POST /helloworld.Greeter/SayHello HTTP/2.0" 200 0' not in log_contents and retry <= 60: log_contents = kube_apis.v1.read_namespaced_pod_log( ic_pod_name, ingress_controller_prerequisites.namespace) retry += 1 wait_before_test(1) print(f"Logs not yet updated, retrying... #{retry}") assert '"POST /helloworld.Greeter/SayHello HTTP/2.0" 200 0' in log_contents scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "grpc1", virtual_server_setup.namespace, 0) scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "grpc2", virtual_server_setup.namespace, 0) wait_before_test() with grpc.secure_channel(target, credentials, options) as channel: stub = GreeterStub(channel) try: response = stub.SayHello(HelloRequest(name=virtual_server_setup.public_endpoint.public_ip)) # assert the grpc status has been returned in the header assert response.status == 14 pytest.fail("RPC error was expected during call, exiting...") except grpc.RpcError as e: print(e) # Assert the grpc_status is also in the logs. ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace) wait_before_test() # Need to get full log because of a race condition on the last log entry. log_contents = kube_apis.v1.read_namespaced_pod_log(ic_pod_name, ingress_controller_prerequisites.namespace) retry = 0 while '"POST /helloworld.Greeter/SayHello HTTP/2.0" 204 14' not in log_contents and retry <= 60: log_contents = kube_apis.v1.read_namespaced_pod_log( ic_pod_name, ingress_controller_prerequisites.namespace) retry += 1 wait_before_test(1) print(f"Logs not yet updated, retrying... #{retry}")
def test_grpc_error_intercept(self, kube_apis, ingress_controller_prerequisites, crd_ingress_controller, backend_setup, virtual_server_setup): cert = get_certificate(virtual_server_setup.public_endpoint.public_ip, virtual_server_setup.vs_host, virtual_server_setup.public_endpoint.port_ssl) target = f'{virtual_server_setup.public_endpoint.public_ip}:{virtual_server_setup.public_endpoint.port_ssl}' credentials = grpc.ssl_channel_credentials( root_certificates=cert.encode()) options = (('grpc.ssl_target_name_override', virtual_server_setup.vs_host), ) with grpc.secure_channel(target, credentials, options) as channel: stub = GreeterStub(channel) response = "" try: response = stub.SayHello( HelloRequest( name=virtual_server_setup.public_endpoint.public_ip)) valid_message = "Hello {}".format( virtual_server_setup.public_endpoint.public_ip) # no status has been returned in the response assert valid_message in response.message except grpc.RpcError as e: print(e.details()) pytest.fail( "RPC error was not expected during call, exiting...") scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "grpc1", virtual_server_setup.namespace, 0) scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "grpc2", virtual_server_setup.namespace, 0) time.sleep(1) with grpc.secure_channel(target, credentials, options) as channel: stub = GreeterStub(channel) try: response = stub.SayHello( HelloRequest( name=virtual_server_setup.public_endpoint.public_ip)) # assert the grpc status has been returned in the header assert response.status == 14 pytest.fail("RPC error was expected during call, exiting...") except grpc.RpcError as e: print(e)