kuma
563 строки · 17.1 Кб
1package gateway
2
3import (
4"encoding/base64"
5"fmt"
6"strings"
7
8"github.com/gruntwork-io/terratest/modules/k8s"
9. "github.com/onsi/ginkgo/v2"
10. "github.com/onsi/gomega"
11
12"github.com/kumahq/kuma/pkg/plugins/policies/meshtrafficpermission/api/v1alpha1"
13. "github.com/kumahq/kuma/test/framework"
14"github.com/kumahq/kuma/test/framework/client"
15"github.com/kumahq/kuma/test/framework/deployments/testserver"
16"github.com/kumahq/kuma/test/framework/envs/kubernetes"
17)
18
19func Mtls() {
20meshName := "gateway-mtls"
21namespace := "gateway-mtls"
22clientNamespace := "gateway-mtls-client"
23
24meshGateway := `
25apiVersion: kuma.io/v1alpha1
26kind: MeshGateway
27metadata:
28name: mtls-edge-gateway
29mesh: gateway-mtls
30spec:
31selectors:
32- match:
33kuma.io/service: mtls-edge-gateway
34conf:
35listeners:
36- port: 8080
37protocol: HTTP
38hostname: example.kuma.io
39tags:
40hostname: example.kuma.io
41- port: 8081
42protocol: TCP
43tags:
44protocol: tcp
45- port: 8082
46protocol: TLS
47tls:
48mode: PASSTHROUGH
49hostname: example-passthrough.kuma.io
50tags:
51name: tls-passthrough
52- port: 8083
53protocol: TLS
54tls:
55mode: TERMINATE
56certificates:
57- secret: kuma-io-certificate-k8s-mtls
58tags:
59name: tls-terminate
60`
61
62BeforeAll(func() {
63httpsSecret := func() string {
64cert, key, err := CreateCertsFor("example.kuma.io")
65Expect(err).To(Succeed())
66secretData := base64.StdEncoding.EncodeToString([]byte(strings.Join([]string{key, cert}, "\n")))
67return fmt.Sprintf(`
68apiVersion: v1
69kind: Secret
70metadata:
71name: kuma-io-certificate-k8s-mtls
72namespace: %s
73labels:
74kuma.io/mesh: gateway-mtls
75data:
76value: %s
77type: system.kuma.io/secret
78`, Config.KumaNamespace, secretData)
79}
80err := NewClusterSetup().
81Install(MTLSMeshKubernetes(meshName)).
82Install(NamespaceWithSidecarInjection(namespace)).
83Install(Namespace(clientNamespace)).
84Install(testserver.Install(
85testserver.WithName("demo-client"),
86testserver.WithNamespace(clientNamespace),
87)).
88Install(YamlK8s(httpsSecret())).
89Install(YamlK8s(meshGateway)).
90Install(MeshTrafficPermissionAllowAllKubernetes(meshName)).
91Install(YamlK8s(MkGatewayInstance("mtls-edge-gateway", namespace, meshName))).
92Setup(kubernetes.Cluster)
93Expect(err).ToNot(HaveOccurred())
94})
95
96E2EAfterAll(func() {
97Expect(kubernetes.Cluster.TriggerDeleteNamespace(namespace)).To(Succeed())
98Expect(kubernetes.Cluster.TriggerDeleteNamespace(clientNamespace)).To(Succeed())
99Expect(kubernetes.Cluster.DeleteMesh(meshName)).To(Succeed())
100})
101
102Context("HTTP", func() {
103meshGatewayRouteHTTP := `
104apiVersion: kuma.io/v1alpha1
105kind: MeshGatewayRoute
106metadata:
107name: mtls-edge-gateway-http
108mesh: gateway-mtls
109spec:
110selectors:
111- match:
112kuma.io/service: mtls-edge-gateway
113hostname: example.kuma.io
114conf:
115http:
116rules:
117- matches:
118- path:
119match: PREFIX
120value: /prefix-trailing/middle/
121filters:
122- rewrite:
123replacePrefixMatch: /middle
124backends:
125- destination:
126kuma.io/service: echo-server_gateway-mtls_svc_80
127- matches:
128- path:
129match: PREFIX
130value: /prefix/middle
131filters:
132- rewrite:
133replacePrefixMatch: /middle
134backends:
135- destination:
136kuma.io/service: echo-server_gateway-mtls_svc_80
137- matches:
138- path:
139match: PREFIX
140value: /drop-prefix
141filters:
142- rewrite:
143replacePrefixMatch: /
144backends:
145- destination:
146kuma.io/service: echo-server_gateway-mtls_svc_80
147- matches:
148- path:
149match: PREFIX
150value: /drop-prefix-trailing/
151filters:
152- rewrite:
153replacePrefixMatch: /
154backends:
155- destination:
156kuma.io/service: echo-server_gateway-mtls_svc_80
157- matches:
158- path:
159match: PREFIX
160value: /non-accessible
161backends:
162- destination:
163kuma.io/service: non-accessible-echo-server_gateway-mtls_svc_80
164- matches:
165- path:
166match: PREFIX
167value: /
168backends:
169- destination:
170kuma.io/service: echo-server_gateway-mtls_svc_80
171`
172BeforeAll(func() {
173err := NewClusterSetup().
174Install(testserver.Install(
175testserver.WithMesh(meshName),
176testserver.WithName("echo-server"),
177testserver.WithNamespace(namespace),
178testserver.WithEchoArgs("echo", "--instance", "kubernetes"),
179)).
180Install(testserver.Install(
181testserver.WithMesh(meshName),
182testserver.WithName("non-accessible-echo-server"),
183testserver.WithNamespace(namespace),
184testserver.WithEchoArgs("echo", "--instance", "non-accessible-echo-server"),
185)).
186Install(YamlK8s(meshGatewayRouteHTTP)).
187Setup(kubernetes.Cluster)
188Expect(err).ToNot(HaveOccurred())
189})
190
191BeforeEach(func() {
192Expect(kubernetes.Cluster.Install(MeshTrafficPermissionAllowAllKubernetes(meshName))).To(Succeed())
193})
194
195It("should proxy simple HTTP requests", func() {
196Eventually(func(g Gomega) {
197response, err := client.CollectEchoResponse(
198kubernetes.Cluster, "demo-client",
199"http://mtls-edge-gateway.gateway-mtls:8080/",
200client.WithHeader("host", "example.kuma.io"),
201client.FromKubernetesPod(clientNamespace, "demo-client"),
202)
203
204g.Expect(err).ToNot(HaveOccurred())
205g.Expect(response.Instance).To(Equal("kubernetes"))
206}, "30s", "1s").Should(Succeed())
207})
208
209replacePrefix := func(prefix string) func() {
210return func() {
211Specify("when the prefix is the entire path", func() {
212Eventually(func(g Gomega) {
213response, err := client.CollectEchoResponse(
214kubernetes.Cluster, "demo-client", fmt.Sprintf("http://mtls-edge-gateway.gateway-mtls:8080/%s/middle", prefix),
215client.WithHeader("host", "example.kuma.io"),
216client.FromKubernetesPod(clientNamespace, "demo-client"),
217)
218
219g.Expect(err).ToNot(HaveOccurred())
220g.Expect(response.Received.Path).To(Equal("/middle"))
221}, "30s", "1s").Should(Succeed())
222})
223
224Specify("when it's a non-trivial prefix", func() {
225Eventually(func(g Gomega) {
226response, err := client.CollectEchoResponse(
227kubernetes.Cluster, "demo-client", fmt.Sprintf("http://mtls-edge-gateway.gateway-mtls:8080/%s/middle/tail", prefix),
228client.WithHeader("host", "example.kuma.io"),
229client.FromKubernetesPod(clientNamespace, "demo-client"),
230)
231
232g.Expect(err).ToNot(HaveOccurred())
233g.Expect(response.Received.Path).To(Equal("/middle/tail"))
234}, "30s", "1s").Should(Succeed())
235})
236
237Specify("ignoring non-path-separated prefixes", func() {
238Eventually(func(g Gomega) {
239response, err := client.CollectEchoResponse(
240kubernetes.Cluster, "demo-client", fmt.Sprintf("http://mtls-edge-gateway.gateway-mtls:8080/%s/middle_andmore", prefix),
241client.WithHeader("host", "example.kuma.io"),
242client.FromKubernetesPod(clientNamespace, "demo-client"),
243)
244
245g.Expect(err).ToNot(HaveOccurred())
246g.Expect(response.Received.Path).To(Equal(fmt.Sprintf("/%s/middle_andmore", prefix)))
247}, "30s", "1s").Should(Succeed())
248})
249}
250}
251
252replacePrefixWithRoot := func(prefix string) func() {
253return func() {
254Specify("when the prefix is the entire path", func() {
255Eventually(func(g Gomega) {
256response, err := client.CollectEchoResponse(
257kubernetes.Cluster, "demo-client", fmt.Sprintf("http://mtls-edge-gateway.gateway-mtls:8080/%s", prefix),
258client.WithHeader("host", "example.kuma.io"),
259client.FromKubernetesPod(clientNamespace, "demo-client"),
260)
261
262g.Expect(err).ToNot(HaveOccurred())
263g.Expect(response.Received.Path).To(Equal("/"))
264}, "30s", "1s").Should(Succeed())
265})
266
267Specify("when it's a non-trivial prefix", func() {
268Eventually(func(g Gomega) {
269response, err := client.CollectEchoResponse(
270kubernetes.Cluster, "demo-client", fmt.Sprintf("http://mtls-edge-gateway.gateway-mtls:8080/%s/tail", prefix),
271client.WithHeader("host", "example.kuma.io"),
272client.FromKubernetesPod(clientNamespace, "demo-client"),
273)
274
275g.Expect(err).ToNot(HaveOccurred())
276g.Expect(response.Received.Path).To(Equal("/tail"))
277}, "30s", "1s").Should(Succeed())
278})
279
280Specify("ignoring non-path-separated prefixes", func() {
281Eventually(func(g Gomega) {
282response, err := client.CollectEchoResponse(
283kubernetes.Cluster, "demo-client", fmt.Sprintf("http://mtls-edge-gateway.gateway-mtls:8080/%s_andmore", prefix),
284client.WithHeader("host", "example.kuma.io"),
285client.FromKubernetesPod(clientNamespace, "demo-client"),
286)
287
288g.Expect(err).ToNot(HaveOccurred())
289g.Expect(response.Received.Path).To(Equal(fmt.Sprintf("/%s_andmore", prefix)))
290}, "30s", "1s").Should(Succeed())
291})
292}
293}
294
295Describe("replacing a path prefix", replacePrefix("prefix"))
296Describe("replacing a path prefix with trailing prefix", replacePrefix("prefix-trailing"))
297
298Describe("replacing a path prefix with /", replacePrefixWithRoot("drop-prefix"))
299Describe("replacing a path prefix with /", replacePrefixWithRoot("drop-prefix-trailing"))
300
301It("should not access a service for which we don't have traffic permission", func() {
302Expect(DeleteMeshResources(kubernetes.Cluster, meshName, v1alpha1.MeshTrafficPermissionResourceTypeDescriptor)).To(Succeed())
303tp := `
304apiVersion: kuma.io/v1alpha1
305kind: MeshTrafficPermission
306metadata:
307namespace: kuma-system
308name: tp-non-accessible-echo-server.kuma-system
309labels:
310kuma.io/mesh: gateway-mtls
311spec:
312targetRef:
313kind: MeshService
314name: non-accessible-echo-server_gateway-mtls_svc_80
315from:
316- targetRef:
317kind: MeshService
318name: not-mtls-edge-gateway
319default:
320action: Allow`
321Expect(kubernetes.Cluster.Install(YamlK8s(tp))).To(Succeed())
322
323Eventually(func(g Gomega) {
324status, err := client.CollectFailure(
325kubernetes.Cluster, "demo-client", "http://mtls-edge-gateway.gateway-mtls:8080/non-accessible",
326client.WithHeader("host", "example.kuma.io"),
327client.FromKubernetesPod(clientNamespace, "demo-client"),
328)
329
330g.Expect(err).ToNot(HaveOccurred())
331g.Expect(status.ResponseCode).To(Equal(403))
332}, "30s", "1s").Should(Succeed())
333
334Expect(DeleteMeshPolicyOrError(
335kubernetes.Cluster,
336v1alpha1.MeshTrafficPermissionResourceTypeDescriptor,
337"tp-non-accessible-echo-server.kuma-system",
338)).To(Succeed())
339})
340
341It("should access a service when we have mesh traffic permission using MeshGateway", func() {
342// when no MeshTrafficPermission
343Expect(DeleteMeshResources(kubernetes.Cluster, meshName, v1alpha1.MeshTrafficPermissionResourceTypeDescriptor)).To(Succeed())
344
345// then cannot reach service
346Eventually(func(g Gomega) {
347response, err := client.CollectFailure(
348kubernetes.Cluster, "demo-client",
349"http://mtls-edge-gateway.gateway-mtls:8080/",
350client.WithHeader("host", "example.kuma.io"),
351client.FromKubernetesPod(clientNamespace, "demo-client"),
352client.NoFail(),
353client.OutputFormat(`{ "received": { "status": %{response_code} } }`),
354)
355
356g.Expect(err).ToNot(HaveOccurred())
357g.Expect(response.ResponseCode).To(Equal(403))
358}, "30s", "1s").Should(Succeed())
359
360tp := `
361apiVersion: kuma.io/v1alpha1
362kind: MeshTrafficPermission
363metadata:
364namespace: kuma-system
365name: access-echo-server.kuma-system
366labels:
367kuma.io/mesh: gateway-mtls
368spec:
369targetRef:
370kind: MeshService
371name: echo-server_gateway-mtls_svc_80
372from:
373- targetRef:
374kind: MeshGateway
375name: mtls-edge-gateway
376default:
377action: Allow`
378Expect(kubernetes.Cluster.Install(YamlK8s(tp))).To(Succeed())
379
380Eventually(func(g Gomega) {
381response, err := client.CollectEchoResponse(
382kubernetes.Cluster, "demo-client",
383"http://mtls-edge-gateway.gateway-mtls:8080/",
384client.WithHeader("host", "example.kuma.io"),
385client.FromKubernetesPod(clientNamespace, "demo-client"),
386)
387
388g.Expect(err).ToNot(HaveOccurred())
389g.Expect(response.Instance).To(Equal("kubernetes"))
390}, "30s", "1s").Should(Succeed())
391
392Expect(DeleteMeshPolicyOrError(
393kubernetes.Cluster,
394v1alpha1.MeshTrafficPermissionResourceTypeDescriptor,
395"access-echo-server.kuma-system",
396)).To(Succeed())
397})
398})
399
400Context("TCP", func() {
401tcpRoute := `
402apiVersion: kuma.io/v1alpha1
403kind: MeshGatewayRoute
404metadata:
405name: mtls-gateway-tcp
406mesh: gateway-mtls
407spec:
408selectors:
409- match:
410kuma.io/service: mtls-edge-gateway
411protocol: tcp
412conf:
413tcp:
414rules:
415- backends:
416- destination:
417kuma.io/service: tcp-server_gateway-mtls_svc_80
418`
419
420BeforeAll(func() {
421err := NewClusterSetup().
422Install(YamlK8s(tcpRoute)).
423Install(testserver.Install(
424testserver.WithMesh(meshName),
425testserver.WithName("tcp-server"),
426testserver.WithNamespace(namespace),
427testserver.WithHealthCheckTCPArgs("health-check", "tcp", "--port", "80"),
428)).
429Install(MeshTrafficPermissionAllowAllKubernetes(meshName)).
430Setup(kubernetes.Cluster)
431Expect(err).ToNot(HaveOccurred())
432})
433
434It("should proxy TCP connections", func() {
435Eventually(func(g Gomega) {
436response, err := client.CollectTCPResponse(kubernetes.Cluster, "demo-client", "telnet://mtls-edge-gateway.gateway-mtls:8081", "request",
437client.FromKubernetesPod(clientNamespace, "demo-client"),
438)
439
440g.Expect(err).ToNot(HaveOccurred())
441g.Expect(response).Should(Equal("response"))
442}, "30s", "1s").Should(Succeed())
443})
444})
445
446Context("TLS", func() {
447tlsServerRoute := `
448apiVersion: kuma.io/v1alpha1
449kind: MeshGatewayRoute
450metadata:
451name: mtls-gateway-tls-passthrough
452mesh: gateway-mtls
453spec:
454selectors:
455- match:
456kuma.io/service: mtls-edge-gateway
457name: tls-passthrough
458conf:
459tcp:
460rules:
461- backends:
462- destination:
463kuma.io/service: tls-server_gateway-mtls_svc_443
464`
465tcpServerRoute := `
466apiVersion: kuma.io/v1alpha1
467kind: MeshGatewayRoute
468metadata:
469name: mtls-gateway-tls-terminate
470mesh: gateway-mtls
471spec:
472selectors:
473- match:
474kuma.io/service: mtls-edge-gateway
475name: tls-terminate
476conf:
477tcp:
478rules:
479- backends:
480- destination:
481kuma.io/service: tcp-server_gateway-mtls_svc_80
482`
483
484BeforeAll(func() {
485cert, key, err := CreateCertsFor("example.kuma.io")
486Expect(err).To(Succeed())
487
488setup := NewClusterSetup().
489Install(YamlK8s(tlsServerRoute)).
490Install(YamlK8s(tcpServerRoute)).
491Install(testserver.Install(
492testserver.WithMesh(meshName),
493testserver.WithName("tls-server"),
494testserver.WithTLS(key, cert),
495testserver.WithNamespace(namespace),
496)).
497Install(testserver.Install(
498testserver.WithMesh(meshName),
499testserver.WithName("tcp-server"),
500testserver.WithNamespace(namespace),
501testserver.WithHealthCheckTCPArgs("health-check", "tcp", "--port", "80"),
502)).
503Install(MeshTrafficPermissionAllowAllKubernetes(meshName))
504Expect(setup.Setup(kubernetes.Cluster)).To(Succeed())
505})
506
507It("should passthrough TLS connections", func() {
508Eventually(func(g Gomega) {
509clusterIP, err := k8s.RunKubectlAndGetOutputE(
510kubernetes.Cluster.GetTesting(),
511kubernetes.Cluster.GetKubectlOptions(namespace),
512"get", "service", "mtls-edge-gateway", "-ojsonpath={.spec.clusterIP}",
513)
514g.Expect(err).ToNot(HaveOccurred())
515
516response, err := client.CollectEchoResponse(
517kubernetes.Cluster, "demo-client",
518"https://example-passthrough.kuma.io:8082/",
519client.Resolve("example-passthrough.kuma.io:8082", clusterIP),
520client.Insecure(),
521client.FromKubernetesPod(clientNamespace, "demo-client"),
522)
523
524g.Expect(err).ToNot(HaveOccurred())
525g.Expect(response.Instance).To(HavePrefix("tls-server"))
526}, "30s", "1s").Should(Succeed())
527})
528
529It("should not passthrough TLS connections that don't match SNI", func() {
530Consistently(func(g Gomega) {
531clusterIP, err := k8s.RunKubectlAndGetOutputE(
532kubernetes.Cluster.GetTesting(),
533kubernetes.Cluster.GetKubectlOptions(namespace),
534"get", "service", "mtls-edge-gateway", "-ojsonpath={.spec.clusterIP}",
535)
536g.Expect(err).ToNot(HaveOccurred())
537
538g.Expect(err).ToNot(HaveOccurred())
539status, err := client.CollectFailure(
540kubernetes.Cluster, "demo-client",
541"https://example-other-hostname.kuma.io:8082/",
542client.Resolve("example-other-hostname.kuma.io:8082", clusterIP),
543client.Insecure(),
544client.FromKubernetesPod(clientNamespace, "demo-client"),
545)
546
547g.Expect(err).ToNot(HaveOccurred())
548g.Expect(status.Exitcode).To(Equal(35))
549}, "30s", "1s").Should(Succeed())
550})
551
552It("should terminate TLS and proxy TCP connections", func() {
553Eventually(func(g Gomega) {
554response, err := client.CollectTLSResponse(kubernetes.Cluster, "demo-client", "mtls-edge-gateway.gateway-mtls:8083", "request",
555client.FromKubernetesPod(clientNamespace, "demo-client"),
556)
557
558g.Expect(err).ToNot(HaveOccurred())
559g.Expect(response).Should(Equal("response"))
560}, "30s", "1s").Should(Succeed())
561})
562})
563}
564