1import json
2from typing import Generator, Literal, Tuple, Union, cast
3
4from abstract_tests import AGRPC, AHTTP, HTTP, AmbassadorTest, Node, ServiceType, WebsocketEcho
5from kat.harness import EDGE_STACK, Query
6from tests.selfsigned import TLSCerts
7
8
9class AuthenticationGRPCTest(AmbassadorTest):
10
11 target: ServiceType
12 auth: ServiceType
13
14 def init(self):
15 if EDGE_STACK:
16 self.xfail = "XFailing for now, custom AuthServices not supported in Edge Stack"
17 self.target = HTTP()
18 self.auth = AGRPC(name="auth")
19
20 def manifests(self) -> str:
21 return (
22 self.format(
23 """
24---
25apiVersion: getambassador.io/v3alpha1
26kind: Mapping
27metadata:
28 name: auth-context-mapping
29spec:
30 ambassador_id: [{self.ambassador_id}]
31 service: {self.target.path.fqdn}
32 hostname: "*"
33 prefix: /context-extensions-crd/
34 auth_context_extensions:
35 context: "auth-context-name"
36 data: "auth-data"
37"""
38 )
39 + super().manifests()
40 )
41
42 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
43 yield self, self.format(
44 """
45---
46apiVersion: getambassador.io/v3alpha1
47kind: AuthService
48name: {self.auth.path.k8s}
49auth_service: "{self.auth.path.fqdn}"
50timeout_ms: 5000
51proto: grpc
52protocol_version: "v3"
53"""
54 )
55 yield self, self.format(
56 """
57---
58apiVersion: getambassador.io/v3alpha1
59kind: Mapping
60name: {self.target.path.k8s}
61hostname: "*"
62prefix: /target/
63service: {self.target.path.fqdn}
64---
65apiVersion: getambassador.io/v3alpha1
66kind: Mapping
67name: {self.target.path.k8s}-context-extensions
68hostname: "*"
69prefix: /context-extensions/
70service: {self.target.path.fqdn}
71auth_context_extensions:
72 first: "first element"
73 second: "second element"
74"""
75 )
76
77 def queries(self):
78 # [0]
79 yield Query(
80 self.url("target/"),
81 headers={
82 "kat-req-extauth-requested-status": "401",
83 "baz": "baz",
84 "request-header": "baz",
85 },
86 expected=401,
87 )
88 # [1]
89 yield Query(
90 self.url("target/"),
91 headers={
92 "kat-req-extauth-requested-status": "302",
93 "kat-req-extauth-requested-location": "foo",
94 },
95 expected=302,
96 )
97
98 # [2]
99 yield Query(
100 self.url("target/"),
101 headers={
102 "kat-req-extauth-requested-status": "401",
103 "x-foo": "foo",
104 "kat-req-extauth-requested-header": "x-foo",
105 },
106 expected=401,
107 )
108 # [3]
109 yield Query(
110 self.url("target/"),
111 headers={
112 "kat-req-extauth-requested-status": "200",
113 "authorization": "foo-11111",
114 "foo": "foo",
115 "kat-req-extauth-append": "foo=bar;baz=bar",
116 "kat-req-http-requested-header": "Authorization",
117 },
118 expected=200,
119 )
120 # [4]
121 yield Query(
122 self.url("context-extensions/"),
123 headers={
124 "request-status": "200",
125 "authorization": "foo-22222",
126 "kat-req-http-requested-header": "Authorization",
127 },
128 expected=200,
129 )
130 # [5]
131 yield Query(
132 self.url("context-extensions-crd/"),
133 headers={
134 "request-status": "200",
135 "authorization": "foo-33333",
136 "kat-req-http-requested-header": "Authorization",
137 },
138 expected=200,
139 )
140
141 def check(self):
142 # [0] Verifies all request headers sent to the authorization server.
143 assert self.results[0].backend
144 assert self.results[0].backend.name == self.auth.path.k8s
145 assert self.results[0].backend.request
146 assert self.results[0].backend.request.url.path == "/target/"
147 assert self.results[0].backend.request.headers["x-envoy-internal"] == ["true"]
148 assert self.results[0].backend.request.headers["x-forwarded-proto"] == ["http"]
149 assert "user-agent" in self.results[0].backend.request.headers
150 assert "baz" in self.results[0].backend.request.headers
151 assert self.results[0].status == 401
152 assert self.results[0].headers["Server"] == ["envoy"]
153 assert self.results[0].headers["Kat-Resp-Extauth-Protocol-Version"] == ["v3"]
154
155 # [1] Verifies that Location header is returned from Envoy.
156 assert self.results[1].backend
157 assert self.results[1].backend.name == self.auth.path.k8s
158 assert self.results[1].backend.request
159 assert self.results[1].backend.request.headers["kat-req-extauth-requested-status"] == [
160 "302"
161 ]
162 assert self.results[1].backend.request.headers["kat-req-extauth-requested-location"] == [
163 "foo"
164 ]
165 assert self.results[1].status == 302
166 assert self.results[1].headers["Location"] == ["foo"]
167 assert self.results[1].headers["Kat-Resp-Extauth-Protocol-Version"] == ["v3"]
168
169 # [2] Verifies Envoy returns whitelisted headers input by the user.
170 assert self.results[2].backend
171 assert self.results[2].backend.name == self.auth.path.k8s
172 assert self.results[2].backend.request
173 assert self.results[2].backend.request.headers["kat-req-extauth-requested-status"] == [
174 "401"
175 ]
176 assert self.results[2].backend.request.headers["kat-req-extauth-requested-header"] == [
177 "x-foo"
178 ]
179 assert self.results[2].backend.request.headers["x-foo"] == ["foo"]
180 assert self.results[2].status == 401
181 assert self.results[2].headers["Server"] == ["envoy"]
182 assert self.results[2].headers["X-Foo"] == ["foo"]
183 assert self.results[2].headers["Kat-Resp-Extauth-Protocol-Version"] == ["v3"]
184
185 # [3] Verifies default whitelisted Authorization request header.
186 assert self.results[3].backend
187 assert self.results[3].backend.request
188 assert self.results[3].backend.request.headers["kat-req-extauth-requested-status"] == [
189 "200"
190 ]
191 assert self.results[3].backend.request.headers["kat-req-http-requested-header"] == [
192 "Authorization"
193 ]
194 assert self.results[3].backend.request.headers["authorization"] == ["foo-11111"]
195 assert self.results[3].backend.request.headers["foo"] == ["foo,bar"]
196 assert self.results[3].backend.request.headers["baz"] == ["bar"]
197 assert self.results[3].status == 200
198 assert self.results[3].headers["Server"] == ["envoy"]
199 assert self.results[3].headers["Authorization"] == ["foo-11111"]
200 assert self.results[3].backend.request.headers["kat-resp-extauth-protocol-version"] == [
201 "v3"
202 ]
203
204 # [4] Verifies that auth_context_extension is passed along by Envoy.
205 assert self.results[4].status == 200
206 assert self.results[4].headers["Server"] == ["envoy"]
207 assert self.results[4].headers["Authorization"] == ["foo-22222"]
208 assert self.results[4].backend
209 assert self.results[4].backend.request
210 context_ext = json.loads(
211 self.results[4].backend.request.headers["kat-resp-extauth-context-extensions"][0]
212 )
213 assert context_ext["first"] == "first element"
214 assert context_ext["second"] == "second element"
215
216 # [5] Verifies that auth_context_extension is passed along by Envoy when using a crd Mapping
217 assert self.results[5].status == 200
218 assert self.results[5].headers["Server"] == ["envoy"]
219 assert self.results[5].headers["Authorization"] == ["foo-33333"]
220 assert self.results[5].backend
221 assert self.results[5].backend.request
222 context_ext = json.loads(
223 self.results[5].backend.request.headers["kat-resp-extauth-context-extensions"][0]
224 )
225 assert context_ext["context"] == "auth-context-name"
226 assert context_ext["data"] == "auth-data"
227
228
229class AuthenticationHTTPPartialBufferTest(AmbassadorTest):
230
231 target: ServiceType
232 auth: ServiceType
233
234 def init(self):
235 if EDGE_STACK:
236 self.xfail = "XFailing for now, custom AuthServices not supported in Edge Stack"
237 self.target = HTTP()
238 self.auth = HTTP(name="auth")
239
240 def manifests(self) -> str:
241 return (
242 f"""
243---
244apiVersion: v1
245data:
246 tls.crt: {TLSCerts["tls-context-host-1"].k8s_crt}
247 tls.key: {TLSCerts["tls-context-host-1"].k8s_key}
248kind: Secret
249metadata:
250 name: auth-partial-secret
251type: kubernetes.io/tls
252"""
253 + super().manifests()
254 )
255
256 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
257 yield self, self.format(
258 """
259---
260apiVersion: getambassador.io/v3alpha1
261kind: TLSContext
262name: {self.name}-same-context-1
263secret: auth-partial-secret
264
265---
266apiVersion: getambassador.io/v3alpha1
267kind: AuthService
268name: {self.auth.path.k8s}
269auth_service: "{self.auth.path.fqdn}"
270path_prefix: "/extauth"
271timeout_ms: 5000
272tls: {self.name}-same-context-1
273
274allowed_request_headers:
275- Kat-Req-Http-Requested-Status
276- Kat-Req-Http-Requested-Header
277
278allowed_authorization_headers:
279- Kat-Resp-Http-Request-Body
280
281add_auth_headers:
282 X-Added-Auth: auth-added
283
284include_body:
285 max_bytes: 7
286 allow_partial: true
287"""
288 )
289 yield self, self.format(
290 """
291---
292apiVersion: getambassador.io/v3alpha1
293kind: Mapping
294name: {self.target.path.k8s}
295hostname: "*"
296prefix: /target/
297service: {self.target.path.fqdn}
298"""
299 )
300
301 def queries(self):
302 # [0]
303 yield Query(
304 self.url("target/"),
305 headers={"kat-req-http-requested-status": "200"},
306 body="message_body",
307 expected=200,
308 )
309
310 # [1]
311 yield Query(
312 self.url("target/"),
313 headers={"kat-req-http-requested-status": "200"},
314 body="body",
315 expected=200,
316 )
317
318 # [2]
319 yield Query(
320 self.url("target/"),
321 headers={"kat-req-http-requested-status": "401"},
322 body="body",
323 expected=401,
324 )
325
326 def check(self):
327 # [0] Verifies that the authorization server received the partial message body.
328 extauth_res1 = json.loads(self.results[0].headers["Extauth"][0])
329 assert self.results[0].backend
330 assert self.results[0].backend.request
331 assert self.results[0].backend.request.headers["kat-req-http-requested-status"] == ["200"]
332 assert self.results[0].status == 200
333 assert self.results[0].headers["Server"] == ["envoy"]
334 assert extauth_res1["request"]["headers"]["kat-resp-http-request-body"] == ["message"]
335
336 # [1] Verifies that the authorization server received the full message body.
337 extauth_res2 = json.loads(self.results[1].headers["Extauth"][0])
338 assert self.results[1].backend
339 assert self.results[1].backend.request
340 assert self.results[1].backend.request.headers["kat-req-http-requested-status"] == ["200"]
341 assert self.results[1].status == 200
342 assert self.results[1].headers["Server"] == ["envoy"]
343 assert extauth_res2["request"]["headers"]["kat-resp-http-request-body"] == ["body"]
344
345 # [2] Verifies that the authorization server received added headers
346 assert self.results[2].backend
347 assert self.results[2].backend.request
348 assert self.results[2].backend.request.headers["kat-req-http-requested-status"] == ["401"]
349 assert self.results[2].backend.request.headers["x-added-auth"] == ["auth-added"]
350 assert self.results[2].status == 401
351 assert self.results[2].headers["Server"] == ["envoy"]
352 assert extauth_res2["request"]["headers"]["kat-resp-http-request-body"] == ["body"]
353
354
355class AuthenticationHTTPBufferedTest(AmbassadorTest):
356
357 target: ServiceType
358 auth: ServiceType
359
360 def init(self):
361 if EDGE_STACK:
362 self.xfail = "XFailing for now, custom AuthServices not supported in Edge Stack"
363 self.target = HTTP()
364 self.auth = HTTP(name="auth")
365
366 def manifests(self) -> str:
367 return (
368 f"""
369---
370apiVersion: v1
371data:
372 tls.crt: {TLSCerts["tls-context-host-1"].k8s_crt}
373 tls.key: {TLSCerts["tls-context-host-1"].k8s_key}
374kind: Secret
375metadata:
376 name: auth-buffered-secret
377type: kubernetes.io/tls
378"""
379 + super().manifests()
380 )
381
382 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
383 yield self, self.format(
384 """
385---
386apiVersion: getambassador.io/v3alpha1
387kind: Module
388name: ambassador
389config:
390 add_linkerd_headers: true
391 buffer:
392 max_request_bytes: 16384
393---
394apiVersion: getambassador.io/v3alpha1
395kind: TLSContext
396name: {self.name}-same-context-1
397secret: auth-buffered-secret
398---
399apiVersion: getambassador.io/v3alpha1
400kind: AuthService
401name: {self.auth.path.k8s}
402auth_service: "{self.auth.path.fqdn}"
403path_prefix: "/extauth"
404timeout_ms: 5000
405tls: {self.name}-same-context-1
406
407allowed_request_headers:
408- X-Foo
409- X-Bar
410- Kat-Req-Http-Requested-Status
411- Kat-Req-Http-Requested-Header
412- Kat-Req-Http-Requested-Cookie
413- Location
414
415allowed_authorization_headers:
416- X-Foo
417- Set-Cookie
418
419include_body:
420 max_bytes: 4096
421 allow_partial: true
422"""
423 )
424 yield self, self.format(
425 """
426---
427apiVersion: getambassador.io/v3alpha1
428kind: Mapping
429name: {self.target.path.k8s}
430hostname: "*"
431prefix: /target/
432service: {self.target.path.fqdn}
433"""
434 )
435
436 def queries(self):
437 # [0]
438 yield Query(
439 self.url("target/"),
440 headers={"kat-req-http-requested-status": "401", "Baz": "baz", "Request-Header": "Baz"},
441 expected=401,
442 )
443 # [1]
444 yield Query(
445 self.url("target/"),
446 headers={
447 "kat-req-http-requested-status": "302",
448 "location": "foo",
449 "kat-req-http-requested-cookie": "foo, bar, baz",
450 "kat-req-http-requested-header": "location",
451 },
452 expected=302,
453 )
454 # [2]
455 yield Query(
456 self.url("target/"),
457 headers={
458 "kat-req-http-requested-status": "401",
459 "X-Foo": "foo",
460 "kat-req-http-requested-header": "X-Foo",
461 },
462 expected=401,
463 )
464 # [3]
465 yield Query(
466 self.url("target/"),
467 headers={
468 "kat-req-http-requested-status": "401",
469 "X-Bar": "bar",
470 "kat-req-http-requested-header": "X-Bar",
471 },
472 expected=401,
473 )
474 # [4]
475 yield Query(
476 self.url("target/"),
477 headers={
478 "kat-req-http-requested-status": "200",
479 "Authorization": "foo-11111",
480 "kat-req-http-requested-header": "Authorization",
481 },
482 expected=200,
483 )
484
485 def check(self):
486 # [0] Verifies all request headers sent to the authorization server.
487 assert self.results[0].backend
488 assert self.results[0].backend.name == self.auth.path.k8s
489 assert self.results[0].backend.request
490 assert self.results[0].backend.request.url.path == "/extauth/target/"
491 assert self.results[0].backend.request.headers["x-forwarded-proto"] == ["http"]
492 assert self.results[0].backend.request.headers["content-length"] == ["0"]
493 assert "x-forwarded-for" in self.results[0].backend.request.headers
494 assert "user-agent" in self.results[0].backend.request.headers
495 assert "baz" not in self.results[0].backend.request.headers
496 assert self.results[0].status == 401
497 assert self.results[0].headers["Server"] == ["envoy"]
498
499 # [1] Verifies that Location header is returned from Envoy.
500 assert self.results[1].backend
501 assert self.results[1].backend.name == self.auth.path.k8s
502 assert self.results[1].backend.request
503 assert self.results[1].backend.request.headers["kat-req-http-requested-status"] == ["302"]
504 assert self.results[1].backend.request.headers["kat-req-http-requested-header"] == [
505 "location"
506 ]
507 assert self.results[1].backend.request.headers["location"] == ["foo"]
508 assert self.results[1].status == 302
509 assert self.results[1].headers["Server"] == ["envoy"]
510 assert self.results[1].headers["Location"] == ["foo"]
511 assert self.results[1].headers["Set-Cookie"] == ["foo=foo", "bar=bar", "baz=baz"]
512
513 # [2] Verifies Envoy returns whitelisted headers input by the user.
514 assert self.results[2].backend
515 assert self.results[2].backend.name == self.auth.path.k8s
516 assert self.results[2].backend.request
517 assert self.results[2].backend.request.headers["kat-req-http-requested-status"] == ["401"]
518 assert self.results[2].backend.request.headers["kat-req-http-requested-header"] == ["X-Foo"]
519 assert self.results[2].backend.request.headers["x-foo"] == ["foo"]
520 assert self.results[2].status == 401
521 assert self.results[2].headers["Server"] == ["envoy"]
522 assert self.results[2].headers["X-Foo"] == ["foo"]
523
524 # [3] Verifies that envoy does not return not whitelisted headers.
525 assert self.results[3].backend
526 assert self.results[3].backend.name == self.auth.path.k8s
527 assert self.results[3].backend.request
528 assert self.results[3].backend.request.headers["kat-req-http-requested-status"] == ["401"]
529 assert self.results[3].backend.request.headers["kat-req-http-requested-header"] == ["X-Bar"]
530 assert self.results[3].backend.request.headers["x-bar"] == ["bar"]
531 assert self.results[3].status == 401
532 assert self.results[3].headers["Server"] == ["envoy"]
533 assert "X-Bar" not in self.results[3].headers
534
535 # [4] Verifies default whitelisted Authorization request header.
536 assert self.results[4].backend
537 assert self.results[4].backend.request
538 assert self.results[4].backend.request.headers["kat-req-http-requested-status"] == ["200"]
539 assert self.results[4].backend.request.headers["kat-req-http-requested-header"] == [
540 "Authorization"
541 ]
542 assert self.results[4].backend.request.headers["authorization"] == ["foo-11111"]
543 assert self.results[4].backend.request.headers["l5d-dst-override"] == [
544 f"{self.target.path.fqdn}:80"
545 ]
546 assert self.results[4].status == 200
547 assert self.results[4].headers["Server"] == ["envoy"]
548 assert self.results[4].headers["Authorization"] == ["foo-11111"]
549
550
551class AuthenticationHTTPFailureModeAllowTest(AmbassadorTest):
552 target: ServiceType
553 auth: ServiceType
554
555 def init(self):
556 if EDGE_STACK:
557 self.xfail = "XFailing for now, custom AuthServices not supported in Edge Stack"
558 self.target = HTTP()
559 self.auth = HTTP(name="auth")
560
561 def manifests(self) -> str:
562 return (
563 f"""
564---
565apiVersion: v1
566data:
567 tls.crt: {TLSCerts["tls-context-host-1"].k8s_crt}
568 tls.key: {TLSCerts["tls-context-host-1"].k8s_key}
569kind: Secret
570metadata:
571 name: auth-failure-secret
572type: kubernetes.io/tls
573"""
574 + super().manifests()
575 )
576
577 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
578 yield self, self.format(
579 """
580---
581apiVersion: getambassador.io/v3alpha1
582kind: TLSContext
583name: {self.name}-failure-context
584secret: auth-failure-secret
585
586---
587apiVersion: getambassador.io/v3alpha1
588kind: AuthService
589name: {self.auth.path.k8s}
590auth_service: "{self.auth.path.fqdn}"
591path_prefix: "/extauth"
592timeout_ms: 5000
593tls: {self.name}-failure-context
594
595allowed_request_headers:
596- Kat-Req-Http-Requested-Status
597- Kat-Req-Http-Requested-Header
598
599failure_mode_allow: true
600"""
601 )
602 yield self, self.format(
603 """
604---
605apiVersion: getambassador.io/v3alpha1
606kind: Mapping
607name: {self.target.path.k8s}
608hostname: "*"
609prefix: /target/
610service: {self.target.path.fqdn}
611"""
612 )
613
614 def queries(self):
615 # [0]
616 yield Query(
617 self.url("target/"), headers={"kat-req-http-requested-status": "200"}, expected=200
618 )
619
620 # [1]
621 yield Query(
622 self.url("target/"), headers={"kat-req-http-requested-status": "503"}, expected=503
623 )
624
625 def check(self):
626 # [0] Verifies that the authorization server received the partial message body.
627 extauth_res1 = json.loads(self.results[0].headers["Extauth"][0])
628 assert self.results[0].backend
629 assert self.results[0].backend.request
630 assert self.results[0].backend.request.headers["kat-req-http-requested-status"] == ["200"]
631 assert self.results[0].status == 200
632 assert self.results[0].headers["Server"] == ["envoy"]
633
634 # [1] Verifies that the authorization server received the full message body.
635 extauth_res2 = json.loads(self.results[1].headers["Extauth"][0])
636 assert self.results[1].backend
637 assert self.results[1].backend.request
638 assert self.results[1].backend.request.headers["kat-req-http-requested-status"] == ["503"]
639 assert self.results[1].headers["Server"] == ["envoy"]
640
641
642class AuthenticationTestV1(AmbassadorTest):
643
644 target: ServiceType
645 auth: ServiceType
646
647 def init(self):
648 if EDGE_STACK:
649 self.xfail = "XFailing for now, custom AuthServices not supported in Edge Stack"
650 self.target = HTTP()
651 self.auth1 = AHTTP(name="auth1")
652 self.auth2 = AHTTP(name="auth2")
653 self.backend_counts = {}
654
655 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
656 yield self, self.format(
657 """
658---
659apiVersion: getambassador.io/v3alpha1
660kind: AuthService
661name: {self.auth1.path.k8s}
662auth_service: "{self.auth1.path.fqdn}"
663proto: http
664path_prefix: "/extauth"
665timeout_ms: 5000
666
667allowed_request_headers:
668- X-Foo
669- X-Bar
670- Kat-Req-Http-Requested-Status
671- Kat-Req-Http-Requested-Header
672- Location
673
674allowed_authorization_headers:
675- X-Foo
676- Extauth
677
678status_on_error:
679 code: 503
680
681---
682apiVersion: getambassador.io/v3alpha1
683kind: AuthService
684name: {self.auth2.path.k8s}
685auth_service: "{self.auth2.path.fqdn}"
686proto: http
687path_prefix: "/extauth"
688timeout_ms: 5000
689add_linkerd_headers: true
690
691allowed_request_headers:
692- X-Foo
693- X-Bar
694- Kat-Req-Http-Requested-Status
695- Kat-Req-Http-Requested-Header
696- Location
697
698allowed_authorization_headers:
699- X-Foo
700- Extauth
701
702status_on_error:
703 code: 503
704
705"""
706 )
707 yield self, self.format(
708 """
709---
710apiVersion: getambassador.io/v3alpha1
711kind: Mapping
712name: {self.target.path.k8s}
713hostname: "*"
714prefix: /target/
715service: {self.target.path.fqdn}
716---
717apiVersion: getambassador.io/v3alpha1
718kind: Mapping
719name: {self.target.path.fqdn}-unauthed
720hostname: "*"
721prefix: /target/unauthed/
722service: {self.target.path.fqdn}
723bypass_auth: true
724"""
725 )
726
727 def queries(self):
728 # [0]
729 yield Query(
730 self.url("target/0"),
731 headers={"kat-req-http-requested-status": "401", "Baz": "baz", "Request-Header": "Baz"},
732 expected=401,
733 )
734 # [1]
735 yield Query(
736 self.url("target/1"),
737 headers={
738 "kat-req-http-requested-status": "302",
739 "location": "foo",
740 "kat-req-http-requested-header": "location",
741 },
742 expected=302,
743 )
744 # [2]
745 yield Query(
746 self.url("target/2"),
747 headers={
748 "kat-req-http-requested-status": "401",
749 "X-Foo": "foo",
750 "kat-req-http-requested-header": "X-Foo",
751 },
752 expected=401,
753 )
754 # [3]
755 yield Query(
756 self.url("target/3"),
757 headers={
758 "kat-req-http-requested-status": "401",
759 "X-Bar": "bar",
760 "kat-req-http-requested-header": "X-Bar",
761 },
762 expected=401,
763 )
764 # [4]
765 yield Query(
766 self.url("target/4"),
767 headers={
768 "kat-req-http-requested-status": "200",
769 "Authorization": "foo-11111",
770 "kat-req-http-requested-header": "Authorization",
771 },
772 expected=200,
773 )
774
775 # [5]
776 yield Query(self.url("target/5"), headers={"X-Forwarded-Proto": "https"}, expected=200)
777
778 # [6]
779 yield Query(
780 self.url("target/unauthed/6"),
781 headers={"kat-req-http-requested-status": "200"},
782 expected=200,
783 )
784
785 # [7]
786 yield Query(
787 self.url("target/7"), headers={"kat-req-http-requested-status": "500"}, expected=503
788 )
789
790 # Create some traffic to make it more likely that both auth services get at least one
791 # request
792 for i in range(20):
793 yield Query(
794 self.url("target/" + str(8 + i)),
795 headers={"kat-req-http-requested-status": "403"},
796 expected=403,
797 )
798
799 def check_backend_name(self, result) -> bool:
800 backend_name = result.backend.name
801
802 self.backend_counts.setdefault(backend_name, 0)
803 self.backend_counts[backend_name] += 1
804
805 return (backend_name == self.auth1.path.k8s) or (backend_name == self.auth2.path.k8s)
806
807 def check(self):
808
809 # [0] Verifies all request headers sent to the authorization server.
810 assert self.check_backend_name(self.results[0])
811 assert self.results[0].backend
812 assert self.results[0].backend.request
813 assert self.results[0].backend.request.url.path == "/extauth/target/0"
814 assert self.results[0].backend.request.headers["x-forwarded-proto"] == ["http"]
815 assert self.results[0].backend.request.headers["content-length"] == ["0"]
816 assert "x-forwarded-for" in self.results[0].backend.request.headers
817 assert "user-agent" in self.results[0].backend.request.headers
818 assert "baz" not in self.results[0].backend.request.headers
819 assert self.results[0].status == 401
820 assert self.results[0].headers["Server"] == ["envoy"]
821
822 # [1] Verifies that Location header is returned from Envoy.
823 assert self.check_backend_name(self.results[1])
824 assert self.results[1].backend
825 assert self.results[1].backend.request
826 assert self.results[1].backend.request.headers["kat-req-http-requested-status"] == ["302"]
827 assert self.results[1].backend.request.headers["kat-req-http-requested-header"] == [
828 "location"
829 ]
830 assert self.results[1].backend.request.headers["location"] == ["foo"]
831 assert self.results[1].status == 302
832 assert self.results[1].headers["Server"] == ["envoy"]
833 assert self.results[1].headers["Location"] == ["foo"]
834
835 # [2] Verifies Envoy returns whitelisted headers input by the user.
836 assert self.check_backend_name(self.results[2])
837 assert self.results[2].backend
838 assert self.results[2].backend.request
839 assert self.results[2].backend.request.headers["kat-req-http-requested-status"] == ["401"]
840 assert self.results[2].backend.request.headers["kat-req-http-requested-header"] == ["X-Foo"]
841 assert self.results[2].backend.request.headers["x-foo"] == ["foo"]
842 assert self.results[2].status == 401
843 assert self.results[2].headers["Server"] == ["envoy"]
844 assert self.results[2].headers["X-Foo"] == ["foo"]
845
846 # [3] Verifies that envoy does not return not whitelisted headers.
847 assert self.check_backend_name(self.results[3])
848 assert self.results[3].backend
849 assert self.results[3].backend.request
850 assert self.results[3].backend.request.headers["kat-req-http-requested-status"] == ["401"]
851 assert self.results[3].backend.request.headers["kat-req-http-requested-header"] == ["X-Bar"]
852 assert self.results[3].backend.request.headers["x-bar"] == ["bar"]
853 assert self.results[3].status == 401
854 assert self.results[3].headers["Server"] == ["envoy"]
855 assert "X-Bar" not in self.results[3].headers
856
857 # [4] Verifies default whitelisted Authorization request header.
858 assert self.results[4].backend
859 assert (
860 self.results[4].backend.name == self.target.path.k8s
861 ) # this response is from an auth success
862 assert self.results[4].backend.request
863 assert self.results[4].backend.request.headers["kat-req-http-requested-status"] == ["200"]
864 assert self.results[4].backend.request.headers["kat-req-http-requested-header"] == [
865 "Authorization"
866 ]
867 assert self.results[4].backend.request.headers["authorization"] == ["foo-11111"]
868 assert self.results[4].status == 200
869 assert self.results[4].headers["Server"] == ["envoy"]
870 assert self.results[4].headers["Authorization"] == ["foo-11111"]
871
872 extauth_req = json.loads(self.results[4].backend.request.headers["extauth"][0])
873 assert extauth_req["request"]["headers"]["l5d-dst-override"] == ["extauth:80"]
874
875 # [5] Verify that X-Forwarded-Proto makes it to the auth service.
876 #
877 # We use the 'extauth' header returned from the test extauth service for this, since
878 # the extauth service (on success) won't actually alter other things going upstream.
879 r5 = self.results[5]
880 assert r5
881 assert r5.backend
882 assert r5.backend.name == self.target.path.k8s # this response is from an auth success
883
884 assert r5.status == 200
885 assert r5.headers["Server"] == ["envoy"]
886
887 assert r5.backend.request
888 eahdr = r5.backend.request.headers["extauth"]
889 assert eahdr, "no extauth header was returned?"
890 assert eahdr[0], "an empty extauth header element was returned?"
891
892 # [6] Verifies that Envoy bypasses external auth when disabled for a mapping.
893 assert self.results[6].backend
894 assert (
895 self.results[6].backend.name == self.target.path.k8s
896 ) # ensure the request made it to the backend
897 assert not self.check_backend_name(
898 self.results[6]
899 ) # ensure the request did not go to the auth service
900 assert self.results[6].backend.request
901 assert self.results[6].backend.request.headers["kat-req-http-requested-status"] == ["200"]
902 assert self.results[6].status == 200
903 assert self.results[6].headers["Server"] == ["envoy"]
904
905 try:
906 eainfo = json.loads(eahdr[0])
907
908 if eainfo:
909 # Envoy should force this to HTTP, not HTTPS.
910 assert eainfo["request"]["headers"]["x-forwarded-proto"] == ["http"]
911 except ValueError as e:
912 assert False, "could not parse Extauth header '%s': %s" % (eahdr, e)
913
914 # [7] Verifies that envoy returns customized status_on_error code.
915 assert self.results[7].status == 503
916
917 # TODO(gsagula): Write tests for all UCs which request header headers
918 # are overridden, e.g. Authorization.
919
920 for i in range(20):
921 assert self.check_backend_name(self.results[8 + i])
922
923 print("auth1 service got %d requests" % self.backend_counts.get(self.auth1.path.k8s, -1))
924 print("auth2 service got %d requests" % self.backend_counts.get(self.auth2.path.k8s, -1))
925 assert self.backend_counts.get(self.auth1.path.k8s, 0) > 0, "auth1 got no requests"
926 assert self.backend_counts.get(self.auth2.path.k8s, 0) > 0, "auth2 got no requests"
927
928
929class AuthenticationTest(AmbassadorTest):
930 target: ServiceType
931 auth: ServiceType
932
933 def init(self):
934 if EDGE_STACK:
935 self.xfail = "XFailing for now, custom AuthServices not supported in Edge Stack"
936 self.target = HTTP()
937 self.auth = AHTTP(name="auth")
938
939 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
940 yield self, self.format(
941 """
942---
943apiVersion: getambassador.io/v3alpha1
944kind: AuthService
945name: {self.auth.path.k8s}
946auth_service: "{self.auth.path.fqdn}"
947path_prefix: "/extauth"
948
949allowed_request_headers:
950- X-Foo
951- X-Bar
952- Kat-Req-Http-Requested-Location
953- Kat-Req-Http-Requested-Status
954- Kat-Req-Http-Requested-Header
955
956allowed_authorization_headers:
957- X-Foo
958- X-Bar
959- Extauth
960
961"""
962 )
963 yield self, self.format(
964 """
965---
966apiVersion: getambassador.io/v3alpha1
967kind: Mapping
968name: {self.target.path.k8s}
969hostname: "*"
970prefix: /target/
971service: {self.target.path.fqdn}
972"""
973 )
974
975 def queries(self):
976 # [0]
977 yield Query(
978 self.url("target/"),
979 headers={"kat-req-http-requested-status": "401", "Baz": "baz", "Request-Header": "Baz"},
980 expected=401,
981 )
982 # [1]
983 yield Query(
984 self.url("target/"),
985 headers={
986 "kat-req-http-requested-status": "302",
987 "kat-req-http-requested-location": "foo",
988 "kat-req-http-requested-header": "location",
989 },
990 expected=302,
991 )
992 # [2]
993 yield Query(
994 self.url("target/"),
995 headers={
996 "kat-req-http-requested-status": "401",
997 "X-Foo": "foo",
998 "kat-req-http-requested-header": "X-Foo",
999 },
1000 expected=401,
1001 )
1002 # [3]
1003 yield Query(
1004 self.url("target/"),
1005 headers={
1006 "kat-req-http-requested-status": "401",
1007 "X-Bar": "bar",
1008 "kat-req-http-requested-header": "X-Bar",
1009 },
1010 expected=401,
1011 )
1012 # [4]
1013 yield Query(
1014 self.url("target/"),
1015 headers={
1016 "kat-req-http-requested-status": "200",
1017 "Authorization": "foo-11111",
1018 "kat-req-http-requested-header": "Authorization",
1019 },
1020 expected=200,
1021 )
1022 # [5]
1023 yield Query(self.url("target/"), headers={"X-Forwarded-Proto": "https"}, expected=200)
1024
1025 def check(self):
1026 # [0] Verifies all request headers sent to the authorization server.
1027 assert self.results[0].backend
1028 assert (
1029 self.results[0].backend.name == self.auth.path.k8s
1030 ), f"wanted backend {self.auth.path.k8s}, got {self.results[0].backend.name}"
1031 assert self.results[0].backend.request
1032 assert self.results[0].backend.request.url.path == "/extauth/target/"
1033 assert self.results[0].backend.request.headers["content-length"] == ["0"]
1034 assert "x-forwarded-for" in self.results[0].backend.request.headers
1035 assert "user-agent" in self.results[0].backend.request.headers
1036 assert "baz" not in self.results[0].backend.request.headers
1037 assert self.results[0].status == 401
1038 assert self.results[0].headers["Server"] == ["envoy"]
1039
1040 # [1] Verifies that Location header is returned from Envoy.
1041 assert self.results[1].backend
1042 assert self.results[1].backend.name == self.auth.path.k8s
1043 assert self.results[1].backend.request
1044 assert self.results[1].backend.request.headers["kat-req-http-requested-status"] == ["302"]
1045 assert self.results[1].backend.request.headers["kat-req-http-requested-header"] == [
1046 "location"
1047 ]
1048 assert self.results[1].backend.request.headers["kat-req-http-requested-location"] == ["foo"]
1049 assert self.results[1].status == 302
1050 assert self.results[1].headers["Server"] == ["envoy"]
1051 assert self.results[1].headers["Location"] == ["foo"]
1052
1053 # [2] Verifies Envoy returns whitelisted headers input by the user.
1054 assert self.results[2].backend
1055 assert self.results[2].backend.name == self.auth.path.k8s
1056 assert self.results[2].backend.request
1057 assert self.results[2].backend.request.headers["kat-req-http-requested-status"] == ["401"]
1058 assert self.results[2].backend.request.headers["kat-req-http-requested-header"] == ["X-Foo"]
1059 assert self.results[2].backend.request.headers["x-foo"] == ["foo"]
1060 assert self.results[2].status == 401
1061 assert self.results[2].headers["Server"] == ["envoy"]
1062 assert self.results[2].headers["X-Foo"] == ["foo"]
1063
1064 # [3] Verifies that envoy does not return not whitelisted headers.
1065 assert self.results[3].backend
1066 assert self.results[3].backend.name == self.auth.path.k8s
1067 assert self.results[3].backend.request
1068 assert self.results[3].backend.request.headers["kat-req-http-requested-status"] == ["401"]
1069 assert self.results[3].backend.request.headers["kat-req-http-requested-header"] == ["X-Bar"]
1070 assert self.results[3].backend.request.headers["x-bar"] == ["bar"]
1071 assert self.results[3].status == 401
1072 assert self.results[3].headers["Server"] == ["envoy"]
1073 assert "X-Bar" in self.results[3].headers
1074
1075 # [4] Verifies default whitelisted Authorization request header.
1076 assert self.results[4].backend
1077 assert self.results[4].backend.request
1078 assert self.results[4].backend.request.headers["kat-req-http-requested-status"] == ["200"]
1079 assert self.results[4].backend.request.headers["kat-req-http-requested-header"] == [
1080 "Authorization"
1081 ]
1082 assert self.results[4].backend.request.headers["authorization"] == ["foo-11111"]
1083 assert self.results[4].status == 200
1084 assert self.results[4].headers["Server"] == ["envoy"]
1085 assert self.results[4].headers["Authorization"] == ["foo-11111"]
1086
1087 # [5] Verify that X-Forwarded-Proto makes it to the auth service.
1088 #
1089 # We use the 'extauth' header returned from the test extauth service for this, since
1090 # the extauth service (on success) won't actually alter other things going upstream.
1091 r5 = self.results[5]
1092 assert r5
1093
1094 assert r5.status == 200
1095 assert r5.headers["Server"] == ["envoy"]
1096
1097 assert r5.backend
1098 assert r5.backend.request
1099 eahdr = r5.backend.request.headers["extauth"]
1100 assert eahdr, "no extauth header was returned?"
1101 assert eahdr[0], "an empty extauth header element was returned?"
1102
1103 try:
1104 eainfo = json.loads(eahdr[0])
1105
1106 if eainfo:
1107 # Envoy should force this to HTTP, not HTTPS.
1108 assert eainfo["request"]["headers"]["x-forwarded-proto"] == ["http"]
1109 except ValueError as e:
1110 assert False, "could not parse Extauth header '%s': %s" % (eahdr, e)
1111
1112 # TODO(gsagula): Write tests for all UCs which request header headers
1113 # are overridden, e.g. Authorization.
1114
1115
1116class AuthenticationWebsocketTest(AmbassadorTest):
1117
1118 auth: ServiceType
1119 backend: ServiceType
1120
1121 def init(self):
1122 if EDGE_STACK:
1123 self.xfail = "XFailing for now, custom AuthServices not supported in Edge Stack"
1124 self.auth = HTTP(name="auth")
1125 self.backend = WebsocketEcho()
1126
1127 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
1128 yield self, self.format(
1129 """
1130---
1131apiVersion: getambassador.io/v3alpha1
1132kind: AuthService
1133name: {self.auth.path.k8s}
1134auth_service: "{self.auth.path.fqdn}"
1135path_prefix: "/extauth"
1136timeout_ms: 10000
1137allowed_request_headers:
1138- Kat-Req-Http-Requested-Status
1139allow_request_body: true
1140---
1141apiVersion: getambassador.io/v3alpha1
1142kind: Mapping
1143name: {self.name}
1144hostname: "*"
1145prefix: /{self.name}/
1146service: {self.backend.path.fqdn}
1147use_websocket: true
1148"""
1149 )
1150
1151 def queries(self):
1152 yield Query(self.url(self.name + "/"), expected=404)
1153
1154 yield Query(self.url(self.name + "/", scheme="ws"), messages=["one", "two", "three"])
1155
1156 def check(self):
1157 assert self.results[-1].messages == ["one", "two", "three"]
1158
1159
1160class AuthenticationGRPCVerTest(AmbassadorTest):
1161
1162 target: ServiceType
1163 specified_protocol_version: Literal["v2", "v3", "default"]
1164 expected_protocol_version: Literal["v3", "invalid"]
1165 auth: ServiceType
1166
1167 @classmethod
1168 def variants(cls) -> Generator[Node, None, None]:
1169 for protocol_version in ["v2", "v3", "default"]:
1170 yield cls(protocol_version, name="{self.specified_protocol_version}")
1171
1172 def init(self, protocol_version: Literal["v2", "v3", "default"]):
1173 self.target = HTTP()
1174 self.specified_protocol_version = protocol_version
1175 self.expected_protocol_version = cast(
1176 Literal["v3", "invalid"], protocol_version if protocol_version in ["v3"] else "invalid"
1177 )
1178 self.auth = AGRPC(
1179 name="auth",
1180 protocol_version=(
1181 self.expected_protocol_version
1182 if self.expected_protocol_version != "invalid"
1183 else "v3"
1184 ),
1185 )
1186
1187 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
1188 yield self, self.format(
1189 """
1190---
1191apiVersion: getambassador.io/v3alpha1
1192kind: AuthService
1193name: {self.auth.path.k8s}
1194auth_service: "{self.auth.path.fqdn}"
1195timeout_ms: 5000
1196proto: grpc
1197"""
1198 ) + (
1199 ""
1200 if self.specified_protocol_version == "default"
1201 else f"protocol_version: '{self.specified_protocol_version}'"
1202 )
1203
1204 yield self, self.format(
1205 """
1206---
1207apiVersion: getambassador.io/v3alpha1
1208kind: Mapping
1209name: {self.target.path.k8s}
1210hostname: "*"
1211prefix: /target/
1212service: {self.target.path.fqdn}
1213"""
1214 )
1215
1216 def queries(self):
1217 # TODO add more
1218 # [0]
1219 yield Query(
1220 self.url("target/"),
1221 headers={
1222 "kat-req-extauth-requested-status": "401",
1223 "baz": "baz",
1224 "kat-req-extauth-request-header": "baz",
1225 },
1226 expected=(500 if self.expected_protocol_version == "invalid" else 401),
1227 )
1228
1229 # [1]
1230 yield Query(
1231 self.url("target/"),
1232 headers={
1233 "kat-req-extauth-requested-status": "302",
1234 "kat-req-extauth-requested-location": "foo",
1235 },
1236 expected=(500 if self.expected_protocol_version == "invalid" else 302),
1237 )
1238
1239 # [2]
1240 yield Query(
1241 self.url("target/"),
1242 headers={
1243 "kat-req-extauth-requested-status": "401",
1244 "x-foo": "foo",
1245 "kat-req-extauth-requested-header": "x-foo",
1246 },
1247 expected=(500 if self.expected_protocol_version == "invalid" else 401),
1248 )
1249 # [3]
1250 yield Query(
1251 self.url("target/"),
1252 headers={
1253 "kat-req-extauth-requested-status": "200",
1254 "authorization": "foo-11111",
1255 "foo": "foo",
1256 "kat-req-extauth-append": "foo=bar;baz=bar",
1257 "kat-req-http-requested-header": "Authorization",
1258 },
1259 expected=(500 if self.expected_protocol_version == "invalid" else 200),
1260 )
1261
1262 def check(self):
1263 if self.expected_protocol_version == "invalid":
1264 for i, result in enumerate(self.results):
1265 # Verify the basic structure of the HTTP 500's JSON body.
1266 assert result.json, f"self.results[{i}] does not have a JSON body"
1267 assert (
1268 result.json["status_code"] == 500
1269 ), f"self.results[{i}] JSON body={repr(result.json)} does not have status_code=500"
1270 assert result.json[
1271 "request_id"
1272 ], f"self.results[{i}] JSON body={repr(result.json)} does not have request_id"
1273 assert (
1274 self.path.k8s in result.json["message"]
1275 ), f"self.results[{i}] JSON body={repr(result.json)} does not have thing-containing-the-annotation-containing-the-AuthService name {repr(self.path.k8s)} in message"
1276 assert (
1277 "AuthService" in result.json["message"]
1278 ), f"self.results[{i}] JSON body={repr(result.json)} does not have type 'AuthService' in message"
1279 return
1280
1281 # [0] Verifies all request headers sent to the authorization server.
1282 assert self.results[0].backend
1283 assert self.results[0].backend.name == self.auth.path.k8s
1284 assert self.results[0].backend.request
1285 assert self.results[0].backend.request.url.path == "/target/"
1286 assert self.results[0].backend.request.headers["x-forwarded-proto"] == ["http"]
1287 assert "user-agent" in self.results[0].backend.request.headers
1288 assert "baz" in self.results[0].backend.request.headers
1289 assert self.results[0].status == 401
1290 assert self.results[0].headers["Server"] == ["envoy"]
1291 assert self.results[0].headers["Kat-Resp-Extauth-Protocol-Version"] == [
1292 self.expected_protocol_version
1293 ]
1294
1295 # [1] Verifies that Location header is returned from Envoy.
1296 assert self.results[1].backend
1297 assert self.results[1].backend.name == self.auth.path.k8s
1298 assert self.results[1].backend.request
1299 assert self.results[1].backend.request.headers["kat-req-extauth-requested-status"] == [
1300 "302"
1301 ]
1302 assert self.results[1].backend.request.headers["kat-req-extauth-requested-location"] == [
1303 "foo"
1304 ]
1305 assert self.results[1].status == 302
1306 assert self.results[1].headers["Location"] == ["foo"]
1307 assert self.results[1].headers["Kat-Resp-Extauth-Protocol-Version"] == [
1308 self.expected_protocol_version
1309 ]
1310
1311 # [2] Verifies Envoy returns whitelisted headers input by the user.
1312 assert self.results[2].backend
1313 assert self.results[2].backend.name == self.auth.path.k8s
1314 assert self.results[2].backend.request
1315 assert self.results[2].backend.request.headers["kat-req-extauth-requested-status"] == [
1316 "401"
1317 ]
1318 assert self.results[2].backend.request.headers["kat-req-extauth-requested-header"] == [
1319 "x-foo"
1320 ]
1321 assert self.results[2].backend.request.headers["x-foo"] == ["foo"]
1322 assert self.results[2].status == 401
1323 assert self.results[2].headers["Server"] == ["envoy"]
1324 assert self.results[2].headers["X-Foo"] == ["foo"]
1325 assert self.results[2].headers["Kat-Resp-Extauth-Protocol-Version"] == [
1326 self.expected_protocol_version
1327 ]
1328
1329 # [3] Verifies default whitelisted Authorization request header.
1330 assert self.results[3].backend
1331 assert self.results[3].backend.request
1332 assert self.results[3].backend.request.headers["kat-req-extauth-requested-status"] == [
1333 "200"
1334 ]
1335 assert self.results[3].backend.request.headers["kat-req-http-requested-header"] == [
1336 "Authorization"
1337 ]
1338 assert self.results[3].backend.request.headers["authorization"] == ["foo-11111"]
1339 assert self.results[3].backend.request.headers["foo"] == ["foo,bar"]
1340 assert self.results[3].backend.request.headers["baz"] == ["bar"]
1341 assert self.results[3].status == 200
1342 assert self.results[3].headers["Server"] == ["envoy"]
1343 assert self.results[3].headers["Authorization"] == ["foo-11111"]
1344 assert self.results[3].backend.request.headers["kat-resp-extauth-protocol-version"] == [
1345 self.expected_protocol_version
1346 ]
1347
1348
1349class AuthenticationDisabledOnRedirectTest(AmbassadorTest):
1350 """
1351 AuthenticationDisableOnRedirectTest: ensures that when a route is configured
1352 for https_redirect or host_redirect that it will perform the redirect
1353 without calling the AuthService (ext_authz).
1354 """
1355
1356 target: ServiceType
1357 auth: ServiceType
1358
1359 def init(self):
1360 if EDGE_STACK:
1361 self.xfail = "custom AuthServices not supported in Edge Stack"
1362 self.target = HTTP()
1363 self.auth = AHTTP(name="auth")
1364 self.add_default_http_listener = False
1365 self.add_default_https_listener = True
1366
1367 def manifests(self) -> str:
1368 return (
1369 self.format(
1370 """
1371---
1372apiVersion: v1
1373kind: Secret
1374metadata:
1375 name: {self.path.k8s}-secret
1376 labels:
1377 kat-ambassador-id: {self.ambassador_id}
1378type: kubernetes.io/tls
1379data:
1380 tls.crt: """
1381 + TLSCerts["localhost"].k8s_crt
1382 + """
1383 tls.key: """
1384 + TLSCerts["localhost"].k8s_key
1385 + """
1386---
1387apiVersion: getambassador.io/v3alpha1
1388kind: Listener
1389metadata:
1390 name: {self.path.k8s}
1391spec:
1392 ambassador_id: [{self.ambassador_id}]
1393 port: 8080
1394 protocol: HTTP
1395 securityModel: XFP
1396 l7Depth: 1
1397 hostBinding:
1398 namespace:
1399 from: ALL
1400---
1401apiVersion: getambassador.io/v3alpha1
1402kind: AuthService
1403metadata:
1404 name: {self.auth.path.k8s}
1405spec:
1406 ambassador_id: [ {self.ambassador_id} ]
1407 auth_service: "{self.auth.path.fqdn}"
1408 proto: http
1409 protocol_version: "v3"
1410---
1411apiVersion: getambassador.io/v3alpha1
1412kind: Host
1413metadata:
1414 name: {self.path.k8s}-host
1415 labels:
1416 kat-ambassador-id: {self.ambassador_id}
1417spec:
1418 ambassador_id: [ {self.ambassador_id} ]
1419 hostname: "*"
1420 acmeProvider:
1421 authority: none
1422 tlsSecret:
1423 name: {self.path.k8s}-secret
1424---
1425apiVersion: getambassador.io/v3alpha1
1426kind: Mapping
1427metadata:
1428 name: {self.target.path.k8s}
1429spec:
1430 ambassador_id: [{self.ambassador_id}]
1431 hostname: "*"
1432 prefix: /target/
1433 service: {self.target.path.fqdn}
1434 host_redirect: true
1435"""
1436 )
1437 + super().manifests()
1438 )
1439
1440 def requirements(self):
1441 # The client doesn't follow redirects so we must force checks to
1442 # match the XFP https route. The Listener is configured with
1443 # l7depth: 1 so that Envoy trusts the header XFP header forwarded
1444 # by the client.
1445 yield (
1446 "url",
1447 Query(self.url("ambassador/v0/check_ready"), headers={"X-Forwarded-Proto": "https"}),
1448 )
1449 yield (
1450 "url",
1451 Query(self.url("ambassador/v0/check_alive"), headers={"X-Forwarded-Proto": "https"}),
1452 )
1453
1454 def queries(self):
1455 # send http request
1456 yield Query(
1457 self.url("target/", scheme="http"), headers={"X-Forwarded-Proto": "http"}, expected=301
1458 )
1459
1460 # send https request
1461 yield Query(
1462 self.url("target/", scheme="https"),
1463 insecure=True,
1464 headers={"X-Forwarded-Proto": "https"},
1465 expected=301,
1466 )
1467
1468 def check(self):
1469 # we should NOT make a call to the backend service,
1470 # rather envoy should have redirected to https
1471 assert self.results[0].backend is None
1472 assert self.results[0].headers["Location"] == [f"https://{self.path.fqdn}/target/"]
1473
1474 assert self.results[1].backend is None
1475 assert self.results[1].headers["Location"] == [f"https://{self.target.path.fqdn}/target/"]
View as plain text