From 44bcbf894071e0c3aa3cbb0d5fbed5da466cd2e6 Mon Sep 17 00:00:00 2001 From: Venktesh Shivam Patel Date: Fri, 3 Feb 2023 14:21:44 +0000 Subject: [PATCH] Add tests for jwt jwksuri (#3511) * Add tests for jwt jwksuri * fix jwks docs --- .github/actions/smoke-tests/action.yaml | 4 + .github/workflows/ci.yml | 1 + examples/custom-resources/jwks/README.md | 2 +- tests/conftest.py | 6 + .../configmap/nginx-config.yaml | 9 ++ .../policies/jwt-policy-valid.yaml | 10 ++ .../virtual-server-policy-route.yaml | 22 ++++ .../virtual-server-policy-spec.yaml | 22 ++++ tests/suite/test_jwt_policies_jwksuri.py | 118 ++++++++++++++++++ 9 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 tests/data/jwt-policy-jwksuri/configmap/nginx-config.yaml create mode 100644 tests/data/jwt-policy-jwksuri/policies/jwt-policy-valid.yaml create mode 100644 tests/data/jwt-policy-jwksuri/virtual-server/virtual-server-policy-route.yaml create mode 100644 tests/data/jwt-policy-jwksuri/virtual-server/virtual-server-policy-spec.yaml create mode 100644 tests/suite/test_jwt_policies_jwksuri.py diff --git a/.github/actions/smoke-tests/action.yaml b/.github/actions/smoke-tests/action.yaml index 6c8a66e939..5934a53e69 100644 --- a/.github/actions/smoke-tests/action.yaml +++ b/.github/actions/smoke-tests/action.yaml @@ -23,6 +23,9 @@ inputs: nginx-crt: description: Nginx cert to use required: false + azure-ad-secret: + description: Azure Active Directory secret for JWKs + required: false outputs: test-results-name: @@ -113,6 +116,7 @@ runs: --self-contained-html \ --durations=10 \ --show-ic-logs=yes \ + --ad-secret=${{ inputs.azure-ad-secret }} \ -m ${{ inputs.marker != '' && inputs.marker || '""' }} working-directory: ./tests shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef4326bb06..874b364bb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -318,6 +318,7 @@ jobs: k8s-version: ${{ matrix.k8s }} nginx-crt: ${{ contains(matrix.images.image, 'nap') && secrets.NGINX_AP_CRT || secrets.NGINX_CRT }} nginx-key: ${{ contains(matrix.images.image, 'nap') && secrets.NGINX_AP_KEY || secrets.NGINX_KEY }} + azure-ad-secret: ${{ secrets.AZURE_AD_AUTOMATION }} - name: Upload Test Results uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: diff --git a/examples/custom-resources/jwks/README.md b/examples/custom-resources/jwks/README.md index 906a00736f..afd8e20587 100644 --- a/examples/custom-resources/jwks/README.md +++ b/examples/custom-resources/jwks/README.md @@ -21,7 +21,7 @@ In this example, KeyCloak is deployed as a single container for the purpose of e ## Step 1 - Deploy a TLS Secret -Create a secret with the TLS certificate and key that will be used for TLS termination of the web application and Keycloak: +Create a secret with the TLS certificate and key that will be used for TLS termination of the Keycloak application: ``` $ kubectl apply -f tls-secret.yaml ``` diff --git a/tests/conftest.py b/tests/conftest.py index 10c5fea530..403a39806b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -114,6 +114,12 @@ def pytest_addoption(parser) -> None: default=NS_COUNT, help="Number for namespaces to deploy for use in test_multiple_ns_perf.py", ) + parser.addoption( + "--ad-secret", + action="store", + default=os.environ.get("AZURE_AD_AUTOMATION"), + help="Azure active directory secret for JWKs", + ) # import fixtures into pytest global namespace diff --git a/tests/data/jwt-policy-jwksuri/configmap/nginx-config.yaml b/tests/data/jwt-policy-jwksuri/configmap/nginx-config.yaml new file mode 100644 index 0000000000..87f1665bfa --- /dev/null +++ b/tests/data/jwt-policy-jwksuri/configmap/nginx-config.yaml @@ -0,0 +1,9 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: nginx-config + namespace: nginx-ingress +data: + resolver-addresses: "kube-dns.kube-system.svc.cluster.local" + http-snippets: | + subrequest_output_buffer_size 64k; diff --git a/tests/data/jwt-policy-jwksuri/policies/jwt-policy-valid.yaml b/tests/data/jwt-policy-jwksuri/policies/jwt-policy-valid.yaml new file mode 100644 index 0000000000..a4c29a0a60 --- /dev/null +++ b/tests/data/jwt-policy-jwksuri/policies/jwt-policy-valid.yaml @@ -0,0 +1,10 @@ +apiVersion: k8s.nginx.org/v1 +kind: Policy +metadata: + name: jwt-policy-valid +spec: + jwt: + realm: MyProductAPI + token: $http_token + jwksURI: https://login.microsoftonline.com/dd3dfd2f-6a3b-40d1-9be0-bf8327d81c50/discovery/v2.0/keys + keyCache: 1h diff --git a/tests/data/jwt-policy-jwksuri/virtual-server/virtual-server-policy-route.yaml b/tests/data/jwt-policy-jwksuri/virtual-server/virtual-server-policy-route.yaml new file mode 100644 index 0000000000..aa0cb6f44c --- /dev/null +++ b/tests/data/jwt-policy-jwksuri/virtual-server/virtual-server-policy-route.yaml @@ -0,0 +1,22 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: virtual-server +spec: + host: virtual-server.example.com + upstreams: + - name: backend2 + service: backend2-svc + port: 80 + - name: backend1 + service: backend1-svc + port: 80 + routes: + - path: "/backend1" + action: + pass: backend1 + policies: + - name: jwt-policy-valid + - path: "/backend2" + action: + pass: backend2 diff --git a/tests/data/jwt-policy-jwksuri/virtual-server/virtual-server-policy-spec.yaml b/tests/data/jwt-policy-jwksuri/virtual-server/virtual-server-policy-spec.yaml new file mode 100644 index 0000000000..f45335ec7f --- /dev/null +++ b/tests/data/jwt-policy-jwksuri/virtual-server/virtual-server-policy-spec.yaml @@ -0,0 +1,22 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: virtual-server +spec: + host: virtual-server.example.com + policies: + - name: jwt-policy-valid + upstreams: + - name: backend2 + service: backend2-svc + port: 80 + - name: backend1 + service: backend1-svc + port: 80 + routes: + - path: "/backend1" + action: + pass: backend1 + - path: "/backend2" + action: + pass: backend2 diff --git a/tests/suite/test_jwt_policies_jwksuri.py b/tests/suite/test_jwt_policies_jwksuri.py new file mode 100644 index 0000000000..a30d2e7fa8 --- /dev/null +++ b/tests/suite/test_jwt_policies_jwksuri.py @@ -0,0 +1,118 @@ +import time +from unittest import mock + +import pytest +import requests +from settings import TEST_DATA +from suite.utils.policy_resources_utils import create_policy_from_yaml, delete_policy +from suite.utils.resources_utils import replace_configmap_from_yaml, wait_before_test +from suite.utils.vs_vsr_resources_utils import delete_and_create_vs_from_yaml, patch_v_s_route_from_yaml + +std_vs_src = f"{TEST_DATA}/virtual-server/standard/virtual-server.yaml" +jwt_pol_valid_src = f"{TEST_DATA}/jwt-policy-jwksuri/policies/jwt-policy-valid.yaml" +jwt_vs_spec_src = f"{TEST_DATA}/jwt-policy-jwksuri/virtual-server/virtual-server-policy-spec.yaml" +jwt_vs_route_src = f"{TEST_DATA}/jwt-policy-jwksuri/virtual-server/virtual-server-policy-route.yaml" +jwt_cm_src = f"{TEST_DATA}/jwt-policy-jwksuri/configmap/nginx-config.yaml" +ad_tenant = "dd3dfd2f-6a3b-40d1-9be0-bf8327d81c50" +client_id = "8a172a83-a630-41a4-9ca6-1e5ef03cd7e7" + + +def get_token(request): + """ + get jwt token from azure ad endpoint + """ + data = { + "client_id": f"{client_id}", + "scope": ".default", + "client_secret": request.config.getoption("--ad-secret"), + "grant_type": "client_credentials", + } + ad_response = requests.post(f"https://login.microsoftonline.com/{ad_tenant}/oauth2/token", data=data) + + if ad_response.status_code == 200: + return ad_response.json()["access_token"] + else: + pytest.fail("Unable to request Azure token endpoint") + + +@pytest.mark.skip_for_nginx_oss +@pytest.mark.policies +@pytest.mark.parametrize( + "crd_ingress_controller, virtual_server_setup", + [ + ( + { + "type": "complete", + "extra_args": [ + f"-enable-custom-resources", + f"-enable-leader-election=false", + ], + }, + { + "example": "virtual-server", + "app_type": "simple", + }, + ) + ], + indirect=True, +) +class TestJWTPoliciesVsJwksuri: + @pytest.mark.parametrize("jwt_virtual_server", [jwt_vs_spec_src, jwt_vs_route_src]) + def test_jwt_policy_jwksuri( + self, + request, + kube_apis, + ingress_controller_prerequisites, + crd_ingress_controller, + virtual_server_setup, + test_namespace, + jwt_virtual_server, + ): + """ + Test jwt-policy in Virtual Server (spec and route) with keys fetched form Azure + """ + replace_configmap_from_yaml( + kube_apis.v1, + ingress_controller_prerequisites.config_map["metadata"]["name"], + ingress_controller_prerequisites.namespace, + jwt_cm_src, + ) + pol_name = create_policy_from_yaml(kube_apis.custom_objects, jwt_pol_valid_src, test_namespace) + wait_before_test() + + print(f"Patch vs with policy: {jwt_virtual_server}") + delete_and_create_vs_from_yaml( + kube_apis.custom_objects, + virtual_server_setup.vs_name, + jwt_virtual_server, + virtual_server_setup.namespace, + ) + resp1 = mock.Mock() + resp1.status_code == 502 + counter = 0 + + while resp1.status_code != 401 and counter < 3: + resp1 = requests.get( + virtual_server_setup.backend_1_url, + headers={"host": virtual_server_setup.vs_host}, + ) + wait_before_test() + counter = +1 + + token = get_token(request) + + resp2 = requests.get( + virtual_server_setup.backend_1_url, + headers={"host": virtual_server_setup.vs_host, "token": token}, + ) + + delete_policy(kube_apis.custom_objects, pol_name, test_namespace) + delete_and_create_vs_from_yaml( + kube_apis.custom_objects, + virtual_server_setup.vs_name, + std_vs_src, + virtual_server_setup.namespace, + ) + + assert resp1.status_code == 401 and f"Authorization Required" in resp1.text + assert resp2.status_code == 200 and f"Request ID:" in resp2.text