github.com/nginxinc/kubernetes-ingress@v1.12.5/tests/suite/test_app_protect_grpc.py (about) 1 import grpc 2 import pytest 3 from settings import TEST_DATA, DEPLOYMENTS 4 from suite.custom_resources_utils import ( 5 create_ap_logconf_from_yaml, 6 create_ap_policy_from_yaml, 7 delete_ap_policy, 8 delete_ap_logconf, 9 ) 10 from suite.grpc.helloworld_pb2 import HelloRequest 11 from suite.grpc.helloworld_pb2_grpc import GreeterStub 12 13 from suite.resources_utils import ( 14 wait_before_test, 15 create_example_app, 16 wait_until_all_pods_are_ready, 17 create_items_from_yaml, 18 delete_items_from_yaml, 19 delete_common_app, 20 replace_configmap_from_yaml, 21 create_ingress_with_ap_annotations, 22 wait_before_test, 23 get_file_contents, 24 get_service_endpoint, 25 ) 26 from suite.ssl_utils import get_certificate 27 from suite.yaml_utils import get_first_ingress_host_from_yaml 28 29 log_loc = f"/var/log/messages" 30 valid_resp_txt = "Hello" 31 invalid_resp_text = "The request was rejected. Please consult with your administrator." 32 33 class BackendSetup: 34 """ 35 Encapsulate the example details. 36 37 Attributes: 38 ingress_host (str): 39 """ 40 41 def __init__(self, ingress_host, ip, port_ssl): 42 self.ingress_host = ingress_host 43 self.ip = ip 44 self.port_ssl = port_ssl 45 46 47 @pytest.fixture(scope="function") 48 def backend_setup(request, kube_apis, ingress_controller_endpoint, ingress_controller_prerequisites, test_namespace) -> BackendSetup: 49 """ 50 Deploy a simple application and AppProtect manifests. 51 52 :param request: pytest fixture 53 :param kube_apis: client apis 54 :param ingress_controller_endpoint: public endpoint 55 :param test_namespace: 56 :return: BackendSetup 57 """ 58 print("------------------------- Replace ConfigMap with HTTP2 -------------------------") 59 replace_configmap_from_yaml(kube_apis.v1, 60 ingress_controller_prerequisites.config_map['metadata']['name'], 61 ingress_controller_prerequisites.namespace, 62 f"{TEST_DATA}/appprotect/grpc/nginx-config.yaml") 63 64 policy = request.param["policy"] 65 print("------------------------- Deploy backend application -------------------------") 66 create_example_app(kube_apis, "grpc", test_namespace) 67 wait_until_all_pods_are_ready(kube_apis.v1, test_namespace) 68 69 print("------------------------- Deploy Secret -----------------------------") 70 src_sec_yaml = f"{TEST_DATA}/appprotect/appprotect-secret.yaml" 71 create_items_from_yaml(kube_apis, src_sec_yaml, test_namespace) 72 73 print("------------------------- Deploy logconf -----------------------------") 74 src_log_yaml = f"{TEST_DATA}/appprotect/logconf.yaml" 75 log_name = create_ap_logconf_from_yaml(kube_apis.custom_objects, src_log_yaml, test_namespace) 76 77 print(f"------------------------- Deploy appolicy: {policy} ---------------------------") 78 src_pol_yaml = f"{TEST_DATA}/appprotect/grpc/{policy}.yaml" 79 pol_name = create_ap_policy_from_yaml(kube_apis.custom_objects, src_pol_yaml, test_namespace) 80 81 print("------------------------- Deploy Syslog -----------------------------") 82 src_syslog_yaml = f"{TEST_DATA}/appprotect/syslog.yaml" 83 create_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace) 84 syslog_ep = get_service_endpoint(kube_apis, "syslog-svc", test_namespace) 85 print(syslog_ep) 86 print("------------------------- Deploy ingress -----------------------------") 87 src_ing_yaml = f"{TEST_DATA}/appprotect/grpc/ingress.yaml" 88 create_ingress_with_ap_annotations(kube_apis, src_ing_yaml, test_namespace, policy, "True", "True", f"{syslog_ep}:514") 89 ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml) 90 wait_before_test(40) 91 92 def fin(): 93 print("Clean up:") 94 delete_items_from_yaml(kube_apis, src_syslog_yaml, test_namespace) 95 delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace) 96 delete_ap_policy(kube_apis.custom_objects, pol_name, test_namespace) 97 delete_ap_logconf(kube_apis.custom_objects, log_name, test_namespace) 98 delete_common_app(kube_apis, "grpc", test_namespace) 99 delete_items_from_yaml(kube_apis, src_sec_yaml, test_namespace) 100 replace_configmap_from_yaml(kube_apis.v1, 101 ingress_controller_prerequisites.config_map['metadata']['name'], 102 ingress_controller_prerequisites.namespace, 103 f"{DEPLOYMENTS}/common/nginx-config.yaml") 104 105 request.addfinalizer(fin) 106 107 return BackendSetup(ingress_host, ingress_controller_endpoint.public_ip, ingress_controller_endpoint.port_ssl) 108 109 110 @pytest.mark.skip_for_nginx_oss 111 @pytest.mark.appprotect 112 @pytest.mark.parametrize( 113 "crd_ingress_controller_with_ap", 114 [{"extra_args": [f"-enable-custom-resources", f"-enable-app-protect"]}], 115 indirect=["crd_ingress_controller_with_ap"], 116 ) 117 class TestAppProtect: 118 @pytest.mark.smoke 119 @pytest.mark.parametrize("backend_setup", [{"policy": "grpc-block-sayhello"}], indirect=True) 120 def test_responses_grpc_block( 121 self, kube_apis, crd_ingress_controller_with_ap, backend_setup, test_namespace 122 ): 123 """ 124 Test grpc-block-hello AppProtect policy: Blocks /sayhello gRPC method only 125 Client sends request to /sayhello 126 """ 127 syslog_pod = kube_apis.v1.list_namespaced_pod(test_namespace).items[-1].metadata.name 128 129 # we need to get the cert so that it can be used in credentials in grpc.secure_channel to verify itself. 130 # without verification, we will not be able to use the channel 131 cert = get_certificate(backend_setup.ip, backend_setup.ingress_host, backend_setup.port_ssl) 132 133 target = f'{backend_setup.ip}:{backend_setup.port_ssl}' 134 credentials = grpc.ssl_channel_credentials(root_certificates=cert.encode()) 135 136 # this option is necessary to set the SNI of a gRPC connection and it only works with grpc.secure_channel. 137 # also, the TLS cert for the Ingress must have the CN equal to backend_setup.ingress_host 138 options = (('grpc.ssl_target_name_override', backend_setup.ingress_host),) 139 140 with grpc.secure_channel(target, credentials, options) as channel: 141 stub = GreeterStub(channel) 142 ex = "" 143 try: 144 stub.SayHello(HelloRequest(name=backend_setup.ip)) 145 pytest.fail("RPC error was expected during call, exiting...") 146 except grpc.RpcError as e: 147 # grpc.RpcError is also grpc.Call https://grpc.github.io/grpc/python/grpc.html#client-side-context 148 ex = e.details() 149 print(ex) 150 151 log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod, test_namespace) 152 assert ( 153 invalid_resp_text in ex and 154 'ASM:attack_type="Directory Indexing"' in log_contents and 155 'violations="Illegal gRPC method"' in log_contents and 156 'severity="Error"' in log_contents and 157 'outcome="REJECTED"' in log_contents 158 ) 159 160 @pytest.mark.parametrize("backend_setup", [{"policy": "grpc-block-saygoodbye"}], indirect=True) 161 def test_responses_grpc_allow( 162 self, kube_apis, crd_ingress_controller_with_ap, backend_setup, test_namespace, ingress_controller_endpoint 163 ): 164 """ 165 Test grpc-block-goodbye AppProtect policy: Blocks /saygoodbye gRPC method only 166 Client sends request to /sayhello thus should pass 167 """ 168 syslog_pod = kube_apis.v1.list_namespaced_pod(test_namespace).items[-1].metadata.name 169 cert = get_certificate(backend_setup.ip, backend_setup.ingress_host, backend_setup.port_ssl) 170 171 target = f'{backend_setup.ip}:{backend_setup.port_ssl}' 172 credentials = grpc.ssl_channel_credentials(root_certificates=cert.encode()) 173 options = (('grpc.ssl_target_name_override', backend_setup.ingress_host),) 174 175 with grpc.secure_channel(target, credentials, options) as channel: 176 stub = GreeterStub(channel) 177 response = "" 178 try: 179 response = stub.SayHello(HelloRequest(name=backend_setup.ip)) 180 print(response) 181 except grpc.RpcError as e: 182 print(e.details()) 183 pytest.fail("RPC error was not expected during call, exiting...") 184 185 log_contents = get_file_contents(kube_apis.v1, log_loc, syslog_pod, test_namespace) 186 assert ( 187 valid_resp_txt in response.message and 188 'ASM:attack_type="N/A"' in log_contents and 189 'violations="N/A"' in log_contents and 190 'severity="Informational"' in log_contents and 191 'outcome="PASSED"' in log_contents 192 )