diff --git a/pkg/app/sleep.go b/pkg/app/sleep.go index 01447bd6..3c678bc2 100644 --- a/pkg/app/sleep.go +++ b/pkg/app/sleep.go @@ -22,7 +22,8 @@ import ( "github.com/maistra/maistra-test-tool/pkg/util/oc" "github.com/maistra/maistra-test-tool/pkg/util/pod" "github.com/maistra/maistra-test-tool/pkg/util/retry" - "github.com/maistra/maistra-test-tool/pkg/util/test" + + . "github.com/maistra/maistra-test-tool/pkg/util/test" ) type sleep struct { @@ -66,17 +67,17 @@ func (a *sleep) Namespace() string { return a.ns } -func (a *sleep) Install(t test.TestHelper) { +func (a *sleep) Install(t TestHelper) { t.T().Helper() oc.ApplyTemplate(t, a.ns, sleepTemplate, a.values(t)) } -func (a *sleep) Uninstall(t test.TestHelper) { +func (a *sleep) Uninstall(t TestHelper) { t.T().Helper() oc.DeleteFromTemplate(t, a.ns, sleepTemplate, a.values(t)) } -func (a *sleep) values(t test.TestHelper) map[string]interface{} { +func (a *sleep) values(t TestHelper) map[string]interface{} { proxy := oc.GetProxy(t) return map[string]interface{}{ "InjectSidecar": a.injectSidecar, @@ -90,7 +91,7 @@ func (a *sleep) values(t test.TestHelper) map[string]interface{} { } } -func (a *sleep) WaitReady(t test.TestHelper) { +func (a *sleep) WaitReady(t TestHelper) { t.T().Helper() oc.WaitDeploymentRolloutComplete(t, a.ns, "sleep") } @@ -101,35 +102,35 @@ type CurlOpts struct { Options []string } -func ExecInSleepPod(t test.TestHelper, ns string, command string, checks ...common.CheckFunc) { +func ExecInSleepPod(t TestHelper, ns string, command string, checks ...common.CheckFunc) { t.T().Helper() - retry.UntilSuccess(t, func(t test.TestHelper) { + retry.UntilSuccessWithOptions(t, retry.Options().MaxAttempts(10), func(t TestHelper) { t.T().Helper() oc.Exec(t, pod.MatchingSelector("app=sleep", ns), "sleep", command, checks...) }) } -func AssertSleepPodRequestSuccess(t test.TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { +func AssertSleepPodRequestSuccess(t TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { assertSleepPodRequestResponse(t, sleepNamespace, url, "200", opts...) } -func AssertSleepPodRequestFailure(t test.TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { +func AssertSleepPodRequestFailure(t TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { assertSleepPodRequestResponse(t, sleepNamespace, url, curlFailedMessage, opts...) } -func AssertSleepPodRequestForbidden(t test.TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { +func AssertSleepPodRequestForbidden(t TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { assertSleepPodRequestResponse(t, sleepNamespace, url, "403", opts...) } -func AssertSleepPodRequestUnauthorized(t test.TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { +func AssertSleepPodRequestUnauthorized(t TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { assertSleepPodRequestResponse(t, sleepNamespace, url, "401", opts...) } -func AssertSleepPodZeroesPlaceholder(t test.TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { +func AssertSleepPodZeroesPlaceholder(t TestHelper, sleepNamespace string, url string, opts ...CurlOpts) { assertSleepPodRequestResponse(t, sleepNamespace, url, "000", opts...) } -func assertSleepPodRequestResponse(t test.TestHelper, sleepNamespace, url, expected string, opts ...CurlOpts) { +func assertSleepPodRequestResponse(t TestHelper, sleepNamespace, url, expected string, opts ...CurlOpts) { command := buildCurlCmd(url, opts...) ExecInSleepPod(t, sleepNamespace, command, assert.OutputContains(expected, @@ -162,7 +163,7 @@ func buildCurlCmd(url string, opts ...CurlOpts) string { } } - return fmt.Sprintf(`curl -sS %s%s -X %s -o /dev/null -w "%%{http_code}" %s 2>/dev/null || echo %s`, options, headers, method, url, curlFailedMessage) + return fmt.Sprintf(`curl -sS%s%s -X %s -o /dev/null -w "%%{http_code}" %s 2>/dev/null || echo %s`, headers, options, method, url, curlFailedMessage) } const curlFailedMessage = "CURL_FAILED" diff --git a/pkg/tests/tasks/security/authorization/ext_auth_test.go b/pkg/tests/tasks/security/authorization/ext_auth_test.go index 9a7c8823..e8b34a25 100644 --- a/pkg/tests/tasks/security/authorization/ext_auth_test.go +++ b/pkg/tests/tasks/security/authorization/ext_auth_test.go @@ -15,43 +15,44 @@ package authorization import ( + "fmt" "testing" "github.com/maistra/maistra-test-tool/pkg/app" "github.com/maistra/maistra-test-tool/pkg/tests/ossm" "github.com/maistra/maistra-test-tool/pkg/util/env" + "github.com/maistra/maistra-test-tool/pkg/util/ns" "github.com/maistra/maistra-test-tool/pkg/util/oc" - "github.com/maistra/maistra-test-tool/pkg/util/test" "github.com/maistra/maistra-test-tool/pkg/util/version" + + . "github.com/maistra/maistra-test-tool/pkg/util/test" ) func TestEnvoyExtAuthzHttpExtensionProvider(t *testing.T) { - test.NewTest(t).Id("T37").Groups(test.Full, test.InterOp, test.ARM).Run(func(t test.TestHelper) { + NewTest(t).Id("T37").Groups(Full, InterOp, ARM).Run(func(t TestHelper) { if env.GetSMCPVersion().LessThan(version.SMCP_2_3) { t.Skip("extensionProviders.envoyExtAuthzHttp was added in v2.3") } t.Log("This test validates authorization policies with a JWT Token") - ns := "foo" - ossm.DeployControlPlane(t) t.LogStep("Install httpbin and sleep") - app.InstallAndWaitReady(t, app.Httpbin(ns), app.Sleep(ns)) + app.InstallAndWaitReady(t, app.Httpbin(ns.Foo), app.Sleep(ns.Foo)) t.Cleanup(func() { - app.Uninstall(t, app.Httpbin(ns), app.Sleep(ns)) + app.Uninstall(t, app.Httpbin(ns.Foo), app.Sleep(ns.Foo)) }) t.LogStep("Check if httpbin returns 200 OK when no authorization policies are in place") - app.AssertSleepPodRequestSuccess(t, ns, "http://httpbin:8000/ip") + app.AssertSleepPodRequestSuccess(t, ns.Foo, "http://httpbin:8000/ip") t.LogStep("Deploy the External Authorizer and Verify the sample external authorizer is up and running") - oc.ApplyTemplate(t, ns, ExternalAuthzService, nil) + oc.ApplyTemplate(t, ns.Foo, ExternalAuthzService, nil) t.Cleanup(func() { - oc.DeleteFromTemplate(t, ns, ExternalAuthzService, nil) + oc.DeleteFromTemplate(t, ns.Foo, ExternalAuthzService, nil) }) - oc.WaitDeploymentRolloutComplete(t, ns, "ext-authz") + oc.WaitDeploymentRolloutComplete(t, ns.Foo, "ext-authz") t.LogStep("Set envoyExtAuthzHttp extension provider in SMCP") if env.GetSMCPVersion().LessThan(version.SMCP_2_4) { @@ -120,48 +121,46 @@ spec: t.LogStep("Deploy the external authorization in the Authorization policy") t.Cleanup(func() { - oc.DeleteFromString(t, ns, ExternalRoute) + oc.DeleteFromString(t, ns.Foo, ExternalRoute) }) - oc.ApplyString(t, ns, ExternalRoute) + oc.ApplyString(t, ns.Foo, ExternalRoute) t.LogStep("Verify a request to path /headers with header x-ext-authz: deny is denied by the sample ext_authz server:") - app.AssertSleepPodRequestForbidden(t, ns, "http://httpbin:8000/headers", app.CurlOpts{Headers: []string{"x-ext-authz: deny"}}) + app.AssertSleepPodRequestForbidden(t, ns.Foo, "http://httpbin:8000/headers", app.CurlOpts{Headers: []string{"x-ext-authz: deny"}}) t.LogStep("Verify a request to path /headers with header x-ext-authz: allow is allowed by the sample ext_authz server") - app.AssertSleepPodRequestSuccess(t, ns, "http://httpbin:8000/headers", app.CurlOpts{Headers: []string{"x-ext-authz: allow"}}) + app.AssertSleepPodRequestSuccess(t, ns.Foo, "http://httpbin:8000/headers", app.CurlOpts{Headers: []string{"x-ext-authz: allow"}}) t.LogStep("Verify a request to path /ip is allowed and does not trigger the external authorization") - app.AssertSleepPodRequestSuccess(t, ns, "http://httpbin:8000/ip") + app.AssertSleepPodRequestSuccess(t, ns.Foo, "http://httpbin:8000/ip") }) } func TestEnvoyExtAuthzGrpcExtensionProvider(t *testing.T) { - test.NewTest(t).Id("T42").Groups(test.Full, test.InterOp, test.ARM).Run(func(t test.TestHelper) { + NewTest(t).Id("T42").Groups(Full, InterOp, ARM).Run(func(t TestHelper) { if env.GetSMCPVersion().LessThan(version.SMCP_2_3) { t.Skip("extensionProviders.envoyExtAuthzGrpc is not supported in versions below v2.3") } t.Log("This test validates authorization policies with a JWT Token") - ns := "foo" - ossm.DeployControlPlane(t) t.LogStep("Install httpbin and sleep") - app.InstallAndWaitReady(t, app.Httpbin(ns), app.Sleep(ns)) + app.InstallAndWaitReady(t, app.Httpbin(ns.Foo), app.Sleep(ns.Foo)) t.Cleanup(func() { - app.Uninstall(t, app.Httpbin(ns), app.Sleep(ns)) + app.Uninstall(t, app.Httpbin(ns.Foo), app.Sleep(ns.Foo)) }) t.LogStep("Check if httpbin returns 200 OK when no authorization policies are in place") - app.AssertSleepPodRequestSuccess(t, ns, "http://httpbin:8000/ip") + app.AssertSleepPodRequestSuccess(t, ns.Foo, "http://httpbin:8000/ip") t.LogStep("Deploy the External Authorizer and Verify the sample external authorizer is up and running") - oc.ApplyTemplate(t, ns, ExternalAuthzService, nil) + oc.ApplyTemplate(t, ns.Foo, ExternalAuthzService, nil) t.Cleanup(func() { - oc.DeleteFromTemplate(t, ns, ExternalAuthzService, nil) + oc.DeleteFromTemplate(t, ns.Foo, ExternalAuthzService, nil) }) - oc.WaitDeploymentRolloutComplete(t, ns, "ext-authz") + oc.WaitDeploymentRolloutComplete(t, ns.Foo, "ext-authz") t.LogStep("Set envoyExtAuthzgRPC extension provider in SMCP") if env.GetSMCPVersion().LessThan(version.SMCP_2_4) { @@ -202,21 +201,84 @@ spec: t.LogStep("Deploy the external authorization in the Authorization policy") t.Cleanup(func() { - oc.DeleteFromString(t, ns, ExternalRouteGrpc) + oc.DeleteFromString(t, ns.Foo, ExternalRouteGrpc) }) - oc.ApplyString(t, ns, ExternalRouteGrpc) + oc.ApplyString(t, ns.Foo, ExternalRouteGrpc) t.LogStep("Verify a request to path /headers with header x-ext-authz: deny is denied by the sample ext_authz server:") - app.AssertSleepPodRequestForbidden(t, ns, "http://httpbin:8000/headers", app.CurlOpts{Headers: []string{"x-ext-authz: deny"}}) + app.AssertSleepPodRequestForbidden(t, ns.Foo, "http://httpbin:8000/headers", app.CurlOpts{Headers: []string{"x-ext-authz: deny"}}) t.LogStep("Verify a request to path /headers with header x-ext-authz: allow is allowed by the sample ext_authz server") - app.AssertSleepPodRequestSuccess(t, ns, "http://httpbin:8000/headers", app.CurlOpts{Headers: []string{"x-ext-authz: allow"}}) + app.AssertSleepPodRequestSuccess(t, ns.Foo, "http://httpbin:8000/headers", app.CurlOpts{Headers: []string{"x-ext-authz: allow"}}) t.LogStep("Verify a request to path /ip is allowed and does not trigger the external authorization") - app.AssertSleepPodRequestSuccess(t, ns, "http://httpbin:8000/ip") + app.AssertSleepPodRequestSuccess(t, ns.Foo, "http://httpbin:8000/ip") }) } +func TestEnvoyExtAuthzRequestPayloadTooLarge(t *testing.T) { + NewTest(t).Groups(Full, Disconnected, ARM).Run(func(t TestHelper) { + t.Log("Verify that Istio proxy doesn't fail with 'Request payload too large' error") + t.Log("Reference: https://issues.redhat.com/browse/OSSM-5850") + + t.Cleanup(func() { + oc.RecreateNamespace(t, ns.Foo, meshNamespace) + }) + + ossm.DeployControlPlane(t) + + t.LogStep("Install httpbin and sleep") + app.InstallAndWaitReady(t, + app.Httpbin(ns.Foo), + app.Sleep(ns.Foo)) + + t.LogStep("Create 2MB file in the sleep pod") + app.ExecInSleepPod(t, ns.Foo, "fallocate -l 2M /tmp/fakefile_2MB") + assertFileUploadSuccess(t) + + t.LogStep("Deploy the External Authorizer and Verify the sample external authorizer is up and running") + oc.ApplyTemplate(t, ns.Foo, ExternalAuthzService, nil) + oc.WaitDeploymentRolloutComplete(t, ns.Foo, "ext-authz") + + t.LogStep("Deploy the external authorization in the Authorization policy") + oc.ApplyString(t, ns.Foo, ExternalRoute) + + t.LogStep("Patch SMCP to enable envoyExtAuthzHttp with allowPartialMessage true and maxRequestBytes 1KB") + oc.Patch(t, meshNamespace, "smcp", smcpName, "merge", uploadEnvoyExtSpec("true", 1024)) + oc.WaitSMCPReady(t, meshNamespace, smcpName) + assertFileUploadSuccess(t) + + t.LogStep("Patch SMCP to set allowPartialMessage to false and increase maxRequestBytes to 5MB") + oc.Patch(t, meshNamespace, "smcp", smcpName, "merge", uploadEnvoyExtSpec("false", 5000000)) + oc.WaitSMCPReady(t, meshNamespace, smcpName) + assertFileUploadSuccess(t) + }) +} + +func assertFileUploadSuccess(t TestHelper) { + t.LogStep("Send a POST request with a large file (2MB)") + app.AssertSleepPodRequestSuccess(t, ns.Foo, "http://httpbin:8000/post", app.CurlOpts{ + Method: "POST", + Headers: []string{"x-ext-authz: allow", "Content-Type: multipart/form-data"}, + Options: []string{"-F file=@/tmp/fakefile_2MB"}}) +} + +func uploadEnvoyExtSpec(AllowPartialMessage string, MaxRequestBytes int) string { + return fmt.Sprintf(` +spec: + meshConfig: + extensionProviders: + - name: sample-ext-authz-http + envoyExtAuthzHttp: + service: ext-authz.foo.svc.cluster.local + port: 8000 + includeRequestHeadersInCheck: ["x-ext-authz"] + includeRequestBodyInCheck: + allowPartialMessage: %s + maxRequestBytes: %d + packAsBytes: true`, AllowPartialMessage, MaxRequestBytes) +} + const ( ExternalRoute = ` apiVersion: security.istio.io/v1beta1 @@ -237,7 +299,7 @@ spec: # The rules specify when to trigger the external authorizer. - to: - operation: - paths: ["/headers"] + paths: ["/headers", "/post"] ` ExternalRouteGrpc = ` apiVersion: security.istio.io/v1beta1