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

     1  import pytest, requests, time
     2  from kubernetes.client.rest import ApiException
     3  from suite.resources_utils import (
     4      wait_before_test,
     5      replace_configmap_from_yaml,
     6      create_secret_from_yaml,
     7      delete_secret,
     8      replace_secret,
     9  )
    10  from suite.custom_resources_utils import (
    11      read_custom_resource,
    12      delete_virtual_server,
    13      patch_virtual_server_from_yaml,
    14      patch_v_s_route_from_yaml,
    15      create_policy_from_yaml,
    16      delete_policy,
    17      read_policy,
    18  )
    19  from settings import TEST_DATA, DEPLOYMENTS
    20  
    21  std_vs_src = f"{TEST_DATA}/virtual-server-route/standard/virtual-server.yaml"
    22  std_vsr_src = f"{TEST_DATA}/virtual-server-route/route-multiple.yaml"
    23  jwk_sec_valid_src = f"{TEST_DATA}/jwt-policy/secret/jwk-secret-valid.yaml"
    24  jwk_sec_invalid_src = f"{TEST_DATA}/jwt-policy/secret/jwk-secret-invalid.yaml"
    25  jwt_pol_valid_src = f"{TEST_DATA}/jwt-policy/policies/jwt-policy-valid.yaml"
    26  jwt_pol_multi_src = f"{TEST_DATA}/jwt-policy/policies/jwt-policy-valid-multi.yaml"
    27  jwt_pol_invalid_src = f"{TEST_DATA}/jwt-policy/policies/jwt-policy-invalid.yaml"
    28  jwt_pol_invalid_sec_src = f"{TEST_DATA}/jwt-policy/policies/jwt-policy-invalid-secret.yaml"
    29  jwt_vsr_invalid_src = (
    30      f"{TEST_DATA}/jwt-policy/route-subroute/virtual-server-route-invalid-subroute.yaml"
    31  )
    32  jwt_vsr_invalid_sec_src = (
    33      f"{TEST_DATA}/jwt-policy/route-subroute/virtual-server-route-invalid-subroute-secret.yaml"
    34  )
    35  jwt_vsr_override_src = (
    36      f"{TEST_DATA}/jwt-policy/route-subroute/virtual-server-route-override-subroute.yaml"
    37  )
    38  jwt_vsr_valid_src = (
    39      f"{TEST_DATA}/jwt-policy/route-subroute/virtual-server-route-valid-subroute.yaml"
    40  )
    41  jwt_vsr_valid_multi_src = (
    42      f"{TEST_DATA}/jwt-policy/route-subroute/virtual-server-route-valid-subroute-multi.yaml"
    43  )
    44  jwt_vs_override_spec_src = (
    45      f"{TEST_DATA}/jwt-policy/route-subroute/virtual-server-vsr-spec-override.yaml"
    46  )
    47  jwt_vs_override_route_src = (
    48      f"{TEST_DATA}/jwt-policy/route-subroute/virtual-server-vsr-route-override.yaml"
    49  )
    50  valid_token = f"{TEST_DATA}/jwt-policy/token.jwt"
    51  invalid_token = f"{TEST_DATA}/jwt-policy/invalid-token.jwt"
    52  
    53  
    54  @pytest.mark.skip_for_nginx_oss
    55  @pytest.mark.policies
    56  @pytest.mark.parametrize(
    57      "crd_ingress_controller, v_s_route_setup",
    58      [
    59          (
    60              {
    61                  "type": "complete",
    62                  "extra_args": [
    63                      f"-enable-custom-resources",
    64                      f"-enable-preview-policies",
    65                      f"-enable-leader-election=false",
    66                  ],
    67              },
    68              {"example": "virtual-server-route"},
    69          )
    70      ],
    71      indirect=True,
    72  )
    73  class TestJWTPoliciesVsr:
    74      def setup_single_policy(self, kube_apis, namespace, token, secret, policy, vs_host):
    75          print(f"Create jwk secret")
    76          secret_name = create_secret_from_yaml(kube_apis.v1, namespace, secret)
    77  
    78          print(f"Create jwt policy")
    79          pol_name = create_policy_from_yaml(kube_apis.custom_objects, policy, namespace)
    80  
    81          wait_before_test()
    82          with open(token, "r") as file:
    83              data = file.readline()
    84          headers = {"host": vs_host, "token": data}
    85  
    86          return secret_name, pol_name, headers
    87  
    88      def setup_multiple_policies(
    89          self, kube_apis, namespace, token, secret, policy_1, policy_2, vs_host
    90      ):
    91          print(f"Create jwk secret")
    92          secret_name = create_secret_from_yaml(kube_apis.v1, namespace, secret)
    93  
    94          print(f"Create jwt policy #1")
    95          pol_name_1 = create_policy_from_yaml(kube_apis.custom_objects, policy_1, namespace)
    96          print(f"Create jwt policy #2")
    97          pol_name_2 = create_policy_from_yaml(kube_apis.custom_objects, policy_2, namespace)
    98  
    99          wait_before_test()
   100          with open(token, "r") as file:
   101              data = file.readline()
   102          headers = {"host": vs_host, "token": data}
   103  
   104          return secret_name, pol_name_1, pol_name_2, headers
   105  
   106      @pytest.mark.parametrize("token", [valid_token, invalid_token])
   107      def test_jwt_policy_token(
   108          self,
   109          kube_apis,
   110          crd_ingress_controller,
   111          v_s_route_app_setup,
   112          v_s_route_setup,
   113          test_namespace,
   114          token,
   115      ):
   116          """
   117              Test jwt-policy with no token, valid token and invalid token
   118          """
   119          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   120          secret, pol_name, headers = self.setup_single_policy(
   121              kube_apis,
   122              v_s_route_setup.route_m.namespace,
   123              token,
   124              jwk_sec_valid_src,
   125              jwt_pol_valid_src,
   126              v_s_route_setup.vs_host,
   127          )
   128  
   129          print(f"Patch vsr with policy: {jwt_vsr_valid_src}")
   130          patch_v_s_route_from_yaml(
   131              kube_apis.custom_objects,
   132              v_s_route_setup.route_m.name,
   133              jwt_vsr_valid_src,
   134              v_s_route_setup.route_m.namespace,
   135          )
   136          wait_before_test()
   137  
   138          resp1 = requests.get(
   139              f"{req_url}{v_s_route_setup.route_m.paths[0]}",
   140              headers={"host": v_s_route_setup.vs_host},
   141          )
   142          print(resp1.status_code)
   143  
   144          resp2 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers)
   145          print(resp2.status_code)
   146  
   147          delete_policy(kube_apis.custom_objects, pol_name, v_s_route_setup.route_m.namespace)
   148          delete_secret(kube_apis.v1, secret, v_s_route_setup.route_m.namespace)
   149  
   150          patch_v_s_route_from_yaml(
   151              kube_apis.custom_objects,
   152              v_s_route_setup.route_m.name,
   153              std_vsr_src,
   154              v_s_route_setup.route_m.namespace,
   155          )
   156  
   157          assert resp1.status_code == 401
   158          assert f"401 Authorization Required" in resp1.text
   159  
   160          if token == valid_token:
   161              assert resp2.status_code == 200
   162              assert f"Request ID:" in resp2.text
   163          else:
   164              assert resp2.status_code == 401
   165              assert f"Authorization Required" in resp2.text
   166  
   167      @pytest.mark.parametrize("jwk_secret", [jwk_sec_valid_src, jwk_sec_invalid_src])
   168      def test_jwt_policy_secret(
   169          self,
   170          kube_apis,
   171          crd_ingress_controller,
   172          v_s_route_app_setup,
   173          v_s_route_setup,
   174          test_namespace,
   175          jwk_secret,
   176      ):
   177          """
   178              Test jwt-policy with a valid and an invalid secret
   179          """
   180          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   181          if jwk_secret == jwk_sec_valid_src:
   182              pol = jwt_pol_valid_src
   183              vsr = jwt_vsr_valid_src
   184          elif jwk_secret == jwk_sec_invalid_src:
   185              pol = jwt_pol_invalid_sec_src
   186              vsr = jwt_vsr_invalid_sec_src
   187          else:
   188              pytest.fail(f"Not a valid case or parameter")
   189  
   190          secret, pol_name, headers = self.setup_single_policy(
   191              kube_apis,
   192              v_s_route_setup.route_m.namespace,
   193              valid_token,
   194              jwk_secret,
   195              pol,
   196              v_s_route_setup.vs_host,
   197          )
   198  
   199          print(f"Patch vsr with policy: {pol}")
   200          patch_v_s_route_from_yaml(
   201              kube_apis.custom_objects,
   202              v_s_route_setup.route_m.name,
   203              vsr,
   204              v_s_route_setup.route_m.namespace,
   205          )
   206          wait_before_test()
   207  
   208          resp = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers,)
   209          print(resp.status_code)
   210  
   211          crd_info = read_custom_resource(
   212              kube_apis.custom_objects,
   213              v_s_route_setup.route_m.namespace,
   214              "virtualserverroutes",
   215              v_s_route_setup.route_m.name,
   216          )
   217          delete_policy(kube_apis.custom_objects, pol_name, v_s_route_setup.route_m.namespace)
   218          delete_secret(kube_apis.v1, secret, v_s_route_setup.route_m.namespace)
   219  
   220          patch_v_s_route_from_yaml(
   221              kube_apis.custom_objects,
   222              v_s_route_setup.route_m.name,
   223              std_vsr_src,
   224              v_s_route_setup.route_m.namespace,
   225          )
   226  
   227          if jwk_secret == jwk_sec_valid_src:
   228              assert resp.status_code == 200
   229              assert f"Request ID:" in resp.text
   230              assert crd_info["status"]["state"] == "Valid"
   231          elif jwk_secret == jwk_sec_invalid_src:
   232              assert resp.status_code == 500
   233              assert f"Internal Server Error" in resp.text
   234              assert crd_info["status"]["state"] == "Warning"
   235          else:
   236              pytest.fail(f"Not a valid case or parameter")
   237  
   238      @pytest.mark.smoke
   239      @pytest.mark.parametrize("policy", [jwt_pol_valid_src, jwt_pol_invalid_src])
   240      def test_jwt_policy(
   241          self,
   242          kube_apis,
   243          crd_ingress_controller,
   244          v_s_route_app_setup,
   245          v_s_route_setup,
   246          test_namespace,
   247          policy,
   248      ):
   249          """
   250              Test jwt-policy with a valid and an invalid policy
   251          """
   252          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   253          if policy == jwt_pol_valid_src:
   254              vsr = jwt_vsr_valid_src
   255          elif policy == jwt_pol_invalid_src:
   256              vsr = jwt_vsr_invalid_src
   257          else:
   258              pytest.fail(f"Not a valid case or parameter")
   259  
   260          secret, pol_name, headers = self.setup_single_policy(
   261              kube_apis,
   262              v_s_route_setup.route_m.namespace,
   263              valid_token,
   264              jwk_sec_valid_src,
   265              policy,
   266              v_s_route_setup.vs_host,
   267          )
   268  
   269          print(f"Patch vsr with policy: {policy}")
   270          patch_v_s_route_from_yaml(
   271              kube_apis.custom_objects,
   272              v_s_route_setup.route_m.name,
   273              vsr,
   274              v_s_route_setup.route_m.namespace,
   275          )
   276          wait_before_test()
   277  
   278          resp = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers,)
   279          print(resp.status_code)
   280          policy_info = read_custom_resource(
   281              kube_apis.custom_objects, v_s_route_setup.route_m.namespace, "policies", pol_name
   282          )
   283          crd_info = read_custom_resource(
   284              kube_apis.custom_objects,
   285              v_s_route_setup.route_m.namespace,
   286              "virtualserverroutes",
   287              v_s_route_setup.route_m.name,
   288          )
   289          delete_policy(kube_apis.custom_objects, pol_name, v_s_route_setup.route_m.namespace)
   290          delete_secret(kube_apis.v1, secret, v_s_route_setup.route_m.namespace)
   291  
   292          patch_v_s_route_from_yaml(
   293              kube_apis.custom_objects,
   294              v_s_route_setup.route_m.name,
   295              std_vsr_src,
   296              v_s_route_setup.route_m.namespace,
   297          )
   298  
   299          if policy == jwt_pol_valid_src:
   300              assert resp.status_code == 200
   301              assert f"Request ID:" in resp.text
   302              assert crd_info["status"]["state"] == "Valid"
   303              assert (
   304                  policy_info["status"]
   305                  and policy_info["status"]["reason"] == "AddedOrUpdated"
   306                  and policy_info["status"]["state"] == "Valid"
   307              )
   308          elif policy == jwt_pol_invalid_src:
   309              assert resp.status_code == 500
   310              assert f"Internal Server Error" in resp.text
   311              assert crd_info["status"]["state"] == "Warning"
   312              assert (
   313                  policy_info["status"]
   314                  and policy_info["status"]["reason"] == "Rejected"
   315                  and policy_info["status"]["state"] == "Invalid"
   316              )
   317          else:
   318              pytest.fail(f"Not a valid case or parameter")
   319  
   320      def test_jwt_policy_delete_secret(
   321          self,
   322          kube_apis,
   323          crd_ingress_controller,
   324          v_s_route_app_setup,
   325          v_s_route_setup,
   326          test_namespace,
   327      ):
   328          """
   329              Test if requests result in 500 when secret is deleted
   330          """
   331          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   332          secret, pol_name, headers = self.setup_single_policy(
   333              kube_apis,
   334              v_s_route_setup.route_m.namespace,
   335              valid_token,
   336              jwk_sec_valid_src,
   337              jwt_pol_valid_src,
   338              v_s_route_setup.vs_host,
   339          )
   340  
   341          print(f"Patch vsr with policy: {jwt_pol_valid_src}")
   342          patch_v_s_route_from_yaml(
   343              kube_apis.custom_objects,
   344              v_s_route_setup.route_m.name,
   345              jwt_vsr_valid_src,
   346              v_s_route_setup.route_m.namespace,
   347          )
   348          wait_before_test()
   349  
   350          resp1 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers,)
   351          print(resp1.status_code)
   352  
   353          delete_secret(kube_apis.v1, secret, v_s_route_setup.route_m.namespace)
   354          resp2 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers,)
   355          print(resp2.status_code)
   356          crd_info = read_custom_resource(
   357              kube_apis.custom_objects,
   358              v_s_route_setup.route_m.namespace,
   359              "virtualserverroutes",
   360              v_s_route_setup.route_m.name,
   361          )
   362          delete_policy(kube_apis.custom_objects, pol_name, v_s_route_setup.route_m.namespace)
   363  
   364          patch_v_s_route_from_yaml(
   365              kube_apis.custom_objects,
   366              v_s_route_setup.route_m.name,
   367              std_vsr_src,
   368              v_s_route_setup.route_m.namespace,
   369          )
   370          assert resp1.status_code == 200
   371          assert f"Request ID:" in resp1.text
   372          assert crd_info["status"]["state"] == "Warning"
   373          assert (
   374              f"references an invalid secret {v_s_route_setup.route_m.namespace}/{secret}: secret doesn't exist or of an unsupported type"
   375              in crd_info["status"]["message"]
   376          )
   377          assert resp2.status_code == 500
   378          assert f"Internal Server Error" in resp2.text
   379  
   380      def test_jwt_policy_delete_policy(
   381          self,
   382          kube_apis,
   383          crd_ingress_controller,
   384          v_s_route_app_setup,
   385          v_s_route_setup,
   386          test_namespace,
   387      ):
   388          """
   389              Test if requests result in 500 when policy is deleted
   390          """
   391          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   392          secret, pol_name, headers = self.setup_single_policy(
   393              kube_apis,
   394              v_s_route_setup.route_m.namespace,
   395              valid_token,
   396              jwk_sec_valid_src,
   397              jwt_pol_valid_src,
   398              v_s_route_setup.vs_host,
   399          )
   400  
   401          print(f"Patch vsr with policy: {jwt_pol_valid_src}")
   402          patch_v_s_route_from_yaml(
   403              kube_apis.custom_objects,
   404              v_s_route_setup.route_m.name,
   405              jwt_vsr_valid_src,
   406              v_s_route_setup.route_m.namespace,
   407          )
   408          wait_before_test()
   409  
   410          resp1 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers,)
   411          print(resp1.status_code)
   412          delete_policy(kube_apis.custom_objects, pol_name, v_s_route_setup.route_m.namespace)
   413  
   414          resp2 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers,)
   415          print(resp2.status_code)
   416          crd_info = read_custom_resource(
   417              kube_apis.custom_objects,
   418              v_s_route_setup.route_m.namespace,
   419              "virtualserverroutes",
   420              v_s_route_setup.route_m.name,
   421          )
   422          delete_secret(kube_apis.v1, secret, v_s_route_setup.route_m.namespace)
   423  
   424          patch_v_s_route_from_yaml(
   425              kube_apis.custom_objects,
   426              v_s_route_setup.route_m.name,
   427              std_vsr_src,
   428              v_s_route_setup.route_m.namespace,
   429          )
   430          assert resp1.status_code == 200
   431          assert f"Request ID:" in resp1.text
   432          assert crd_info["status"]["state"] == "Warning"
   433          assert (
   434              f"{v_s_route_setup.route_m.namespace}/{pol_name} is missing"
   435              in crd_info["status"]["message"]
   436          )
   437          assert resp2.status_code == 500
   438          assert f"Internal Server Error" in resp2.text
   439  
   440      def test_jwt_policy_override(
   441          self,
   442          kube_apis,
   443          crd_ingress_controller,
   444          v_s_route_app_setup,
   445          v_s_route_setup,
   446          test_namespace,
   447      ):
   448          """
   449              Test if first reference to a policy in the same context(subroute) takes precedence,
   450              i.e. in this case, policy without $httptoken over policy with $httptoken.
   451          """
   452          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   453          secret, pol_name_1, pol_name_2, headers = self.setup_multiple_policies(
   454              kube_apis,
   455              v_s_route_setup.route_m.namespace,
   456              valid_token,
   457              jwk_sec_valid_src,
   458              jwt_pol_valid_src,
   459              jwt_pol_multi_src,
   460              v_s_route_setup.vs_host,
   461          )
   462  
   463          print(f"Patch vsr with policies: {jwt_pol_valid_src}")
   464          patch_v_s_route_from_yaml(
   465              kube_apis.custom_objects,
   466              v_s_route_setup.route_m.name,
   467              jwt_vsr_override_src,
   468              v_s_route_setup.route_m.namespace,
   469          )
   470          wait_before_test()
   471  
   472          resp = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers,)
   473          print(resp.status_code)
   474  
   475          crd_info = read_custom_resource(
   476              kube_apis.custom_objects,
   477              v_s_route_setup.route_m.namespace,
   478              "virtualserverroutes",
   479              v_s_route_setup.route_m.name,
   480          )
   481          delete_policy(kube_apis.custom_objects, pol_name_1, v_s_route_setup.route_m.namespace)
   482          delete_policy(kube_apis.custom_objects, pol_name_2, v_s_route_setup.route_m.namespace)
   483          delete_secret(kube_apis.v1, secret, v_s_route_setup.route_m.namespace)
   484  
   485          patch_v_s_route_from_yaml(
   486              kube_apis.custom_objects,
   487              v_s_route_setup.route_m.name,
   488              std_vsr_src,
   489              v_s_route_setup.route_m.namespace,
   490          )
   491          assert resp.status_code == 401
   492          assert f"Authorization Required" in resp.text
   493          assert (
   494              f"Multiple jwt policies in the same context is not valid."
   495              in crd_info["status"]["message"]
   496          )
   497  
   498      @pytest.mark.parametrize("vs_src", [jwt_vs_override_route_src, jwt_vs_override_spec_src])
   499      def test_jwt_policy_override_vs_vsr(
   500          self,
   501          kube_apis,
   502          crd_ingress_controller,
   503          v_s_route_app_setup,
   504          v_s_route_setup,
   505          test_namespace,
   506          vs_src,
   507      ):
   508          """
   509              Test if policy specified in vsr:subroute (policy without $httptoken) takes preference over policy specified in:
   510              1. vs:spec (policy with $httptoken)
   511              2. vs:route (policy with $httptoken)
   512          """
   513          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   514          secret, pol_name_1, pol_name_2, headers = self.setup_multiple_policies(
   515              kube_apis,
   516              v_s_route_setup.route_m.namespace,
   517              valid_token,
   518              jwk_sec_valid_src,
   519              jwt_pol_valid_src,
   520              jwt_pol_multi_src,
   521              v_s_route_setup.vs_host,
   522          )
   523  
   524          print(f"Patch vsr with policies: {jwt_pol_valid_src}")
   525          patch_v_s_route_from_yaml(
   526              kube_apis.custom_objects,
   527              v_s_route_setup.route_m.name,
   528              jwt_vsr_valid_multi_src,
   529              v_s_route_setup.route_m.namespace,
   530          )
   531          patch_virtual_server_from_yaml(
   532              kube_apis.custom_objects, v_s_route_setup.vs_name, vs_src, v_s_route_setup.namespace,
   533          )
   534          wait_before_test()
   535  
   536          resp = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}", headers=headers,)
   537          print(resp.status_code)
   538  
   539          delete_policy(kube_apis.custom_objects, pol_name_1, v_s_route_setup.route_m.namespace)
   540          delete_policy(kube_apis.custom_objects, pol_name_2, v_s_route_setup.route_m.namespace)
   541          delete_secret(kube_apis.v1, secret, v_s_route_setup.route_m.namespace)
   542  
   543          patch_v_s_route_from_yaml(
   544              kube_apis.custom_objects,
   545              v_s_route_setup.route_m.name,
   546              std_vsr_src,
   547              v_s_route_setup.route_m.namespace,
   548          )
   549          patch_virtual_server_from_yaml(
   550              kube_apis.custom_objects, v_s_route_setup.vs_name, std_vs_src, v_s_route_setup.namespace
   551          )
   552          assert resp.status_code == 401
   553          assert f"Authorization Required" in resp.text