From e0eea8adab11724dc8011acb93e15a5c1292455f Mon Sep 17 00:00:00 2001 From: jm96441n Date: Tue, 1 Aug 2023 15:49:51 -0400 Subject: [PATCH 1/8] JWT auth basic acceptance test --- .../tests/api-gateway/api_gateway_test.go | 245 ++++++++++++++++++ .../api-gateways/jwt-auth/api-gateway.yaml | 37 +++ .../api-gateways/jwt-auth/gateway-policy.yaml | 24 ++ .../jwt-auth/httproute-no-auth.yaml | 19 ++ .../api-gateways/jwt-auth/httproute.yaml | 32 +++ .../api-gateways/jwt-auth/jwt-provider.yaml | 9 + .../jwt-auth/jwt-route-filter.yaml | 12 + .../api-gateways/jwt-auth/kustomization.yaml | 12 + 8 files changed, 390 insertions(+) create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-no-auth.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 721bbf2527..bfeebded19 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -288,6 +288,251 @@ func TestAPIGateway_Basic(t *testing.T) { } } +func TestAPIGateway_JWTAuth_Basic(t *testing.T) { + t.Skip() + ctx := suite.Environment().DefaultContext(t) + cfg := suite.Config() + helmValues := map[string]string{ + "connectInject.enabled": "true", + "global.acls.manageSystemACLs": "true", // acls must be enabled for JWT auth to take place + "global.tls.enabled": "true", + "global.logLevel": "trace", + } + + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + consulCluster.Create(t) + + // Override the default proxy config settings for this test + consulClient, _ := consulCluster.SetupConsulClient(t, true) + _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ + Kind: api.ProxyDefaults, + Name: api.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, nil) + require.NoError(t, err) + + logger.Log(t, "creating api-gateway resources") + out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/cases/api-gateways/jwt-auth") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/cases/api-gateways/jwt-auth") + }) + + // Create certificate secret, we do this separately since + // applying the secret will make an invalid certificate that breaks other tests + logger.Log(t, "creating certificate secret") + out, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/bases/api-gateway/certificate.yaml") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/bases/api-gateway/certificate.yaml") + }) + + // patch certificate with data + logger.Log(t, "patching certificate secret with generated data") + certificate := generateCertificate(t, nil, "gateway.test.local") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "secret", "certificate", "-p", fmt.Sprintf(`{"data":{"tls.crt":"%s","tls.key":"%s"}}`, base64.StdEncoding.EncodeToString(certificate.CertPEM), base64.StdEncoding.EncodeToString(certificate.PrivateKeyPEM)), "--type=merge") + + // We use the static-client pod so that we can make calls to the api gateway + // via kubectl exec without needing a route into the cluster from the test machine. + logger.Log(t, "creating static-client pod") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") + + k8s.RunKubectl(t, ctx.KubectlOptions(t), "wait", "--for=condition=available", "--timeout=5m", fmt.Sprintf("deploy/%s", "static-server")) + // Grab a kubernetes client so that we can verify binding + // behavior prior to issuing requests through the gateway. + k8sClient := ctx.ControllerRuntimeClient(t) + + // On startup, the controller can take upwards of 1m to perform + // leader election so we may need to wait a long time for + // the reconcile loop to run (hence the 1m timeout here). + var ( + gatewayAddress string + gatewayClass gwv1beta1.GatewayClass + httproute gwv1beta1.HTTPRoute + httprouteAuth gwv1beta1.HTTPRoute + ) + + counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) + require.NoError(r, err) + + // check our finalizers + require.Len(r, gateway.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, gateway.Finalizers[0]) + + // check our statuses + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("ConsulAccepted", "Accepted")) + require.Len(r, gateway.Status.Listeners, 4) + + require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + require.EqualValues(r, 1, gateway.Status.Listeners[1].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + + // check that we have an address to use + require.Len(r, gateway.Status.Addresses, 1) + // now we know we have an address, set it so we can use it + gatewayAddress = gateway.Status.Addresses[0].Value + + // gateway class checks + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway-class"}, &gatewayClass) + require.NoError(r, err) + + // check our finalizers + require.Len(r, gatewayClass.Finalizers, 1) + require.EqualValues(r, gatewayClassFinalizer, gatewayClass.Finalizers[0]) + + // http route checks + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route", Namespace: "default"}, &httproute) + require.NoError(r, err) + + // http route checks + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route-auth", Namespace: "default"}, &httprouteAuth) + require.NoError(r, err) + + // check our finalizers + require.Len(t, httproute.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, httproute.Finalizers[0]) + + // check parent status + require.Len(t, httproute.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) + require.EqualValues(r, "gateway", httproute.Status.Parents[0].ParentRef.Name) + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) + + // check our finalizers + require.Len(r, httprouteAuth.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, httprouteAuth.Finalizers[0]) + + // check parent status + require.Len(r, httprouteAuth.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, httprouteAuth.Status.Parents[0].ControllerName) + require.EqualValues(r, "gateway", httprouteAuth.Status.Parents[0].ParentRef.Name) + checkStatusCondition(r, httprouteAuth.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, httprouteAuth.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(r, httprouteAuth.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) + }) + + // check that the Consul entries were created + entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) + require.NoError(t, err) + gateway := entry.(*api.APIGatewayConfigEntry) + + entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) + require.NoError(t, err) + httpRoute := entry.(*api.HTTPRouteConfigEntry) + + entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route-auth", nil) + require.NoError(t, err) + httpRouteAuth := entry.(*api.HTTPRouteConfigEntry) + + // now check the gateway status conditions + checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) + + // and the route status conditions + checkConsulStatusCondition(t, httpRoute.Status.Conditions, trueConsulCondition("Bound", "Bound")) + checkConsulStatusCondition(t, httpRouteAuth.Status.Conditions, trueConsulCondition("Bound", "Bound")) + + // finally we check that we can actually route to the service(s) via the gateway + k8sOptions := ctx.KubectlOptions(t) + targetHTTPAddress := fmt.Sprintf("http://%s/v1", gatewayAddress) + targetHTTPAddressAdmin := fmt.Sprintf("http://%s:8080/admin", gatewayAddress) + targetHTTPAddressPet := fmt.Sprintf("http://%s:8080/pet", gatewayAddress) + // valid JWT token with role of "doctor" + doctorToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJQUzI1NiIsImtpZCI6IkMtRTFuQ2p3Z0JDLVB1R00yTzQ2N0ZSRGhLeDhBa1ZjdElTQWJvM3JpZXcifQ.eyJpc3MiOiJsb2NhbCIsInJvbGUiOiJkb2N0b3IifQ.FfgpzjMf8Evh6K-fJ1cLXklfIXOm-vojVbWlPPbGVFtzxZ9hxMxoyAY_G8i36SfGrpUlp-RJ6ohMvprMrEgyRgbenu7u5kkm5iGHW-zpMus4izXRxPELBcpWOGF105HIssT2NYRstXieNR8EVzvGfLdvR0GW8ttEERgseqGvuAfdb4-aNYsysGwUUHbsZjazA6H1rZmWqHdCLOJ2ZwFsIdckO9CadnkyTILpcPUmLYyUVJdtlLGOySb0GG8c_dPML_IR5jSXCSUZt6S2JBNBNBdqukrlqpA-fIaaWft0dbWVMhv8DqPC8znult8dKvLZ1qXeU0itsqqJUyE16ihJjw" + // valid JWT token with role of "pet" + petToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJQUzI1NiIsImtpZCI6IkMtRTFuQ2p3Z0JDLVB1R00yTzQ2N0ZSRGhLeDhBa1ZjdElTQWJvM3JpZXcifQ.eyJpc3MiOiJsb2NhbCIsInJvbGUiOiJwZXQifQ.l94rJayGGTMB426HwEw5ipSjaIHjm-UWDHiBAlB_Slmi814AxAfl_0AdRwSz67UDnkoygKbvPpR5xUB03JCXNshLZuKLegWsBeQg_OJYvZGmFagl5NglBFvH7Jbta4e1eQoAxZI6Xyy1jHbu7jFBjQPVnK8EaRvWoW8Pe8a8rp_5xhub0pomhvRF6Pm5kAS4cMnxvqpVc5Oo5nO7ws_SmoNnbt2Ok14k23Zx5E2EWmGStOfbgFsdbhVbepB2DMzqv1j8jvBbwa_OxCwc_7pEOthOOxRV6L3ZjgbRSB4GumlXAOCBYXD1cRLgrMSrWB1GkefAKu8PV0Ho1px6sI9Evg" + + // check that intentions keep our connection from happening + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddress) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressAdmin) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressAdmin) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressAdmin) + + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressPet) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressPet) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressPet) + + // Now we create the allow intention. + _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: "static-server", + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Action: api.IntentionActionAllow, + }, + }, + }, nil) + require.NoError(t, err) + + _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: "static-server-protected", + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Action: api.IntentionActionAllow, + }, + }, + }, nil) + require.NoError(t, err) + + // Test that we can make a call to the api gateway + logger.Log(t, "trying calls to api gateway http") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetHTTPAddress) + + // ensure that overrides -> route extension -> default by making a request to the admin route with a JWT that has an issuer of "local" and a "role" of "doctor" + // we can see that: + // * the "iss" verification in the gateway override takes precedence over the "iss" verification in the route filter + // * the "role" verification in the route extension takes precedence over the "role" verification in the gateway default + // should fail because we're missing JWT + logger.Log(t, "trying calls to api gateway /admin should fail without JWT token") + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressAdmin) + + // should fail because we use the token with the wrong role and correct issuer + logger.Log(t, "trying calls to api gateway /admin should fail with wrong role") + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressAdmin) + + // will succeed because we use the token with the correct role and the correct issuer + logger.Log(t, "trying calls to api gateway /admin should succeed with JWT token with correct role") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressAdmin) + + // ensure that overrides -> route extension -> default by making a request to the admin route with a JWT that has an issuer of "local" and a "role" of "pet" + // the route does not define + // we can see that: + // * the "iss" verification in the gateway override takes precedence over the "iss" verification in the route filter + // * the "role" verification in the route extension takes precedence over the "role" verification in the gateway default + // should fail because we're missing JWT + logger.Log(t, "trying calls to api gateway /pet should fail without JWT token") + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressPet) + + // should fail because we use the token with the wrong role and correct issuer + logger.Log(t, "trying calls to api gateway /pet should fail with wrong role") + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressPet) + + // will succeed because we use the token with the correct role and the correct issuer + logger.Log(t, "trying calls to api gateway /pet should succeed with JWT token with correct role") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressPet) +} + func checkStatusCondition(t require.TestingT, conditions []metav1.Condition, toCheck metav1.Condition) { for _, c := range conditions { if c.Type == toCheck.Type { diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml new file mode 100644 index 0000000000..bef7e96f12 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml @@ -0,0 +1,37 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway +spec: + gatewayClassName: gateway-class + listeners: + - protocol: HTTP + port: 8080 + name: http-auth + allowedRoutes: + namespaces: + from: "All" + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: "All" + - protocol: TCP + port: 81 + name: tcp + allowedRoutes: + namespaces: + from: "All" + - protocol: HTTPS + port: 443 + name: https + tls: + certificateRefs: + - name: "certificate" + allowedRoutes: + namespaces: + from: "All" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml new file mode 100644 index 0000000000..8e47d75062 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml @@ -0,0 +1,24 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ConsulGatewayPolicy +metadata: + name: my-policy +spec: + targetRef: + name: gateway + kind: Gateway + group: gateway.networking.kuberenetes.io + sectionName: http + override: + Providers: + - Provider: "local" + VerifyClaims: + - Path: + - "iss" + Value: "local" + default: + Providers: + - Provider: "local" + VerifyClaims: + - Path: + - "iss" + Value: "local" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-no-auth.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-no-auth.yaml new file mode 100644 index 0000000000..52e206a91e --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-no-auth.yaml @@ -0,0 +1,19 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: http-route +spec: + parentRefs: + - name: gateway + sectionName: http + rules: + - matches: + - path: + type: PathPrefix + value: "/v1" + backendRefs: + - name: static-server + port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml new file mode 100644 index 0000000000..11029edbe9 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml @@ -0,0 +1,32 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: http-route-auth +spec: + parentRefs: + - name: gateway + sectionName: http-auth + rules: + - matches: + - path: + type: PathPrefix + value: "/admin" + backendRefs: + - name: static-server + port: 80 + filters: + - type: ExtensionRef + extensionRef: + group: consul.hashicorp.com + kind: HTTPAuthFilter + name: route-jwt-auth-filter + - matches: + - path: + type: PathPrefix + value: "/pet" + backendRefs: + - name: static-server + port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml new file mode 100644 index 0000000000..37eb034d3c --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml @@ -0,0 +1,9 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: JWTProvider +metadata: + name: local +spec: + issuer: local + jsonWebKeySet: + local: + jwks: "ewogICAgImtleXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAicCI6ICI5TTlWSVhJR0hpR3FlTnhseEJ2V0xFV09oUFh3dXhXZUpod01uM3dGdG9STEtfZmF6VWxjWEc1cUViLTdpMXo3VmlPUWVZRnh6WUZYTS1pbVU3OVFRa1dTVUVSazR2dHZuc2R5UnpUSnVPc3A0ZUhuWFVMSHJPOU51NkJ5bC1VeVprMzFvSnFGeGllM0pHQXlRLUM2OVF2NVFkVjFZV0hfVDkyTzk4d1hYZGMiLAogICAgICAgICAgICAia3R5IjogIlJTQSIsCiAgICAgICAgICAgICJxIjogInFIVnZBb3h0ckgxUTVza25veXNMMkhvbC1ubnU3ZlM3Mjg4clRGdE9jeG9Jb29nWXBKVTljemxwcjctSlo2bjc0TUViVHBBMHRkSUR5TEtQQ0xIN3JKTFRrZzBDZVZNQWpmY01zdkRUcWdFOHNBWE42bzd2ZjYya2hwcExYOHVCU3JxSHkyV1JhZXJsbDROU09hcmRGSkQ2MWhHSVF2cEpXRk4xazFTV3pWcyIsCiAgICAgICAgICAgICJkIjogIlp3elJsVklRZkg5ekZ6d1hOZ2hEMHhkZVctalBCbmRkWnJNZ0wwQ2JjeXZZYlg2X1c0ajlhM1dmYWpobmI2bTFILW9CWjRMczVmNXNRVTB2ZFJ2ZG1laFItUG43aWNRcUdURFNKUTYtdWVtNm15UVRWaEo2UmZiM0lINVJ2VDJTOXUzcVFDZWFadWN3aXFoZ1RCbFhnOWFfV0pwVHJYNFhPQ3JCR1ZsTng3Z2JETVJOamNEN0FnRkZ3S2p2TEZVdDRLTkZmdEJqaFF0TDFLQ2VwblNmamtvRm1RUTVlX3RSS2ozX2U1V3pNSkJkekpQejNkR2YxZEk3OF9wYmJFbmFMcWhqNWg0WUx2UU5JUUhVcURYSGx4ZDc1Qlh3aFJReE1nUDRfd1EwTFk2cVRKNGFDa2Q0RDJBTUtqMzJqeVFiVTRKTE9jQjFNMnZBRWFyc2NTU3l0USIsCiAgICAgICAgICAgICJlIjogIkFRQUIiLAogICAgICAgICAgICAidXNlIjogInNpZyIsCiAgICAgICAgICAgICJraWQiOiAiQy1FMW5DandnQkMtUHVHTTJPNDY3RlJEaEt4OEFrVmN0SVNBYm8zcmlldyIsCiAgICAgICAgICAgICJxaSI6ICJ0N2VOQjhQV21xVHdKREZLQlZKZExrZnJJT2drMFJ4MnREODBGNHB5cjhmNzRuNGlVWXFmWG1haVZtbGx2c2FlT3JlNHlIczQ4UE45NVZsZlVvS3Z6ZEJFaDNZTDFINGZTOGlYYXNzNGJiVnVuWHR4U0hMZFFPYUNZYUplSmhBbGMyUWQ4elR0NFFQWk9yRWVWLVJTYU0tN095ekkwUWtSSF9tcmk1YmRrOXMiLAogICAgICAgICAgICAiZHAiOiAiYnBLckQtVXhrRENDal81MFZLU0NFeE1Ec1Zob2VBZm1tNjMxb1o5aDhUTkZ4TUU1YVptbUJ2VzBJUG9wMm1PUF9qTW9FVWxfUG1RYUlBOEgtVEdqTFp2QTMxSlZBeFN3TU5aQzdwaVFPRjYzVnhneTZUTzlmb1hENVdndC1oLUNxU1N6T2V3eFdmUWNTMmpMcTA3NUFxOTYwTnA2SHhjbE8weUdRN1JDSlpjIiwKICAgICAgICAgICAgImFsZyI6ICJQUzI1NiIsCiAgICAgICAgICAgICJkcSI6ICJpdVZveGwwckFKSEM1c2JzbTZpZWQ3c2ZIVXIwS2Rja0hiVFBLb0lPU1BFcU5YaXBlT3BrWkdEdU55NWlDTXNyRnNHaDFrRW9kTkhZdE40ay1USm5KSDliV296SGdXbGloNnN2R1V0Zi1raFMxWC16ckxaMTJudzlyNDRBbjllWG54bjFaVXMxZm5OakltM3dtZ083algyTWxIeVlNVUZVd0RMd09xNEFPUWsiLAogICAgICAgICAgICAibiI6ICJvUmhjeUREdmp3NFZ4SHRRNTZhRDlNSmRTaWhWSk1nTHd1b2FCQVhhc0RjVDNEWVZjcENlVGxDMVBPdzdPNW1Ec2ZSWVFtcGpoendyRDVZWU8yeDE4REl4czdyNTNJdFMxRy1ybnQxQ1diVE9fUzFJT01DR2xxYzh5VWJnLUhSUkRETXQyb2V3TjJoRGtxYlBKVFJNbXpjRkpNMHRpTm1RZVVMcWViZEVYaWVUblJMT1BkMWg2ZmJycVNLS01mSXlIbGZ1WXFQc1VWSEdkMVBESGljZ3NMazFtZDhtYTNIS1hWM0hJdzZrdUV6R0hQb1gxNHo4YWF6RFFZWndUR3ZxVGlPLUdRUlVDZUJueVo4bVhyWnRmSjNqVk83UUhXcEx3MlM1VDVwVTRwcE0xQXppWTFxUDVfY3ZpOTNZT2Zrb09PalRTX3V3RENZWGFxWjB5bTJHYlEiCiAgICAgICAgfQogICAgXQp9Cg==" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml new file mode 100644 index 0000000000..1b9833d70c --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml @@ -0,0 +1,12 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: HTTPAuthFilter +metadata: + name: example-route-jwt-filter +spec: + type: JWT + JWTProviders: + - Provider: "local" + VerifyClaims: + - Path: + - "role" + Value: "doctor" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml new file mode 100644 index 0000000000..3dc38a090c --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml @@ -0,0 +1,12 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../bases/api-gateway + - ../../static-server-inject + - ./httproute.yaml + - ./jwt-provider.yaml + +patchesStrategicMerge: + - httproute-no-auth.yaml + - api-gateway.yaml From e6e3d4bda899bdf73fee4118ee83cdde4cfc75af Mon Sep 17 00:00:00 2001 From: jm96441n Date: Wed, 2 Aug 2023 10:35:37 -0400 Subject: [PATCH 2/8] Update to run only in enterprise mode, update comment to be correct --- .../tests/api-gateway/api_gateway_test.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index bfeebded19..c14209f2f2 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -126,7 +126,7 @@ func TestAPIGateway_Basic(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 1m timeout here). + // the reconcile loop to run (hence the 2m timeout here). var gatewayAddress string counter := &retry.Counter{Count: 120, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { @@ -292,11 +292,17 @@ func TestAPIGateway_JWTAuth_Basic(t *testing.T) { t.Skip() ctx := suite.Environment().DefaultContext(t) cfg := suite.Config() + + if !cfg.EnableEnterprise { + t.Skipf("skipping this test because -enable-enterprise is not set") + } + helmValues := map[string]string{ - "connectInject.enabled": "true", - "global.acls.manageSystemACLs": "true", // acls must be enabled for JWT auth to take place - "global.tls.enabled": "true", - "global.logLevel": "trace", + "connectInject.enabled": "true", + "connectInject.consulNamespaces.mirroringK8S": "true", + "global.acls.manageSystemACLs": "true", // acls must be enabled for JWT auth to take place + "global.tls.enabled": "true", + "global.logLevel": "trace", } releaseName := helpers.RandomName() @@ -352,7 +358,7 @@ func TestAPIGateway_JWTAuth_Basic(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 1m timeout here). + // the reconcile loop to run (hence the 2m timeout here). var ( gatewayAddress string gatewayClass gwv1beta1.GatewayClass From ef7135412f25a35c45004c803f295f75ee6e7ca8 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Wed, 2 Aug 2023 14:20:50 -0400 Subject: [PATCH 3/8] Remove usage of `testing.t` in retry block --- acceptance/tests/api-gateway/api_gateway_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index c14209f2f2..635a905168 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -412,7 +412,7 @@ func TestAPIGateway_JWTAuth_Basic(t *testing.T) { require.NoError(r, err) // check our finalizers - require.Len(t, httproute.Finalizers, 1) + require.Len(r, httproute.Finalizers, 1) require.EqualValues(r, gatewayFinalizer, httproute.Finalizers[0]) // check parent status From 047c21f190d383bd2a46e429f0e46164221798d7 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Fri, 4 Aug 2023 12:44:44 -0400 Subject: [PATCH 4/8] Fixed last `t` in retry block in tests --- acceptance/tests/api-gateway/api_gateway_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 635a905168..6d7909a823 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -416,7 +416,7 @@ func TestAPIGateway_JWTAuth_Basic(t *testing.T) { require.EqualValues(r, gatewayFinalizer, httproute.Finalizers[0]) // check parent status - require.Len(t, httproute.Status.Parents, 1) + require.Len(r, httproute.Status.Parents, 1) require.EqualValues(r, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) require.EqualValues(r, "gateway", httproute.Status.Parents[0].ParentRef.Name) checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) From fd3d2df92b2d3f7d67165436c14536778c044e82 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Wed, 9 Aug 2023 10:34:26 -0400 Subject: [PATCH 5/8] Update acceptance/tests/api-gateway/api_gateway_test.go Co-authored-by: Nathan Coleman --- acceptance/tests/api-gateway/api_gateway_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 6d7909a823..4c4b59a924 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -362,8 +362,8 @@ func TestAPIGateway_JWTAuth_Basic(t *testing.T) { var ( gatewayAddress string gatewayClass gwv1beta1.GatewayClass - httproute gwv1beta1.HTTPRoute - httprouteAuth gwv1beta1.HTTPRoute + httpRoute gwv1beta1.HTTPRoute + httpRouteAuth gwv1beta1.HTTPRoute ) counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} From d041e8d526c76953fe656a82637970fe08183258 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Wed, 9 Aug 2023 10:34:35 -0400 Subject: [PATCH 6/8] Update acceptance/tests/api-gateway/api_gateway_test.go Co-authored-by: Nathan Coleman --- acceptance/tests/api-gateway/api_gateway_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 4c4b59a924..2c15982584 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -126,7 +126,7 @@ func TestAPIGateway_Basic(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 2m timeout here). + // the reconcile loop to run (hence the timeout here). var gatewayAddress string counter := &retry.Counter{Count: 120, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { From 12c56793106528bcd0e8b4cef71380c46c33c8c6 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Wed, 9 Aug 2023 11:17:03 -0400 Subject: [PATCH 7/8] Updating filenames for gw jwt cases and adding message about why this test is skipped --- .../tests/api-gateway/api_gateway_test.go | 2 +- .../api-gateways/jwt-auth/httproute-auth.yaml | 32 +++++++++++++++++++ .../jwt-auth/httproute-no-auth.yaml | 19 ----------- .../api-gateways/jwt-auth/httproute.yaml | 23 +++---------- .../jwt-auth/jwt-route-filter.yaml | 2 +- 5 files changed, 39 insertions(+), 39 deletions(-) create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-no-auth.yaml diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 2c15982584..5a69d6ede6 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -289,7 +289,7 @@ func TestAPIGateway_Basic(t *testing.T) { } func TestAPIGateway_JWTAuth_Basic(t *testing.T) { - t.Skip() + t.Skip("skipping this test until GW JWT auth is complete") ctx := suite.Environment().DefaultContext(t) cfg := suite.Config() diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml new file mode 100644 index 0000000000..4963277c55 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml @@ -0,0 +1,32 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: http-route-auth +spec: + parentRefs: + - name: gateway + sectionName: http-auth + rules: + - matches: + - path: + type: PathPrefix + value: "/admin" + backendRefs: + - name: static-server + port: 80 + filters: + - type: ExtensionRef + extensionRef: + group: consul.hashicorp.com + kind: HTTPRouteAuthFilter + name: route-jwt-auth-filter + - matches: + - path: + type: PathPrefix + value: "/pet" + backendRefs: + - name: static-server + port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-no-auth.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-no-auth.yaml deleted file mode 100644 index 52e206a91e..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-no-auth.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: http-route -spec: - parentRefs: - - name: gateway - sectionName: http - rules: - - matches: - - path: - type: PathPrefix - value: "/v1" - backendRefs: - - name: static-server - port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml index 11029edbe9..52e206a91e 100644 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml @@ -4,29 +4,16 @@ apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute metadata: - name: http-route-auth + name: http-route spec: parentRefs: - name: gateway - sectionName: http-auth + sectionName: http rules: - matches: - path: type: PathPrefix - value: "/admin" + value: "/v1" backendRefs: - - name: static-server - port: 80 - filters: - - type: ExtensionRef - extensionRef: - group: consul.hashicorp.com - kind: HTTPAuthFilter - name: route-jwt-auth-filter - - matches: - - path: - type: PathPrefix - value: "/pet" - backendRefs: - - name: static-server - port: 80 + - name: static-server + port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml index 1b9833d70c..e0a3128bed 100644 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml @@ -1,5 +1,5 @@ apiVersion: consul.hashicorp.com/v1alpha1 -kind: HTTPAuthFilter +kind: HTTPRouteAuthFilter metadata: name: example-route-jwt-filter spec: From 8a4ca4b1de6120e5819287c9a4d4ae4df86c063e Mon Sep 17 00:00:00 2001 From: jm96441n Date: Wed, 9 Aug 2023 13:36:42 -0400 Subject: [PATCH 8/8] renamed variables to be more readable --- .../tests/api-gateway/api_gateway_test.go | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 5a69d6ede6..df4a097b7e 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -404,36 +404,36 @@ func TestAPIGateway_JWTAuth_Basic(t *testing.T) { require.EqualValues(r, gatewayClassFinalizer, gatewayClass.Finalizers[0]) // http route checks - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route", Namespace: "default"}, &httproute) + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route", Namespace: "default"}, &httpRoute) require.NoError(r, err) // http route checks - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route-auth", Namespace: "default"}, &httprouteAuth) + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route-auth", Namespace: "default"}, &httpRouteAuth) require.NoError(r, err) // check our finalizers - require.Len(r, httproute.Finalizers, 1) - require.EqualValues(r, gatewayFinalizer, httproute.Finalizers[0]) + require.Len(r, httpRoute.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, httpRoute.Finalizers[0]) // check parent status - require.Len(r, httproute.Status.Parents, 1) - require.EqualValues(r, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) - require.EqualValues(r, "gateway", httproute.Status.Parents[0].ParentRef.Name) - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) + require.Len(r, httpRoute.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, httpRoute.Status.Parents[0].ControllerName) + require.EqualValues(r, "gateway", httpRoute.Status.Parents[0].ParentRef.Name) + checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) // check our finalizers - require.Len(r, httprouteAuth.Finalizers, 1) - require.EqualValues(r, gatewayFinalizer, httprouteAuth.Finalizers[0]) + require.Len(r, httpRouteAuth.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, httpRouteAuth.Finalizers[0]) // check parent status - require.Len(r, httprouteAuth.Status.Parents, 1) - require.EqualValues(r, gatewayClassControllerName, httprouteAuth.Status.Parents[0].ControllerName) - require.EqualValues(r, "gateway", httprouteAuth.Status.Parents[0].ParentRef.Name) - checkStatusCondition(r, httprouteAuth.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, httprouteAuth.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - checkStatusCondition(r, httprouteAuth.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) + require.Len(r, httpRouteAuth.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, httpRouteAuth.Status.Parents[0].ControllerName) + require.EqualValues(r, "gateway", httpRouteAuth.Status.Parents[0].ParentRef.Name) + checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) }) // check that the Consul entries were created @@ -443,18 +443,18 @@ func TestAPIGateway_JWTAuth_Basic(t *testing.T) { entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) require.NoError(t, err) - httpRoute := entry.(*api.HTTPRouteConfigEntry) + consulHTTPRoute := entry.(*api.HTTPRouteConfigEntry) entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route-auth", nil) require.NoError(t, err) - httpRouteAuth := entry.(*api.HTTPRouteConfigEntry) + consulHTTPRouteAuth := entry.(*api.HTTPRouteConfigEntry) // now check the gateway status conditions checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) // and the route status conditions - checkConsulStatusCondition(t, httpRoute.Status.Conditions, trueConsulCondition("Bound", "Bound")) - checkConsulStatusCondition(t, httpRouteAuth.Status.Conditions, trueConsulCondition("Bound", "Bound")) + checkConsulStatusCondition(t, consulHTTPRoute.Status.Conditions, trueConsulCondition("Bound", "Bound")) + checkConsulStatusCondition(t, consulHTTPRouteAuth.Status.Conditions, trueConsulCondition("Bound", "Bound")) // finally we check that we can actually route to the service(s) via the gateway k8sOptions := ctx.KubectlOptions(t)