1from typing import Generator, Tuple, Union
2
3from abstract_tests import HTTP, AmbassadorTest, Node, ServiceType
4from ambassador import Config
5from kat.harness import EDGE_STACK, Query
6from tests.integration.manifests import namespace_manifest
7from tests.selfsigned import TLSCerts
8from tests.utils import create_crl_pem_b64
9
10# STILL TO ADD:
11# Host referencing a Secret in another namespace?
12# Mappings without host attributes (infer via Host resource)
13# Host where a TLSContext with the inferred name already exists
14
15bug_single_insecure_action = False # Do all Hosts have to have the same insecure.action?
16bug_forced_star = (
17 True # Do we erroneously send replies in cleartext instead of TLS for unknown hosts?
18)
19bug_404_routes = (
20 True # Do we erroneously send 404 responses directly instead of redirect-to-tls first?
21)
22bug_clientcert_reset = True # Do we sometimes just close the connection instead of sending back tls certificate_required?
23
24
25class HostCRDSingle(AmbassadorTest):
26 """
27 HostCRDSingle: a single Host with a manually-configured TLS. Since the Host is handling the
28 TLSContext, we expect both OSS and Edge Stack to redirect cleartext from 8080 to 8443 here.
29 """
30
31 target: ServiceType
32
33 def init(self):
34 self.edge_stack_cleartext_host = False
35 self.target = HTTP()
36
37 def manifests(self) -> str:
38 return (
39 self.format(
40 """
41---
42apiVersion: v1
43kind: Secret
44metadata:
45 name: {self.path.k8s}-secret
46 labels:
47 kat-ambassador-id: {self.ambassador_id}
48type: kubernetes.io/tls
49data:
50 tls.crt: """
51 + TLSCerts["localhost"].k8s_crt
52 + """
53 tls.key: """
54 + TLSCerts["localhost"].k8s_key
55 + """
56---
57apiVersion: getambassador.io/v3alpha1
58kind: Host
59metadata:
60 name: {self.path.k8s}-host
61 labels:
62 kat-ambassador-id: {self.ambassador_id}
63spec:
64 ambassador_id: [ {self.ambassador_id} ]
65 hostname: {self.path.fqdn}
66 acmeProvider:
67 authority: none
68 tlsSecret:
69 name: {self.path.k8s}-secret
70 mappingSelector:
71 matchLabels:
72 hostname: {self.path.fqdn}
73---
74apiVersion: getambassador.io/v3alpha1
75kind: Mapping
76metadata:
77 name: {self.path.k8s}-target-mapping
78 labels:
79 hostname: {self.path.fqdn}
80spec:
81 ambassador_id: [ {self.ambassador_id} ]
82 prefix: /target/
83 service: {self.target.path.fqdn}
84"""
85 )
86 + super().manifests()
87 )
88
89 def scheme(self) -> str:
90 return "https"
91
92 def queries(self):
93 yield Query(self.url("target/"), insecure=True)
94 yield Query(self.url("target/", scheme="http"), expected=301)
95
96
97class HostCRDNo8080(AmbassadorTest):
98 """
99 HostCRDNo8080: a single Host with manually-configured TLS that explicitly turns off redirection
100 from 8080.
101 """
102
103 target: ServiceType
104
105 def init(self):
106 self.edge_stack_cleartext_host = False
107 self.add_default_http_listener = False
108 self.add_default_https_listener = False
109 self.target = HTTP()
110
111 def manifests(self) -> str:
112 return (
113 self.format(
114 """
115---
116apiVersion: v1
117kind: Secret
118metadata:
119 name: {self.path.k8s}-secret
120 labels:
121 kat-ambassador-id: {self.ambassador_id}
122type: kubernetes.io/tls
123data:
124 tls.crt: """
125 + TLSCerts["localhost"].k8s_crt
126 + """
127 tls.key: """
128 + TLSCerts["localhost"].k8s_key
129 + """
130---
131apiVersion: getambassador.io/v3alpha1
132kind: Listener
133metadata:
134 name: {self.path.k8s}-listener
135 labels:
136 kat-ambassador-id: {self.ambassador_id}
137spec:
138 ambassador_id: [ {self.ambassador_id} ]
139 port: 8443
140 protocol: HTTPS
141 securityModel: XFP
142 hostBinding:
143 namespace:
144 from: ALL
145---
146apiVersion: getambassador.io/v3alpha1
147kind: Host
148metadata:
149 name: {self.path.k8s}-host
150 labels:
151 kat-ambassador-id: {self.ambassador_id}
152spec:
153 ambassador_id: [ {self.ambassador_id} ]
154 hostname: {self.path.fqdn}
155 acmeProvider:
156 authority: none
157 tlsSecret:
158 name: {self.path.k8s}-secret
159 mappingSelector:
160 matchLabels:
161 hostname: {self.path.fqdn}
162 requestPolicy:
163 insecure:
164 action: Reject
165---
166apiVersion: getambassador.io/v3alpha1
167kind: Mapping
168metadata:
169 name: {self.path.k8s}-target-mapping
170 labels:
171 hostname: {self.path.fqdn}
172spec:
173 ambassador_id: [ {self.ambassador_id} ]
174 prefix: /target/
175 service: {self.target.path.fqdn}
176"""
177 )
178 + super().manifests()
179 )
180
181 def scheme(self) -> str:
182 return "https"
183
184 def queries(self):
185 yield Query(self.url("target/"), insecure=True)
186 yield Query(self.url("target/", scheme="http"), error=["EOF", "connection refused"])
187
188
189class HostCRDManualContext(AmbassadorTest):
190 """
191 A single Host with a manually-specified TLS secret and a manually-specified TLSContext,
192 too.
193 """
194
195 target: ServiceType
196
197 def init(self):
198 self.edge_stack_cleartext_host = False
199
200 self.target = HTTP()
201
202 def manifests(self) -> str:
203 return (
204 self.format(
205 """
206---
207apiVersion: v1
208kind: Secret
209metadata:
210 name: {self.path.k8s}-manual-secret
211 labels:
212 kat-ambassador-id: {self.ambassador_id}
213type: kubernetes.io/tls
214data:
215 tls.crt: """
216 + TLSCerts["localhost"].k8s_crt
217 + """
218 tls.key: """
219 + TLSCerts["localhost"].k8s_key
220 + """
221---
222apiVersion: getambassador.io/v3alpha1
223kind: Host
224metadata:
225 name: {self.path.k8s}-manual-host
226 labels:
227 kat-ambassador-id: {self.ambassador_id}
228spec:
229 ambassador_id: [ {self.ambassador_id} ]
230 hostname: {self.path.fqdn}
231 acmeProvider:
232 authority: none
233 mappingSelector:
234 matchLabels:
235 hostname: {self.path.k8s}-manual-hostname
236 tlsSecret:
237 name: {self.path.k8s}-manual-secret
238---
239apiVersion: getambassador.io/v3alpha1
240kind: TLSContext
241metadata:
242 name: {self.path.k8s}-manual-host-context
243 labels:
244 kat-ambassador-id: {self.ambassador_id}
245spec:
246 ambassador_id: [ {self.ambassador_id} ]
247 hosts:
248 - {self.path.fqdn}
249 secret: {self.path.k8s}-manual-secret
250 min_tls_version: v1.2
251 max_tls_version: v1.3
252---
253apiVersion: getambassador.io/v3alpha1
254kind: Mapping
255metadata:
256 name: {self.path.k8s}-target-mapping
257 labels:
258 hostname: {self.path.k8s}-manual-hostname
259spec:
260 ambassador_id: [ {self.ambassador_id} ]
261 prefix: /target/
262 service: {self.target.path.fqdn}
263"""
264 )
265 + super().manifests()
266 )
267
268 def scheme(self) -> str:
269 return "https"
270
271 def queries(self):
272 yield Query(self.url("target/tls-1.2-1.3"), insecure=True, minTLSv="v1.2", maxTLSv="v1.3")
273
274 yield Query(
275 self.url("target/tls-1.0-1.0"),
276 insecure=True,
277 minTLSv="v1.0",
278 maxTLSv="v1.0",
279 error=[
280 "tls: server selected unsupported protocol version 303",
281 "tls: no supported versions satisfy MinVersion and MaxVersion",
282 "tls: protocol version not supported",
283 ],
284 )
285
286 yield Query(self.url("target/cleartext", scheme="http"), expected=301)
287
288
289class HostCRDManualContextCRL(AmbassadorTest):
290 """
291 A single Host with a manually-specified TLS secret, a manually-specified TLSContext and
292 a manually specified mTLS config with CRL list too.
293 """
294
295 target: ServiceType
296
297 def init(self):
298 if Config.envoy_api_version == "V2":
299 self.skip_node = True
300 self.add_default_http_listener = False
301 self.add_default_https_listener = False
302
303 self.target = HTTP()
304
305 def manifests(self) -> str:
306 return (
307 self.format(
308 """
309---
310apiVersion: getambassador.io/v3alpha1
311kind: Listener
312metadata:
313 name: {self.path.k8s}-listener
314 labels:
315 kat-ambassador-id: {self.ambassador_id}
316spec:
317 ambassador_id: [ {self.ambassador_id} ]
318 port: 8443
319 protocol: HTTPS
320 securityModel: XFP
321 hostBinding:
322 namespace:
323 from: SELF
324---
325apiVersion: v1
326kind: Secret
327metadata:
328 name: {self.path.k8s}-server-manual-crl-secret
329 labels:
330 kat-ambassador-id: {self.ambassador_id}
331type: kubernetes.io/tls
332data:
333 tls.crt: """
334 + TLSCerts["ambassador.example.com"].k8s_crt
335 + """
336 tls.key: """
337 + TLSCerts["ambassador.example.com"].k8s_key
338 + """
339---
340apiVersion: v1
341kind: Secret
342metadata:
343 name: {self.path.k8s}-ca-manual-crl-secret
344 labels:
345 kat-ambassador-id: {self.ambassador_id}
346type: kubernetes.io/tls
347data:
348 tls.crt: """
349 + TLSCerts["master.datawire.io"].k8s_crt
350 + """
351 tls.key: ""
352---
353apiVersion: v1
354kind: Secret
355metadata:
356 name: {self.path.k8s}-crl-manual-crl-secret
357 labels:
358 kat-ambassador-id: {self.ambassador_id}
359type: Opaque
360data:
361 crl.pem: """
362 + create_crl_pem_b64(
363 TLSCerts["master.datawire.io"].pubcert,
364 TLSCerts["master.datawire.io"].privkey,
365 [TLSCerts["presto.example.com"].pubcert],
366 )
367 + """
368---
369apiVersion: getambassador.io/v3alpha1
370kind: Host
371metadata:
372 name: {self.path.k8s}-manual-crl-host
373 labels:
374 kat-ambassador-id: {self.ambassador_id}
375spec:
376 ambassador_id: [ {self.ambassador_id} ]
377 hostname: ambassador.example.com
378 acmeProvider:
379 authority: none
380 mappingSelector:
381 matchLabels:
382 hostname: {self.path.k8s}-manual-crl-hostname
383 tlsSecret:
384 name: {self.path.k8s}-server-manual-crl-secret
385---
386apiVersion: getambassador.io/v3alpha1
387kind: TLSContext
388metadata:
389 name: {self.path.k8s}-manual-crl-host-context
390 labels:
391 kat-ambassador-id: {self.ambassador_id}
392spec:
393 ambassador_id: [ {self.ambassador_id} ]
394 hosts:
395 - ambassador.example.com
396 ca_secret: {self.path.k8s}-ca-manual-crl-secret
397 secret: {self.path.k8s}-server-manual-crl-secret
398 cert_required: true
399 crl_secret: {self.path.k8s}-crl-manual-crl-secret
400---
401apiVersion: getambassador.io/v3alpha1
402kind: Mapping
403metadata:
404 name: {self.path.k8s}-target-mapping
405 labels:
406 hostname: {self.path.k8s}-manual-crl-hostname
407spec:
408 ambassador_id: [ {self.ambassador_id} ]
409 hostname: ambassador.example.com
410 prefix: /
411 service: {self.target.path.fqdn}
412"""
413 )
414 + super().manifests()
415 )
416
417 def scheme(self) -> str:
418 return "https"
419
420 def queries(self):
421 base = {
422 "url": self.url(""),
423 "ca_cert": TLSCerts["master.datawire.io"].pubcert,
424 "headers": {"Host": "ambassador.example.com"},
425 "sni": True, # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
426 }
427
428 yield Query(**base, error="tls: certificate required")
429
430 yield Query(
431 **base,
432 client_crt=TLSCerts["presto.example.com"].pubcert,
433 client_key=TLSCerts["presto.example.com"].privkey,
434 error="tls: revoked certificate"
435 )
436
437 def requirements(self):
438 yield ("pod", self.path.k8s)
439
440
441class HostCRDSeparateTLSContext(AmbassadorTest):
442 target: ServiceType
443
444 def init(self):
445 self.edge_stack_cleartext_host = False
446 self.target = HTTP()
447
448 def manifests(self) -> str:
449 return (
450 self.format(
451 """
452---
453apiVersion: v1
454kind: Secret
455metadata:
456 name: {self.path.k8s}-secret
457 labels:
458 kat-ambassador-id: {self.ambassador_id}
459type: kubernetes.io/tls
460data:
461 tls.crt: """
462 + TLSCerts["localhost"].k8s_crt
463 + """
464 tls.key: """
465 + TLSCerts["localhost"].k8s_key
466 + """
467---
468apiVersion: getambassador.io/v3alpha1
469kind: Host
470metadata:
471 name: {self.path.k8s}-manual-host-separate
472 labels:
473 kat-ambassador-id: {self.ambassador_id}
474spec:
475 ambassador_id: [ {self.ambassador_id} ]
476 hostname: {self.path.fqdn}
477 acmeProvider:
478 authority: none
479 mappingSelector:
480 matchLabels:
481 hostname: {self.path.fqdn}
482 tlsSecret:
483 name: {self.path.k8s}-secret
484 tlsContext:
485 name: {self.path.k8s}-separate-tls-context
486---
487apiVersion: getambassador.io/v3alpha1
488kind: TLSContext
489metadata:
490 name: {self.path.k8s}-separate-tls-context
491 labels:
492 kat-ambassador-id: {self.ambassador_id}
493spec:
494 ambassador_id: [ {self.ambassador_id} ]
495 secret: {self.path.k8s}-secret
496 min_tls_version: v1.2
497 max_tls_version: v1.3
498---
499apiVersion: getambassador.io/v3alpha1
500kind: Mapping
501metadata:
502 name: {self.path.k8s}-target-mapping-separate
503 labels:
504 hostname: {self.path.fqdn}
505spec:
506 ambassador_id: [ {self.ambassador_id} ]
507 prefix: /target/
508 service: {self.target.path.fqdn}
509"""
510 )
511 + super().manifests()
512 )
513
514 def scheme(self) -> str:
515 return "https"
516
517 def queries(self):
518 yield Query(self.url("target/"), insecure=True, minTLSv="v1.2", maxTLSv="v1.3")
519
520 yield Query(
521 self.url("target/"),
522 insecure=True,
523 minTLSv="v1.0",
524 maxTLSv="v1.0",
525 error=[
526 "tls: server selected unsupported protocol version 303",
527 "tls: no supported versions satisfy MinVersion and MaxVersion",
528 "tls: protocol version not supported",
529 ],
530 )
531
532
533class HostCRDTLSConfig(AmbassadorTest):
534 target: ServiceType
535
536 def init(self):
537 self.edge_stack_cleartext_host = False
538 self.target = HTTP()
539
540 def manifests(self) -> str:
541 return (
542 self.format(
543 """
544---
545apiVersion: v1
546kind: Secret
547metadata:
548 name: {self.path.k8s}-secret
549 labels:
550 kat-ambassador-id: {self.ambassador_id}
551type: kubernetes.io/tls
552data:
553 tls.crt: """
554 + TLSCerts["localhost"].k8s_crt
555 + """
556 tls.key: """
557 + TLSCerts["localhost"].k8s_key
558 + """
559---
560apiVersion: getambassador.io/v3alpha1
561kind: Host
562metadata:
563 name: {self.path.k8s}-manual-host-tls
564 labels:
565 kat-ambassador-id: {self.ambassador_id}
566spec:
567 ambassador_id: [ {self.ambassador_id} ]
568 hostname: {self.path.fqdn}
569 acmeProvider:
570 authority: none
571 mappingSelector:
572 matchLabels:
573 hostname: {self.path.fqdn}
574 tlsSecret:
575 name: {self.path.k8s}-secret
576 tls:
577 min_tls_version: v1.2
578 max_tls_version: v1.3
579---
580apiVersion: getambassador.io/v3alpha1
581kind: Mapping
582metadata:
583 name: {self.path.k8s}-target-mapping
584 labels:
585 hostname: {self.path.fqdn}
586spec:
587 ambassador_id: [ {self.ambassador_id} ]
588 prefix: /target/
589 service: {self.target.path.fqdn}
590"""
591 )
592 + super().manifests()
593 )
594
595 def scheme(self) -> str:
596 return "https"
597
598 def queries(self):
599 yield Query(self.url("target/"), insecure=True, minTLSv="v1.2", maxTLSv="v1.3")
600
601 yield Query(
602 self.url("target/"),
603 insecure=True,
604 minTLSv="v1.0",
605 maxTLSv="v1.0",
606 error=[
607 "tls: server selected unsupported protocol version 303",
608 "tls: no supported versions satisfy MinVersion and MaxVersion",
609 "tls: protocol version not supported",
610 ],
611 )
612
613
614class HostCRDClearText(AmbassadorTest):
615 """
616 A single Host specifying cleartext only. Since it's just cleartext, no redirection comes
617 into play.
618 """
619
620 target: ServiceType
621
622 def init(self):
623 self.edge_stack_cleartext_host = False
624
625 # Only add the default HTTP listener (we're mimicking the no-TLS case here.)
626 self.add_default_http_listener = True
627 self.add_default_https_listener = False
628
629 self.target = HTTP()
630
631 def manifests(self) -> str:
632 return (
633 self.format(
634 """
635---
636apiVersion: getambassador.io/v3alpha1
637kind: Host
638metadata:
639 name: {self.path.k8s}-cleartext-host
640 labels:
641 kat-ambassador-id: {self.ambassador_id}
642spec:
643 ambassador_id: [ {self.ambassador_id} ]
644 hostname: {self.path.fqdn}
645 acmeProvider:
646 authority: none
647 mappingSelector:
648 matchLabels:
649 hostname: {self.path.k8s}-host-cleartext
650 requestPolicy:
651 insecure:
652 action: Route
653---
654apiVersion: getambassador.io/v3alpha1
655kind: Mapping
656metadata:
657 name: {self.path.k8s}-cleartext-target-mapping
658 labels:
659 hostname: {self.path.k8s}-host-cleartext
660spec:
661 ambassador_id: [ {self.ambassador_id} ]
662 prefix: /target/
663 service: {self.target.path.fqdn}
664"""
665 )
666 + super().manifests()
667 )
668
669 def scheme(self) -> str:
670 return "http"
671
672 def queries(self):
673 yield Query(self.url("target/"), insecure=True)
674 yield Query(self.url("target/", scheme="https"), error=["EOF", "connection refused"])
675
676
677class HostCRDDouble(AmbassadorTest):
678 """
679 HostCRDDouble: "double" is actually a misnomer. We have multiple Hosts, each with a
680 manually-configured TLS secrets, and varying insecure actions:
681 - tls-context-host-1: Route
682 - tls-context-host-2: Redirect
683 - tls-context-host-3: Reject
684
685 We also have Mappings that specify Host matches, and we test the various combinations.
686
687 XXX In the future, the hostname matches should be unnecessary, as it should use
688 metadata.labels.hostname.
689 """
690
691 target1: ServiceType
692 target2: ServiceType
693 target3: ServiceType
694 targetshared: ServiceType
695
696 def init(self):
697 self.edge_stack_cleartext_host = False
698 self.target1 = HTTP(name="target1")
699 self.target2 = HTTP(name="target2")
700 self.target3 = HTTP(name="target3")
701 self.targetshared = HTTP(name="targetshared")
702
703 def manifests(self) -> str:
704 return (
705 self.format(
706 """
707---
708apiVersion: getambassador.io/v3alpha1
709kind: Host
710metadata:
711 name: {self.path.k8s}-host-1
712 labels:
713 kat-ambassador-id: {self.ambassador_id}
714spec:
715 ambassador_id: [ {self.ambassador_id} ]
716 hostname: tls-context-host-1
717 acmeProvider:
718 authority: none
719 mappingSelector:
720 matchLabels:
721 hostname: tls-context-host-1
722 tlsSecret:
723 name: {self.path.k8s}-test-tlscontext-secret-1
724 requestPolicy:
725 insecure:
726 action: Route
727---
728apiVersion: v1
729kind: Secret
730metadata:
731 name: {self.path.k8s}-test-tlscontext-secret-1
732 labels:
733 kat-ambassador-id: {self.ambassador_id}
734type: kubernetes.io/tls
735data:
736 tls.crt: """
737 + TLSCerts["tls-context-host-1"].k8s_crt
738 + """
739 tls.key: """
740 + TLSCerts["tls-context-host-1"].k8s_key
741 + """
742---
743apiVersion: getambassador.io/v3alpha1
744kind: Mapping
745metadata:
746 name: {self.path.k8s}-host-1-mapping
747 labels:
748 hostname: tls-context-host-1
749 kat-ambassador-id: {self.ambassador_id}
750spec:
751 ambassador_id: [ {self.ambassador_id} ]
752 host: "tls-context-host-1"
753 prefix: /target-1/
754 service: {self.target1.path.fqdn}
755
756---
757apiVersion: getambassador.io/v3alpha1
758kind: Host
759metadata:
760 name: {self.path.k8s}-host-2
761 labels:
762 kat-ambassador-id: {self.ambassador_id}
763spec:
764 ambassador_id: [ {self.ambassador_id} ]
765 hostname: tls-context-host-2
766 acmeProvider:
767 authority: none
768 tlsSecret:
769 name: {self.path.k8s}-test-tlscontext-secret-2
770 requestPolicy:
771 insecure:
772 action: Redirect
773---
774apiVersion: v1
775kind: Secret
776metadata:
777 name: {self.path.k8s}-test-tlscontext-secret-2
778 labels:
779 kat-ambassador-id: {self.ambassador_id}
780type: kubernetes.io/tls
781data:
782 tls.crt: """
783 + TLSCerts["tls-context-host-2"].k8s_crt
784 + """
785 tls.key: """
786 + TLSCerts["tls-context-host-2"].k8s_key
787 + """
788---
789apiVersion: getambassador.io/v3alpha1
790kind: Mapping
791metadata:
792 name: {self.path.k8s}-host-2-mapping
793 labels:
794 hostname: tls-context-host-2
795 kat-ambassador-id: {self.ambassador_id}
796spec:
797 ambassador_id: [ {self.ambassador_id} ]
798 host: "tls-context-host-2"
799 prefix: /target-2/
800 service: {self.target2.path.fqdn}
801
802---
803apiVersion: getambassador.io/v3alpha1
804kind: Host
805metadata:
806 name: {self.path.k8s}-host-3
807 labels:
808 kat-ambassador-id: {self.ambassador_id}
809spec:
810 ambassador_id: [ {self.ambassador_id} ]
811 hostname: ambassador.example.com
812 acmeProvider:
813 authority: none
814 mappingSelector:
815 matchLabels:
816 hostname: ambassador.example.com
817 tlsSecret:
818 name: {self.path.k8s}-test-tlscontext-secret-3
819 requestPolicy:
820 insecure:
821 action: Reject
822---
823apiVersion: v1
824kind: Secret
825metadata:
826 name: {self.path.k8s}-test-tlscontext-secret-3
827 labels:
828 kat-ambassador-id: {self.ambassador_id}
829type: kubernetes.io/tls
830data:
831 tls.crt: """
832 + TLSCerts["ambassador.example.com"].k8s_crt
833 + """
834 tls.key: """
835 + TLSCerts["ambassador.example.com"].k8s_key
836 + """
837---
838apiVersion: getambassador.io/v3alpha1
839kind: Mapping
840metadata:
841 name: {self.path.k8s}-host-3-mapping
842 labels:
843 hostname: ambassador.example.com
844 kat-ambassador-id: {self.ambassador_id}
845spec:
846 ambassador_id: [ {self.ambassador_id} ]
847 host: "ambassador.example.com"
848 prefix: /target-3/
849 service: {self.target3.path.fqdn}
850---
851# Add a bogus ACME mapping so that we can distinguish "invalid
852# challenge" from "rejected".
853apiVersion: getambassador.io/v3alpha1
854kind: Mapping
855metadata:
856 name: {self.path.k8s}-host-3-acme
857 labels:
858 hostname: ambassador.example.com
859 kat-ambassador-id: {self.ambassador_id}
860spec:
861 ambassador_id: [ {self.ambassador_id} ]
862 host: "ambassador.example.com"
863 prefix: /.well-known/acme-challenge/
864 service: {self.target3.path.fqdn}
865
866---
867apiVersion: getambassador.io/v3alpha1
868kind: Mapping
869metadata:
870 name: {self.path.k8s}-host-shared-mapping
871 labels:
872 kat-ambassador-id: {self.ambassador_id}
873spec:
874 ambassador_id: [ {self.ambassador_id} ]
875 hostname: "*"
876 prefix: /target-shared/
877 service: {self.targetshared.path.fqdn}
878"""
879 )
880 + super().manifests()
881 )
882
883 def scheme(self) -> str:
884 return "https"
885
886 def queries(self):
887 # 0: Get some info from diagd for self.check() to inspect
888 yield Query(
889 self.url("ambassador/v0/diag/?json=true&filter=errors"),
890 headers={"Host": "tls-context-host-1"},
891 insecure=True,
892 sni=True,
893 )
894
895 # 1-5: Host #1 - TLS
896 yield Query(
897 self.url("target-1/", scheme="https"),
898 headers={"Host": "tls-context-host-1"},
899 insecure=True,
900 sni=True,
901 expected=200,
902 )
903 yield Query(
904 self.url("target-2/", scheme="https"),
905 headers={"Host": "tls-context-host-1"},
906 insecure=True,
907 sni=True,
908 expected=404,
909 )
910 yield Query(
911 self.url("target-3/", scheme="https"),
912 headers={"Host": "tls-context-host-1"},
913 insecure=True,
914 sni=True,
915 expected=404,
916 )
917 yield Query(
918 self.url("target-shared/", scheme="https"),
919 headers={"Host": "tls-context-host-1"},
920 insecure=True,
921 sni=True,
922 expected=200,
923 )
924 yield Query(
925 self.url(".well-known/acme-challenge/foo", scheme="https"),
926 headers={"Host": "tls-context-host-1"},
927 insecure=True,
928 sni=True,
929 expected=404,
930 )
931 # 6-10: Host #1 - cleartext (action: Route)
932 yield Query(
933 self.url("target-1/", scheme="http"),
934 headers={"Host": "tls-context-host-1"},
935 expected=200,
936 )
937 yield Query(
938 self.url("target-2/", scheme="http"),
939 headers={"Host": "tls-context-host-1"},
940 expected=404,
941 )
942 yield Query(
943 self.url("target-3/", scheme="http"),
944 headers={"Host": "tls-context-host-1"},
945 expected=404,
946 )
947 yield Query(
948 self.url("target-shared/", scheme="http"),
949 headers={"Host": "tls-context-host-1"},
950 expected=200,
951 )
952 yield Query(
953 self.url(".well-known/acme-challenge/foo", scheme="http"),
954 headers={"Host": "tls-context-host-1"},
955 expected=404,
956 )
957
958 # 11-15: Host #2 - TLS
959 yield Query(
960 self.url("target-1/", scheme="https"),
961 headers={"Host": "tls-context-host-2"},
962 insecure=True,
963 sni=True,
964 expected=404,
965 )
966 yield Query(
967 self.url("target-2/", scheme="https"),
968 headers={"Host": "tls-context-host-2"},
969 insecure=True,
970 sni=True,
971 expected=200,
972 )
973 yield Query(
974 self.url("target-3/", scheme="https"),
975 headers={"Host": "tls-context-host-2"},
976 insecure=True,
977 sni=True,
978 expected=404,
979 )
980 yield Query(
981 self.url("target-shared/", scheme="https"),
982 headers={"Host": "tls-context-host-2"},
983 insecure=True,
984 sni=True,
985 expected=200,
986 )
987 yield Query(
988 self.url(".well-known/acme-challenge/foo", scheme="https"),
989 headers={"Host": "tls-context-host-2"},
990 insecure=True,
991 sni=True,
992 expected=404,
993 )
994 # 16-20: Host #2 - cleartext (action: Redirect)
995 yield Query(
996 self.url("target-1/", scheme="http"),
997 headers={"Host": "tls-context-host-2"},
998 expected=404,
999 )
1000 yield Query(
1001 self.url("target-2/", scheme="http"),
1002 headers={"Host": "tls-context-host-2"},
1003 expected=301,
1004 )
1005 yield Query(
1006 self.url("target-3/", scheme="http"),
1007 headers={"Host": "tls-context-host-2"},
1008 expected=404,
1009 )
1010 yield Query(
1011 self.url("target-shared/", scheme="http"),
1012 headers={"Host": "tls-context-host-2"},
1013 expected=301,
1014 )
1015 yield Query(
1016 self.url(".well-known/acme-challenge/foo", scheme="http"),
1017 headers={"Host": "tls-context-host-2"},
1018 expected=404,
1019 )
1020
1021 # 21-25: Host #3 - TLS
1022 yield Query(
1023 self.url("target-1/", scheme="https"),
1024 headers={"Host": "ambassador.example.com"},
1025 insecure=True,
1026 sni=True,
1027 expected=404,
1028 )
1029 yield Query(
1030 self.url("target-2/", scheme="https"),
1031 headers={"Host": "ambassador.example.com"},
1032 insecure=True,
1033 sni=True,
1034 expected=404,
1035 )
1036 yield Query(
1037 self.url("target-3/", scheme="https"),
1038 headers={"Host": "ambassador.example.com"},
1039 insecure=True,
1040 sni=True,
1041 expected=200,
1042 )
1043 yield Query(
1044 self.url("target-shared/", scheme="https"),
1045 headers={"Host": "ambassador.example.com"},
1046 insecure=True,
1047 sni=True,
1048 expected=200,
1049 )
1050 yield Query(
1051 self.url(".well-known/acme-challenge/foo", scheme="https"),
1052 headers={"Host": "ambassador.example.com"},
1053 insecure=True,
1054 sni=True,
1055 expected=200,
1056 )
1057 # 26-30: Host #3 - cleartext (action: Reject)
1058 yield Query(
1059 self.url("target-1/", scheme="http"),
1060 headers={"Host": "ambassador.example.com"},
1061 expected=404,
1062 )
1063 yield Query(
1064 self.url("target-2/", scheme="http"),
1065 headers={"Host": "ambassador.example.com"},
1066 expected=404,
1067 )
1068 yield Query(
1069 self.url("target-3/", scheme="http"),
1070 headers={"Host": "ambassador.example.com"},
1071 expected=404,
1072 )
1073 yield Query(
1074 self.url("target-shared/", scheme="http"),
1075 headers={"Host": "ambassador.example.com"},
1076 expected=404,
1077 )
1078 yield Query(
1079 self.url(".well-known/acme-challenge/foo", scheme="http"),
1080 headers={"Host": "ambassador.example.com"},
1081 expected=200,
1082 )
1083
1084 def check(self):
1085 # XXX Ew. If self.results[0].json is empty, the harness won't convert it to a response.
1086 errors = self.results[0].json or []
1087 num_errors = len(errors)
1088 assert num_errors == 0, "expected 0 errors, got {} -\n{}".format(num_errors, errors)
1089
1090 idx = 0
1091
1092 for result in self.results:
1093 if result.status == 200 and result.query.headers and result.tls:
1094 host_header = result.query.headers["Host"]
1095 tls_common_name = result.tls[0]["Subject"]["CommonName"]
1096
1097 assert host_header == tls_common_name, "test %d wanted CN %s, but got %s" % (
1098 idx,
1099 host_header,
1100 tls_common_name,
1101 )
1102
1103 idx += 1
1104
1105 def requirements(self):
1106 # We're replacing super()'s requirements deliberately here. Without a Host header they can't work.
1107 yield (
1108 "url",
1109 Query(
1110 self.url("ambassador/v0/check_ready"),
1111 headers={"Host": "tls-context-host-1"},
1112 insecure=True,
1113 sni=True,
1114 ),
1115 )
1116 yield (
1117 "url",
1118 Query(
1119 self.url("ambassador/v0/check_alive"),
1120 headers={"Host": "tls-context-host-1"},
1121 insecure=True,
1122 sni=True,
1123 ),
1124 )
1125 yield (
1126 "url",
1127 Query(
1128 self.url("ambassador/v0/check_ready"),
1129 headers={"Host": "tls-context-host-2"},
1130 insecure=True,
1131 sni=True,
1132 ),
1133 )
1134 yield (
1135 "url",
1136 Query(
1137 self.url("ambassador/v0/check_alive"),
1138 headers={"Host": "tls-context-host-2"},
1139 insecure=True,
1140 sni=True,
1141 ),
1142 )
1143
1144
1145class HostCRDWildcards(AmbassadorTest):
1146 """This test could be expanded to include more scenarios, like testing
1147 handling of precedence between suffix-match host globs and
1148 prefix-match host-globs. But this is a solid start.
1149
1150 """
1151
1152 target: ServiceType
1153
1154 def init(self):
1155 self.target = HTTP()
1156
1157 def manifests(self) -> str:
1158 return (
1159 self.format(
1160 """
1161---
1162apiVersion: getambassador.io/v3alpha1
1163kind: Host
1164metadata:
1165 name: {self.path.k8s}-wc
1166 labels:
1167 kat-ambassador-id: {self.ambassador_id}
1168spec:
1169 ambassador_id: [ {self.ambassador_id} ]
1170 hostname: "*"
1171 acmeProvider:
1172 authority: none
1173 tlsSecret:
1174 name: {self.path.k8s}-tls
1175---
1176apiVersion: getambassador.io/v3alpha1
1177kind: Host
1178metadata:
1179 name: {self.path.k8s}-wc.domain.com
1180 labels:
1181 kat-ambassador-id: {self.ambassador_id}
1182spec:
1183 ambassador_id: [ {self.ambassador_id} ]
1184 hostname: "*.domain.com"
1185 acmeProvider:
1186 authority: none
1187 tlsSecret:
1188 name: {self.path.k8s}-tls
1189 requestPolicy:
1190 insecure:
1191 action: Route
1192---
1193apiVersion: getambassador.io/v3alpha1
1194kind: Host
1195metadata:
1196 name: {self.path.k8s}-a.domain.com
1197 labels:
1198 kat-ambassador-id: {self.ambassador_id}
1199spec:
1200 ambassador_id: [ {self.ambassador_id} ]
1201 hostname: "a.domain.com"
1202 acmeProvider:
1203 authority: none
1204 tlsSecret:
1205 name: {self.path.k8s}-tls
1206---
1207apiVersion: v1
1208kind: Secret
1209metadata:
1210 name: {self.path.k8s}-tls
1211 labels:
1212 kat-ambassador-id: {self.ambassador_id}
1213type: kubernetes.io/tls
1214data:
1215 tls.crt: """
1216 + TLSCerts["a.domain.com"].k8s_crt
1217 + """
1218 tls.key: """
1219 + TLSCerts["a.domain.com"].k8s_key
1220 + """
1221---
1222apiVersion: getambassador.io/v3alpha1
1223kind: Mapping
1224metadata:
1225 name: {self.path.k8s}
1226 labels:
1227 kat-ambassador-id: {self.ambassador_id}
1228spec:
1229 ambassador_id: [ {self.ambassador_id} ]
1230 hostname: "*"
1231 prefix: /foo/
1232 service: {self.target.path.fqdn}
1233"""
1234 )
1235 + super().manifests()
1236 )
1237
1238 def insecure(self, suffix):
1239 return {
1240 "url": self.url("foo/%s" % suffix, scheme="http"),
1241 }
1242
1243 def secure(self, suffix):
1244 return {
1245 "url": self.url("foo/%s" % suffix, scheme="https"),
1246 "ca_cert": TLSCerts["*.domain.com"].pubcert,
1247 "sni": True,
1248 }
1249
1250 def queries(self):
1251 yield Query(
1252 **self.secure("0-200"), headers={"Host": "a.domain.com"}, expected=200
1253 ) # Host=a.domain.com
1254 yield Query(
1255 **self.secure("1-200"), headers={"Host": "wc.domain.com"}, expected=200
1256 ) # Host=*.domain.com
1257 yield Query(**self.secure("2-200"), headers={"Host": "127.0.0.1"}, expected=200) # Host=*
1258
1259 yield Query(
1260 **self.insecure("3-301"), headers={"Host": "a.domain.com"}, expected=301
1261 ) # Host=a.domain.com
1262 yield Query(
1263 **self.insecure("4-200"), headers={"Host": "wc.domain.com"}, expected=200
1264 ) # Host=*.domain.com
1265 yield Query(**self.insecure("5-301"), headers={"Host": "127.0.0.1"}, expected=301) # Host=*
1266
1267 def scheme(self) -> str:
1268 return "https"
1269
1270 def requirements(self):
1271 for r in super().requirements():
1272 query = r[1]
1273 query.headers = {"Host": "127.0.0.1"}
1274 query.sni = (
1275 True # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
1276 )
1277 query.ca_cert = TLSCerts["*.domain.com"].pubcert
1278 yield (r[0], query)
1279
1280
1281class HostCRDClientCertCrossNamespace(AmbassadorTest):
1282 target: ServiceType
1283
1284 def init(self):
1285 self.target = HTTP()
1286
1287 def manifests(self) -> str:
1288 # All of the things referenced from a Host have a '.' in their
1289 # name, to make sure that Ambassador is correctly interpreting
1290 # the '.' as a namespace-separator (or not). Because most of
1291 # the references are core.v1.LocalObjectReferences, the '.' is
1292 # not taken as a namespace-separator, but it is for the
1293 # tls.ca_secret. And for ca_secret we still put the '.' in
1294 # the name so that we check that it's choosing the correct '.'
1295 # as the separator.
1296 return (
1297 namespace_manifest("alt-namespace")
1298 + self.format(
1299 """
1300---
1301apiVersion: getambassador.io/v3alpha1
1302kind: Host
1303metadata:
1304 name: {self.path.k8s}
1305 labels:
1306 kat-ambassador-id: {self.ambassador_id}
1307spec:
1308 ambassador_id: [ {self.ambassador_id} ]
1309 hostname: ambassador.example.com
1310 acmeProvider:
1311 authority: none
1312 tlsSecret:
1313 name: {self.path.k8s}.server
1314 tls:
1315 # ca_secret supports cross-namespace references, so test it
1316 ca_secret: {self.path.k8s}.ca.alt-namespace
1317 cert_required: true
1318---
1319apiVersion: v1
1320kind: Secret
1321metadata:
1322 name: {self.path.k8s}.ca
1323 namespace: alt-namespace
1324 labels:
1325 kat-ambassador-id: {self.ambassador_id}
1326type: kubernetes.io/tls
1327data:
1328 tls.crt: """
1329 + TLSCerts["master.datawire.io"].k8s_crt
1330 + """
1331 tls.key: ""
1332---
1333apiVersion: v1
1334kind: Secret
1335metadata:
1336 name: {self.path.k8s}.server
1337 labels:
1338 kat-ambassador-id: {self.ambassador_id}
1339type: kubernetes.io/tls
1340data:
1341 tls.crt: """
1342 + TLSCerts["ambassador.example.com"].k8s_crt
1343 + """
1344 tls.key: """
1345 + TLSCerts["ambassador.example.com"].k8s_key
1346 + """
1347---
1348apiVersion: getambassador.io/v3alpha1
1349kind: Mapping
1350metadata:
1351 name: {self.path.k8s}
1352 labels:
1353 kat-ambassador-id: {self.ambassador_id}
1354spec:
1355 ambassador_id: [ {self.ambassador_id} ]
1356 hostname: "*"
1357 prefix: /
1358 service: {self.target.path.fqdn}
1359"""
1360 )
1361 + super().manifests()
1362 )
1363
1364 def scheme(self) -> str:
1365 return "https"
1366
1367 def queries(self):
1368 base = {
1369 "url": self.url(""),
1370 "ca_cert": TLSCerts["master.datawire.io"].pubcert,
1371 "headers": {"Host": "ambassador.example.com"},
1372 "sni": True, # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
1373 }
1374
1375 yield Query(
1376 **base,
1377 client_crt=TLSCerts["presto.example.com"].pubcert,
1378 client_key=TLSCerts["presto.example.com"].privkey
1379 )
1380
1381 # Check that it requires the client cert.
1382 #
1383 # In TLS < 1.3, there's not a dedicated alert code for "the client forgot to include a certificate",
1384 # so we get a generic alert=40 ("handshake_failure").
1385 yield Query(**base, maxTLSv="v1.2", error="tls: handshake failure")
1386 # TLS 1.3 added a dedicated alert=116 ("certificate_required") for that scenario.
1387 yield Query(
1388 **base,
1389 minTLSv="v1.3",
1390 error=(
1391 ["tls: certificate required"]
1392 + (
1393 ["write: connection reset by peer", "write: broken pipe"]
1394 if bug_clientcert_reset
1395 else []
1396 )
1397 )
1398 )
1399
1400 # Check that it's validating the client cert against the CA cert.
1401 yield Query(
1402 **base,
1403 client_crt=TLSCerts["localhost"].pubcert,
1404 client_key=TLSCerts["localhost"].privkey,
1405 maxTLSv="v1.2",
1406 error="tls: handshake failure"
1407 )
1408
1409 def requirements(self):
1410 for r in super().requirements():
1411 query = r[1]
1412 query.headers = {"Host": "ambassador.example.com"}
1413 query.sni = (
1414 True # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
1415 )
1416 query.ca_cert = TLSCerts["master.datawire.io"].pubcert
1417 query.client_cert = TLSCerts["presto.example.com"].pubcert
1418 query.client_key = TLSCerts["presto.example.com"].privkey
1419 yield (r[0], query)
1420
1421
1422class HostCRDClientCertSameNamespace(AmbassadorTest):
1423 target: ServiceType
1424
1425 def init(self):
1426 self.target = HTTP()
1427 self.add_default_http_listener = False
1428 self.add_default_https_listener = False
1429
1430 def manifests(self) -> str:
1431 # Same as HostCRDClientCertCrossNamespace, all of the things
1432 # referenced by a Host have a '.' in their name; except
1433 # (unlike HostCRDClientCertCrossNamespace) the ca_secret
1434 # doesn't, so that we can check that it chooses the correct
1435 # namespace when a ".{namespace}" suffix isn't specified.
1436 return (
1437 namespace_manifest("alt2-namespace")
1438 + self.format(
1439 """
1440---
1441apiVersion: getambassador.io/v3alpha1
1442kind: Listener
1443metadata:
1444 name: ambassador-listener-8443 # This name is to match existing test stuff
1445 namespace: alt2-namespace
1446 labels:
1447 kat-ambassador-id: {self.ambassador_id}
1448spec:
1449 ambassador_id: [ {self.ambassador_id} ]
1450 port: 8443
1451 protocol: HTTPS
1452 securityModel: XFP
1453 hostBinding:
1454 namespace:
1455 from: SELF
1456---
1457apiVersion: getambassador.io/v3alpha1
1458kind: Host
1459metadata:
1460 name: {self.path.k8s}
1461 namespace: alt2-namespace
1462 labels:
1463 kat-ambassador-id: {self.ambassador_id}
1464spec:
1465 ambassador_id: [ {self.ambassador_id} ]
1466 hostname: ambassador.example.com
1467 acmeProvider:
1468 authority: none
1469 tlsSecret:
1470 name: {self.path.k8s}.server
1471 tls:
1472 # ca_secret supports cross-namespace references, so test it
1473 ca_secret: {self.path.k8s}-ca
1474 cert_required: true
1475---
1476apiVersion: v1
1477kind: Secret
1478metadata:
1479 name: {self.path.k8s}-ca
1480 namespace: alt2-namespace
1481 labels:
1482 kat-ambassador-id: {self.ambassador_id}
1483type: kubernetes.io/tls
1484data:
1485 tls.crt: """
1486 + TLSCerts["master.datawire.io"].k8s_crt
1487 + """
1488 tls.key: ""
1489---
1490apiVersion: v1
1491kind: Secret
1492metadata:
1493 name: {self.path.k8s}.server
1494 namespace: alt2-namespace
1495 labels:
1496 kat-ambassador-id: {self.ambassador_id}
1497type: kubernetes.io/tls
1498data:
1499 tls.crt: """
1500 + TLSCerts["ambassador.example.com"].k8s_crt
1501 + """
1502 tls.key: """
1503 + TLSCerts["ambassador.example.com"].k8s_key
1504 + """
1505---
1506apiVersion: getambassador.io/v3alpha1
1507kind: Mapping
1508metadata:
1509 name: {self.path.k8s}
1510 labels:
1511 kat-ambassador-id: {self.ambassador_id}
1512spec:
1513 ambassador_id: [ {self.ambassador_id} ]
1514 hostname: "*"
1515 prefix: /
1516 service: {self.target.path.fqdn}
1517"""
1518 )
1519 + super().manifests()
1520 )
1521
1522 def scheme(self) -> str:
1523 return "https"
1524
1525 def queries(self):
1526 base = {
1527 "url": self.url(""),
1528 "ca_cert": TLSCerts["master.datawire.io"].pubcert,
1529 "headers": {"Host": "ambassador.example.com"},
1530 "sni": True, # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
1531 }
1532
1533 yield Query(
1534 **base,
1535 client_crt=TLSCerts["presto.example.com"].pubcert,
1536 client_key=TLSCerts["presto.example.com"].privkey
1537 )
1538
1539 # Check that it requires the client cert.
1540 #
1541 # In TLS < 1.3, there's not a dedicated alert code for "the client forgot to include a certificate",
1542 # so we get a generic alert=40 ("handshake_failure").
1543 yield Query(**base, maxTLSv="v1.2", error="tls: handshake failure")
1544 # TLS 1.3 added a dedicated alert=116 ("certificate_required") for that scenario.
1545 yield Query(
1546 **base,
1547 minTLSv="v1.3",
1548 error=(
1549 ["tls: certificate required"]
1550 + (
1551 ["write: connection reset by peer", "write: broken pipe"]
1552 if bug_clientcert_reset
1553 else []
1554 )
1555 )
1556 )
1557
1558 # Check that it's validating the client cert against the CA cert.
1559 yield Query(
1560 **base,
1561 client_crt=TLSCerts["localhost"].pubcert,
1562 client_key=TLSCerts["localhost"].privkey,
1563 maxTLSv="v1.2",
1564 error="tls: handshake failure"
1565 )
1566
1567 def requirements(self):
1568 for r in super().requirements():
1569 query = r[1]
1570 query.headers = {"Host": "ambassador.example.com"}
1571 query.sni = (
1572 True # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
1573 )
1574 query.ca_cert = TLSCerts["master.datawire.io"].pubcert
1575 query.client_cert = TLSCerts["presto.example.com"].pubcert
1576 query.client_key = TLSCerts["presto.example.com"].privkey
1577 yield (r[0], query)
1578
1579
1580class HostCRDClientCertCRLEmptyList(AmbassadorTest):
1581 target: ServiceType
1582
1583 def init(self):
1584 if Config.envoy_api_version == "V2":
1585 self.skip_node = True
1586 self.target = HTTP()
1587 self.add_default_http_listener = False
1588 self.add_default_https_listener = False
1589
1590 def manifests(self) -> str:
1591 # Similar to HostCRDClientCertSameNamespace, except we also
1592 # include a Certificate Revocation List in the TLS config
1593 return (
1594 namespace_manifest("alt3-namespace")
1595 + self.format(
1596 """
1597---
1598apiVersion: getambassador.io/v3alpha1
1599kind: Listener
1600metadata:
1601 name: ambassador-listener-8443 # This name is to match existing test stuff
1602 namespace: alt3-namespace
1603 labels:
1604 kat-ambassador-id: {self.ambassador_id}
1605spec:
1606 ambassador_id: [ {self.ambassador_id} ]
1607 port: 8443
1608 protocol: HTTPS
1609 securityModel: XFP
1610 hostBinding:
1611 namespace:
1612 from: SELF
1613---
1614apiVersion: getambassador.io/v3alpha1
1615kind: Host
1616metadata:
1617 name: {self.path.k8s}
1618 namespace: alt3-namespace
1619 labels:
1620 kat-ambassador-id: {self.ambassador_id}
1621spec:
1622 ambassador_id: [ {self.ambassador_id} ]
1623 hostname: ambassador.example.com
1624 acmeProvider:
1625 authority: none
1626 tlsSecret:
1627 name: {self.path.k8s}.server
1628 tls:
1629 ca_secret: {self.path.k8s}-ca
1630 cert_required: true
1631 crl_secret: {self.path.k8s}-crl
1632---
1633apiVersion: v1
1634kind: Secret
1635metadata:
1636 name: {self.path.k8s}-ca
1637 namespace: alt3-namespace
1638 labels:
1639 kat-ambassador-id: {self.ambassador_id}
1640type: kubernetes.io/tls
1641data:
1642 tls.crt: """
1643 + TLSCerts["master.datawire.io"].k8s_crt
1644 + """
1645 tls.key: ""
1646---
1647apiVersion: v1
1648kind: Secret
1649metadata:
1650 name: {self.path.k8s}-crl
1651 namespace: alt3-namespace
1652 labels:
1653 kat-ambassador-id: {self.ambassador_id}
1654type: Opaque
1655data:
1656 crl.pem: """
1657 + create_crl_pem_b64(
1658 TLSCerts["master.datawire.io"].pubcert,
1659 TLSCerts["master.datawire.io"].privkey,
1660 [],
1661 )
1662 + """
1663---
1664apiVersion: v1
1665kind: Secret
1666metadata:
1667 name: {self.path.k8s}.server
1668 namespace: alt3-namespace
1669 labels:
1670 kat-ambassador-id: {self.ambassador_id}
1671type: kubernetes.io/tls
1672data:
1673 tls.crt: """
1674 + TLSCerts["ambassador.example.com"].k8s_crt
1675 + """
1676 tls.key: """
1677 + TLSCerts["ambassador.example.com"].k8s_key
1678 + """
1679---
1680apiVersion: getambassador.io/v3alpha1
1681kind: Mapping
1682metadata:
1683 name: {self.path.k8s}
1684 labels:
1685 kat-ambassador-id: {self.ambassador_id}
1686spec:
1687 ambassador_id: [ {self.ambassador_id} ]
1688 hostname: "*"
1689 prefix: /
1690 service: {self.target.path.fqdn}
1691"""
1692 )
1693 + super().manifests()
1694 )
1695
1696 def scheme(self) -> str:
1697 return "https"
1698
1699 def queries(self):
1700 base = {
1701 "url": self.url(""),
1702 "ca_cert": TLSCerts["master.datawire.io"].pubcert,
1703 "headers": {"Host": "ambassador.example.com"},
1704 "sni": True, # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
1705 }
1706
1707 yield Query(**base, error="tls: certificate required")
1708
1709 yield Query(
1710 **base,
1711 client_crt=TLSCerts["presto.example.com"].pubcert,
1712 client_key=TLSCerts["presto.example.com"].privkey
1713 )
1714
1715 def requirements(self):
1716 yield ("pod", self.path.k8s)
1717
1718
1719class HostCRDClientCertCRLRevokeList(AmbassadorTest):
1720 target: ServiceType
1721
1722 def init(self):
1723 if Config.envoy_api_version == "V2":
1724 self.skip_node = True
1725 self.target = HTTP()
1726 self.add_default_http_listener = False
1727 self.add_default_https_listener = False
1728
1729 def manifests(self) -> str:
1730 # Similar to HostCRDClientCertSameNamespace, except we also
1731 # include a Certificate Revocation List in the TLS config
1732 return (
1733 namespace_manifest("alt4-namespace")
1734 + self.format(
1735 """
1736---
1737apiVersion: getambassador.io/v3alpha1
1738kind: Listener
1739metadata:
1740 name: ambassador-listener-8443 # This name is to match existing test stuff
1741 namespace: alt4-namespace
1742 labels:
1743 kat-ambassador-id: {self.ambassador_id}
1744spec:
1745 ambassador_id: [ {self.ambassador_id} ]
1746 port: 8443
1747 protocol: HTTPS
1748 securityModel: XFP
1749 hostBinding:
1750 namespace:
1751 from: SELF
1752---
1753apiVersion: getambassador.io/v3alpha1
1754kind: Host
1755metadata:
1756 name: {self.path.k8s}
1757 namespace: alt4-namespace
1758 labels:
1759 kat-ambassador-id: {self.ambassador_id}
1760spec:
1761 ambassador_id: [ {self.ambassador_id} ]
1762 hostname: ambassador.example.com
1763 acmeProvider:
1764 authority: none
1765 tlsSecret:
1766 name: {self.path.k8s}.server
1767 tls:
1768 ca_secret: {self.path.k8s}-ca
1769 cert_required: true
1770 crl_secret: {self.path.k8s}-crl
1771---
1772apiVersion: v1
1773kind: Secret
1774metadata:
1775 name: {self.path.k8s}-ca
1776 namespace: alt4-namespace
1777 labels:
1778 kat-ambassador-id: {self.ambassador_id}
1779type: kubernetes.io/tls
1780data:
1781 tls.crt: """
1782 + TLSCerts["master.datawire.io"].k8s_crt
1783 + """
1784 tls.key: ""
1785---
1786apiVersion: v1
1787kind: Secret
1788metadata:
1789 name: {self.path.k8s}-crl
1790 namespace: alt4-namespace
1791 labels:
1792 kat-ambassador-id: {self.ambassador_id}
1793type: Opaque
1794data:
1795 crl.pem: """
1796 + create_crl_pem_b64(
1797 TLSCerts["master.datawire.io"].pubcert,
1798 TLSCerts["master.datawire.io"].privkey,
1799 [TLSCerts["presto.example.com"].pubcert],
1800 )
1801 + """
1802---
1803apiVersion: v1
1804kind: Secret
1805metadata:
1806 name: {self.path.k8s}.server
1807 namespace: alt4-namespace
1808 labels:
1809 kat-ambassador-id: {self.ambassador_id}
1810type: kubernetes.io/tls
1811data:
1812 tls.crt: """
1813 + TLSCerts["ambassador.example.com"].k8s_crt
1814 + """
1815 tls.key: """
1816 + TLSCerts["ambassador.example.com"].k8s_key
1817 + """
1818---
1819apiVersion: getambassador.io/v3alpha1
1820kind: Mapping
1821metadata:
1822 name: {self.path.k8s}
1823 labels:
1824 kat-ambassador-id: {self.ambassador_id}
1825spec:
1826 ambassador_id: [ {self.ambassador_id} ]
1827 hostname: "*"
1828 prefix: /
1829 service: {self.target.path.fqdn}
1830"""
1831 )
1832 + super().manifests()
1833 )
1834
1835 def scheme(self) -> str:
1836 return "https"
1837
1838 def queries(self):
1839 base = {
1840 "url": self.url(""),
1841 "ca_cert": TLSCerts["master.datawire.io"].pubcert,
1842 "headers": {"Host": "ambassador.example.com"},
1843 "sni": True, # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
1844 }
1845
1846 yield Query(**base, error="tls: certificate required")
1847
1848 yield Query(
1849 **base,
1850 client_crt=TLSCerts["presto.example.com"].pubcert,
1851 client_key=TLSCerts["presto.example.com"].privkey,
1852 error="tls: revoked certificate"
1853 )
1854
1855 def requirements(self):
1856 yield ("pod", self.path.k8s)
1857
1858
1859class HostCRDRootRedirectCongratulations(AmbassadorTest):
1860 def manifests(self) -> str:
1861 return (
1862 self.format(
1863 """
1864---
1865apiVersion: getambassador.io/v3alpha1
1866kind: Host
1867metadata:
1868 name: {self.path.k8s}
1869 labels:
1870 kat-ambassador-id: {self.ambassador_id}
1871spec:
1872 ambassador_id: [ {self.ambassador_id} ]
1873 hostname: tls-context-host-1
1874 acmeProvider:
1875 authority: none
1876 mappingSelector:
1877 matchLabels:
1878 hostname: tls-context-host-1
1879 tlsSecret:
1880 name: {self.path.k8s}-test-tlscontext-secret-1
1881 requestPolicy:
1882 insecure:
1883 action: Redirect
1884---
1885apiVersion: v1
1886kind: Secret
1887metadata:
1888 name: {self.path.k8s}-test-tlscontext-secret-1
1889 labels:
1890 kat-ambassador-id: {self.ambassador_id}
1891type: kubernetes.io/tls
1892data:
1893 tls.crt: """
1894 + TLSCerts["tls-context-host-1"].k8s_crt
1895 + """
1896 tls.key: """
1897 + TLSCerts["tls-context-host-1"].k8s_key
1898 + """
1899"""
1900 )
1901 + super().manifests()
1902 )
1903
1904 def scheme(self) -> str:
1905 return "https"
1906
1907 def queries(self):
1908 yield Query(
1909 self.url("", scheme="http"),
1910 headers={"Host": "tls-context-host-1"},
1911 expected=(404 if (EDGE_STACK or bug_404_routes) else 301),
1912 )
1913 yield Query(
1914 self.url("other", scheme="http"),
1915 headers={"Host": "tls-context-host-1"},
1916 expected=(404 if bug_404_routes else 301),
1917 )
1918
1919 yield Query(
1920 self.url("", scheme="https"),
1921 headers={"Host": "tls-context-host-1"},
1922 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
1923 sni=True,
1924 expected=404,
1925 )
1926 yield Query(
1927 self.url("other", scheme="https"),
1928 headers={"Host": "tls-context-host-1"},
1929 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
1930 sni=True,
1931 expected=404,
1932 )
1933
1934 def requirements(self):
1935 for r in super().requirements():
1936 query = r[1]
1937 query.headers = {"Host": "tls-context-host-1"}
1938 query.sni = (
1939 True # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
1940 )
1941 query.ca_cert = TLSCerts["tls-context-host-1"].pubcert
1942 yield (r[0], query)
1943
1944
1945class HostCRDRootRedirectSlashMapping(AmbassadorTest):
1946 target: ServiceType
1947
1948 def init(self):
1949 self.target = HTTP()
1950
1951 def manifests(self) -> str:
1952 return (
1953 self.format(
1954 """
1955---
1956apiVersion: getambassador.io/v3alpha1
1957kind: Host
1958metadata:
1959 name: {self.path.k8s}
1960 labels:
1961 kat-ambassador-id: {self.ambassador_id}
1962spec:
1963 ambassador_id: [ {self.ambassador_id} ]
1964 hostname: tls-context-host-1
1965 acmeProvider:
1966 authority: none
1967 mappingSelector:
1968 matchLabels:
1969 hostname: {self.path.fqdn}
1970 tlsSecret:
1971 name: {self.path.k8s}-test-tlscontext-secret-1
1972 requestPolicy:
1973 insecure:
1974 action: Redirect
1975---
1976apiVersion: v1
1977kind: Secret
1978metadata:
1979 name: {self.path.k8s}-test-tlscontext-secret-1
1980 labels:
1981 kat-ambassador-id: {self.ambassador_id}
1982type: kubernetes.io/tls
1983data:
1984 tls.crt: """
1985 + TLSCerts["tls-context-host-1"].k8s_crt
1986 + """
1987 tls.key: """
1988 + TLSCerts["tls-context-host-1"].k8s_key
1989 + """
1990---
1991apiVersion: getambassador.io/v3alpha1
1992kind: Mapping
1993metadata:
1994 name: {self.path.k8s}-target-mapping
1995 labels:
1996 hostname: {self.path.fqdn}
1997spec:
1998 ambassador_id: [ {self.ambassador_id} ]
1999 prefix: /
2000 service: {self.target.path.fqdn}
2001"""
2002 )
2003 + super().manifests()
2004 )
2005
2006 def scheme(self) -> str:
2007 return "https"
2008
2009 def queries(self):
2010 yield Query(
2011 self.url("", scheme="http"), headers={"Host": "tls-context-host-1"}, expected=301
2012 )
2013 yield Query(
2014 self.url("other", scheme="http"), headers={"Host": "tls-context-host-1"}, expected=301
2015 )
2016
2017 yield Query(
2018 self.url("", scheme="https"),
2019 headers={"Host": "tls-context-host-1"},
2020 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2021 sni=True,
2022 expected=200,
2023 )
2024 yield Query(
2025 self.url("other", scheme="https"),
2026 headers={"Host": "tls-context-host-1"},
2027 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2028 sni=True,
2029 expected=200,
2030 )
2031
2032 def requirements(self):
2033 for r in super().requirements():
2034 query = r[1]
2035 query.headers = {"Host": "tls-context-host-1"}
2036 query.sni = (
2037 True # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
2038 )
2039 query.ca_cert = TLSCerts["tls-context-host-1"].pubcert
2040 yield (r[0], query)
2041
2042
2043class HostCRDRootRedirectRE2Mapping(AmbassadorTest):
2044 target: ServiceType
2045
2046 def init(self):
2047 self.target = HTTP()
2048
2049 def manifests(self) -> str:
2050 return (
2051 self.format(
2052 """
2053---
2054apiVersion: getambassador.io/v3alpha1
2055kind: Host
2056metadata:
2057 name: {self.path.k8s}
2058 labels:
2059 kat-ambassador-id: {self.ambassador_id}
2060spec:
2061 ambassador_id: [ {self.ambassador_id} ]
2062 hostname: tls-context-host-1
2063 acmeProvider:
2064 authority: none
2065 mappingSelector:
2066 matchLabels:
2067 hostname: {self.path.fqdn}
2068 tlsSecret:
2069 name: {self.path.k8s}-test-tlscontext-secret-1
2070 requestPolicy:
2071 insecure:
2072 action: Redirect
2073---
2074apiVersion: v1
2075kind: Secret
2076metadata:
2077 name: {self.path.k8s}-test-tlscontext-secret-1
2078 labels:
2079 kat-ambassador-id: {self.ambassador_id}
2080type: kubernetes.io/tls
2081data:
2082 tls.crt: """
2083 + TLSCerts["tls-context-host-1"].k8s_crt
2084 + """
2085 tls.key: """
2086 + TLSCerts["tls-context-host-1"].k8s_key
2087 + """
2088---
2089apiVersion: getambassador.io/v3alpha1
2090kind: Mapping
2091metadata:
2092 name: {self.path.k8s}-target-mapping
2093 labels:
2094 hostname: {self.path.fqdn}
2095spec:
2096 ambassador_id: [ {self.ambassador_id} ]
2097 prefix: "/[[:word:]]*" # :word: is in RE2 but not ECMAScript RegExp or Python 're'
2098 prefix_regex: true
2099 service: {self.target.path.fqdn}
2100"""
2101 )
2102 + super().manifests()
2103 )
2104
2105 def scheme(self) -> str:
2106 return "https"
2107
2108 def queries(self):
2109 yield Query(
2110 self.url("", scheme="http"), headers={"Host": "tls-context-host-1"}, expected=301
2111 )
2112 yield Query(
2113 self.url("other", scheme="http"), headers={"Host": "tls-context-host-1"}, expected=301
2114 )
2115 yield Query(
2116 self.url("-other", scheme="http"),
2117 headers={"Host": "tls-context-host-1"},
2118 expected=(404 if bug_404_routes else 301),
2119 )
2120
2121 yield Query(
2122 self.url("", scheme="https"),
2123 headers={"Host": "tls-context-host-1"},
2124 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2125 sni=True,
2126 expected=200,
2127 )
2128 yield Query(
2129 self.url("other", scheme="https"),
2130 headers={"Host": "tls-context-host-1"},
2131 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2132 sni=True,
2133 expected=200,
2134 )
2135 yield Query(
2136 self.url("-other", scheme="https"),
2137 headers={"Host": "tls-context-host-1"},
2138 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2139 sni=True,
2140 expected=404,
2141 )
2142
2143 def requirements(self):
2144 for r in super().requirements():
2145 query = r[1]
2146 query.headers = {"Host": "tls-context-host-1"}
2147 query.sni = (
2148 True # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
2149 )
2150 query.ca_cert = TLSCerts["tls-context-host-1"].pubcert
2151 yield (r[0], query)
2152
2153
2154class HostCRDRootRedirectECMARegexMapping(AmbassadorTest):
2155 target: ServiceType
2156
2157 def init(self):
2158 if Config.envoy_api_version == "V3":
2159 self.skip_node = True
2160 self.target = HTTP()
2161
2162 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
2163 yield self, self.format(
2164 """
2165---
2166apiVersion: getambassador.io/v3alpha1
2167kind: Module
2168name: ambassador
2169config:
2170 regex_type: unsafe
2171"""
2172 )
2173
2174 def manifests(self) -> str:
2175 return (
2176 self.format(
2177 """
2178---
2179apiVersion: getambassador.io/v3alpha1
2180kind: Host
2181metadata:
2182 name: {self.path.k8s}
2183 labels:
2184 kat-ambassador-id: {self.ambassador_id}
2185spec:
2186 ambassador_id: [ {self.ambassador_id} ]
2187 hostname: tls-context-host-1
2188 acmeProvider:
2189 authority: none
2190 mappingSelector:
2191 matchLabels:
2192 hostname: {self.path.fqdn}
2193 tlsSecret:
2194 name: {self.path.k8s}-test-tlscontext-secret-1
2195 requestPolicy:
2196 insecure:
2197 action: Redirect
2198---
2199apiVersion: v1
2200kind: Secret
2201metadata:
2202 name: {self.path.k8s}-test-tlscontext-secret-1
2203 labels:
2204 kat-ambassador-id: {self.ambassador_id}
2205type: kubernetes.io/tls
2206data:
2207 tls.crt: """
2208 + TLSCerts["tls-context-host-1"].k8s_crt
2209 + """
2210 tls.key: """
2211 + TLSCerts["tls-context-host-1"].k8s_key
2212 + """
2213---
2214apiVersion: getambassador.io/v3alpha1
2215kind: Mapping
2216metadata:
2217 name: {self.path.k8s}-target-mapping
2218 labels:
2219 hostname: {self.path.fqdn}
2220spec:
2221 ambassador_id: [ {self.ambassador_id} ]
2222 prefix: "/(?!-).*" # (?!re) is valid ECMAScript RegExp but not RE2... unfortunately it's also valid Python 're'
2223 prefix_regex: true
2224 service: {self.target.path.fqdn}
2225"""
2226 )
2227 + super().manifests()
2228 )
2229
2230 def scheme(self) -> str:
2231 return "https"
2232
2233 def queries(self):
2234 yield Query(
2235 self.url("", scheme="http"), headers={"Host": "tls-context-host-1"}, expected=301
2236 )
2237 yield Query(
2238 self.url("other", scheme="http"), headers={"Host": "tls-context-host-1"}, expected=301
2239 )
2240 yield Query(
2241 self.url("-other", scheme="http"),
2242 headers={"Host": "tls-context-host-1"},
2243 expected=(404 if bug_404_routes else 301),
2244 )
2245
2246 yield Query(
2247 self.url("", scheme="https"),
2248 headers={"Host": "tls-context-host-1"},
2249 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2250 sni=True,
2251 expected=200,
2252 )
2253 yield Query(
2254 self.url("other", scheme="https"),
2255 headers={"Host": "tls-context-host-1"},
2256 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2257 sni=True,
2258 expected=200,
2259 )
2260 yield Query(
2261 self.url("-other", scheme="https"),
2262 headers={"Host": "tls-context-host-1"},
2263 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2264 sni=True,
2265 expected=404,
2266 )
2267
2268 def requirements(self):
2269 for r in super().requirements():
2270 query = r[1]
2271 query.headers = {"Host": "tls-context-host-1"}
2272 query.sni = (
2273 True # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
2274 )
2275 query.ca_cert = TLSCerts["tls-context-host-1"].pubcert
2276 yield (r[0], query)
2277
2278
2279class HostCRDForcedStar(AmbassadorTest):
2280 """This test verifies that Ambassador responds properly if we try
2281 talking to it on a hostname that it doesn't recognize.
2282 """
2283
2284 target: ServiceType
2285
2286 def init(self):
2287 # We turn off edge_stack_cleartext_host, and manually
2288 # duplicate the edge_stack_cleartext_host YAML in manifests(),
2289 # since we want that even when testing a non-edge-stack build.
2290 #
2291 # The reason for that is that we're testing that it doesn't
2292 # accidentally consider a cleartext hostname="*" to be a TLS
2293 # hostname="*".
2294 self.edge_stack_cleartext_host = False
2295 self.target = HTTP()
2296
2297 def manifests(self) -> str:
2298 return (
2299 self.format(
2300 """
2301---
2302apiVersion: getambassador.io/v3alpha1
2303kind: Host
2304metadata:
2305 name: {self.path.k8s}-cleartext-host
2306 labels:
2307 kat-ambassador-id: {self.ambassador_id}
2308spec:
2309 ambassador_id: [ "{self.ambassador_id}" ]
2310 hostname: "*"
2311 acmeProvider:
2312 authority: none
2313 requestPolicy:
2314 insecure:
2315 action: Redirect
2316---
2317apiVersion: getambassador.io/v3alpha1
2318kind: Host
2319metadata:
2320 name: {self.path.k8s}-tls-host
2321 labels:
2322 kat-ambassador-id: {self.ambassador_id}
2323spec:
2324 ambassador_id: [ {self.ambassador_id} ]
2325 hostname: tls-context-host-1
2326 acmeProvider:
2327 authority: none
2328 tlsSecret:
2329 name: {self.path.k8s}-test-tlscontext-secret-1
2330 requestPolicy:
2331 insecure:
2332 action: Route
2333---
2334apiVersion: v1
2335kind: Secret
2336metadata:
2337 name: {self.path.k8s}-test-tlscontext-secret-1
2338 labels:
2339 kat-ambassador-id: {self.ambassador_id}
2340type: kubernetes.io/tls
2341data:
2342 tls.crt: """
2343 + TLSCerts["tls-context-host-1"].k8s_crt
2344 + """
2345 tls.key: """
2346 + TLSCerts["tls-context-host-1"].k8s_key
2347 + """
2348---
2349apiVersion: getambassador.io/v3alpha1
2350kind: Mapping
2351metadata:
2352 name: {self.path.k8s}-target-mapping
2353spec:
2354 ambassador_id: [ {self.ambassador_id} ]
2355 hostname: "*"
2356 prefix: /foo/
2357 service: {self.target.path.fqdn}
2358"""
2359 )
2360 + super().manifests()
2361 )
2362
2363 def scheme(self) -> str:
2364 return "https"
2365
2366 def queries(self):
2367 # For each of these, we'll first try it on a recognized hostname ("tls-context-host-1") as a
2368 # sanity check, and then we'll try the same query for an unrecongized hostname
2369 # ("nonmatching-host") to make sure that it is handled the same way.
2370
2371 # 0-1: cleartext 200/301
2372 yield Query(
2373 self.url("foo/0-200", scheme="http"),
2374 headers={"Host": "tls-context-host-1"},
2375 expected=200,
2376 )
2377 yield Query(
2378 self.url("foo/1-301", scheme="http"),
2379 headers={"Host": "nonmatching-hostname"},
2380 expected=301,
2381 )
2382
2383 # 2-3: cleartext 404
2384 yield Query(
2385 self.url("bar/2-404", scheme="http"),
2386 headers={"Host": "tls-context-host-1"},
2387 expected=404,
2388 )
2389 yield Query(
2390 self.url("bar/3-301-or-404", scheme="http"),
2391 headers={"Host": "nonmatching-hostname"},
2392 expected=404 if bug_404_routes else 301,
2393 )
2394
2395 # 4-5: TLS 200
2396 yield Query(
2397 self.url("foo/4-200", scheme="https"),
2398 headers={"Host": "tls-context-host-1"},
2399 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2400 sni=True,
2401 expected=200,
2402 )
2403 yield Query(
2404 self.url("foo/5-200", scheme="https"),
2405 headers={"Host": "nonmatching-hostname"},
2406 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2407 sni=True,
2408 insecure=True,
2409 expected=200,
2410 error=("http: server gave HTTP response to HTTPS client" if bug_forced_star else None),
2411 )
2412
2413 # 6-7: TLS 404
2414 yield Query(
2415 self.url("bar/6-404", scheme="https"),
2416 headers={"Host": "tls-context-host-1"},
2417 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2418 sni=True,
2419 expected=404,
2420 )
2421 yield Query(
2422 self.url("bar/7-404", scheme="https"),
2423 headers={"Host": "nonmatching-hostname"},
2424 ca_cert=TLSCerts["tls-context-host-1"].pubcert,
2425 sni=True,
2426 insecure=True,
2427 expected=404,
2428 error=("http: server gave HTTP response to HTTPS client" if bug_forced_star else None),
2429 )
2430
2431 def requirements(self):
2432 for r in super().requirements():
2433 query = r[1]
2434 query.headers = {"Host": "tls-context-host-1"}
2435 query.sni = (
2436 True # Use query.headers["Host"] instead of urlparse(query.url).hostname for SNI
2437 )
2438 query.ca_cert = TLSCerts["tls-context-host-1"].pubcert
2439 yield (r[0], query)
2440
2441
2442class HostCRDCrossNamespaceNoNamespacing(AmbassadorTest):
2443 """
2444 HostCRDCrossNamespaceNoNamespacing tests that the value of tls_secret_namespacing does not interfere
2445 with the function of being able to specify atlsSecret in another namespace due to the way that the
2446 implicit TLSContext handles secret names.
2447 """
2448
2449 target: ServiceType
2450
2451 def init(self):
2452 self.edge_stack_cleartext_host = False
2453 self.target1 = HTTP(name="target")
2454
2455 def manifests(self) -> str:
2456 return (
2457 namespace_manifest("foobar")
2458 + self.format(
2459 """
2460---
2461apiVersion: getambassador.io/v3alpha1
2462kind: Host
2463metadata:
2464 name: {self.path.k8s}-host-1
2465 labels:
2466 kat-ambassador-id: {self.ambassador_id}
2467spec:
2468 ambassador_id: [ {self.ambassador_id} ]
2469 hostname: tls-context-host-1
2470 acmeProvider:
2471 authority: none
2472 mappingSelector:
2473 matchLabels:
2474 hostname: tls-context-host-1
2475 tlsSecret:
2476 name: {self.path.k8s}.test.tlscontext.secret
2477 namespace: foobar
2478---
2479apiVersion: v1
2480kind: Secret
2481metadata:
2482 name: {self.path.k8s}.test.tlscontext.secret
2483 namespace: foobar
2484 labels:
2485 kat-ambassador-id: {self.ambassador_id}
2486type: kubernetes.io/tls
2487data:
2488 tls.crt: """
2489 + TLSCerts["tls-context-host-1"].k8s_crt
2490 + """
2491 tls.key: """
2492 + TLSCerts["tls-context-host-1"].k8s_key
2493 + """
2494---
2495apiVersion: getambassador.io/v3alpha1
2496kind: Mapping
2497metadata:
2498 name: {self.path.k8s}-host-1-mapping
2499 labels:
2500 hostname: tls-context-host-1
2501 kat-ambassador-id: {self.ambassador_id}
2502spec:
2503 ambassador_id: [ {self.ambassador_id} ]
2504 hostname: tls-context-host-1
2505 prefix: /target/
2506 service: {self.target1.path.fqdn}
2507"""
2508 )
2509 + super().manifests()
2510 )
2511
2512 def scheme(self) -> str:
2513 return "https"
2514
2515 def queries(self):
2516 # Get some info from diagd for self.check() to inspect
2517 yield Query(
2518 self.url("ambassador/v0/diag/?json=true&filter=errors"),
2519 headers={"Host": "tls-context-host-1"},
2520 insecure=True,
2521 sni=True,
2522 )
2523
2524 yield Query(
2525 self.url("target/", scheme="https"),
2526 headers={"Host": "tls-context-host-1"},
2527 sni=True,
2528 insecure=True,
2529 expected=200,
2530 )
2531 yield Query(
2532 self.url("target/", scheme="http"),
2533 headers={"Host": "tls-context-host-1"},
2534 expected=301,
2535 )
2536
2537 def check(self):
2538 # XXX If self.results[0].json is empty, the harness won't convert it to a response.
2539 errors = self.results[0].json or []
2540 num_errors = len(errors)
2541 assert num_errors == 0, "expected 0 errors, got {} -\n{}".format(num_errors, errors)
2542
2543 idx = 0
2544
2545 for result in self.results:
2546 if result.status == 200 and result.query.headers and result.tls:
2547 host_header = result.query.headers["Host"]
2548 tls_common_name = result.tls[0]["Subject"]["CommonName"]
2549
2550 assert host_header == tls_common_name, "test %d wanted CN %s, but got %s" % (
2551 idx,
2552 host_header,
2553 tls_common_name,
2554 )
2555
2556 idx += 1
2557
2558 def requirements(self):
2559 # We're replacing super()'s requirements deliberately here. Without a Host header they can't work.
2560 yield (
2561 "url",
2562 Query(
2563 self.url("ambassador/v0/check_ready"),
2564 headers={"Host": "tls-context-host-1"},
2565 insecure=True,
2566 sni=True,
2567 ),
2568 )
2569 yield (
2570 "url",
2571 Query(
2572 self.url("ambassador/v0/check_alive"),
2573 headers={"Host": "tls-context-host-1"},
2574 insecure=True,
2575 sni=True,
2576 ),
2577 )
2578
2579
2580class HostCRDDoubleCrossNamespace(AmbassadorTest):
2581 """
2582 HostCRDDouble: We have two Hosts, each with a
2583 manually-configured TLS secret, the secrets have
2584 the same name but are in different namespaces.
2585 """
2586
2587 target1: ServiceType
2588 target2: ServiceType
2589
2590 def init(self):
2591 self.edge_stack_cleartext_host = False
2592 self.target1 = HTTP(name="target1")
2593 self.target2 = HTTP(name="target2")
2594
2595 def manifests(self) -> str:
2596 return (
2597 namespace_manifest("bar")
2598 + namespace_manifest("foo")
2599 + self.format(
2600 """
2601---
2602apiVersion: getambassador.io/v3alpha1
2603kind: Host
2604metadata:
2605 name: {self.path.k8s}-host-1
2606 labels:
2607 kat-ambassador-id: {self.ambassador_id}
2608spec:
2609 ambassador_id: [ {self.ambassador_id} ]
2610 hostname: tls-context-host-1
2611 acmeProvider:
2612 authority: none
2613 mappingSelector:
2614 matchLabels:
2615 hostname: tls-context-host-1
2616 tlsSecret:
2617 name: {self.path.k8s}-test-tlscontext-secret
2618 namespace: foo
2619---
2620apiVersion: v1
2621kind: Secret
2622metadata:
2623 name: {self.path.k8s}-test-tlscontext-secret
2624 namespace: foo
2625 labels:
2626 kat-ambassador-id: {self.ambassador_id}
2627type: kubernetes.io/tls
2628data:
2629 tls.crt: """
2630 + TLSCerts["tls-context-host-1"].k8s_crt
2631 + """
2632 tls.key: """
2633 + TLSCerts["tls-context-host-1"].k8s_key
2634 + """
2635---
2636apiVersion: getambassador.io/v3alpha1
2637kind: Mapping
2638metadata:
2639 name: {self.path.k8s}-host-1-mapping
2640 labels:
2641 hostname: tls-context-host-1
2642 kat-ambassador-id: {self.ambassador_id}
2643spec:
2644 ambassador_id: [ {self.ambassador_id} ]
2645 hostname: tls-context-host-1
2646 prefix: /target-1/
2647 service: {self.target1.path.fqdn}
2648
2649---
2650apiVersion: getambassador.io/v3alpha1
2651kind: Host
2652metadata:
2653 name: {self.path.k8s}-host-2
2654 labels:
2655 kat-ambassador-id: {self.ambassador_id}
2656spec:
2657 ambassador_id: [ {self.ambassador_id} ]
2658 hostname: tls-context-host-2
2659 acmeProvider:
2660 authority: none
2661 tlsSecret:
2662 name: {self.path.k8s}-test-tlscontext-secret
2663 namespace: bar
2664---
2665apiVersion: v1
2666kind: Secret
2667metadata:
2668 name: {self.path.k8s}-test-tlscontext-secret
2669 namespace: bar
2670 labels:
2671 kat-ambassador-id: {self.ambassador_id}
2672type: kubernetes.io/tls
2673data:
2674 tls.crt: """
2675 + TLSCerts["tls-context-host-2"].k8s_crt
2676 + """
2677 tls.key: """
2678 + TLSCerts["tls-context-host-2"].k8s_key
2679 + """
2680---
2681apiVersion: getambassador.io/v3alpha1
2682kind: Mapping
2683metadata:
2684 name: {self.path.k8s}-host-2-mapping
2685 labels:
2686 hostname: tls-context-host-2
2687 kat-ambassador-id: {self.ambassador_id}
2688spec:
2689 ambassador_id: [ {self.ambassador_id} ]
2690 hostname: tls-context-host-2
2691 prefix: /target-2/
2692 service: {self.target2.path.fqdn}
2693"""
2694 )
2695 + super().manifests()
2696 )
2697
2698 def scheme(self) -> str:
2699 return "https"
2700
2701 def queries(self):
2702 # Get some info from diagd for self.check() to inspect
2703 yield Query(
2704 self.url("ambassador/v0/diag/?json=true&filter=errors"),
2705 headers={"Host": "tls-context-host-1"},
2706 insecure=True,
2707 sni=True,
2708 )
2709
2710 # Host #1 - TLS
2711 yield Query(
2712 self.url("target-1/", scheme="https"),
2713 headers={"Host": "tls-context-host-1"},
2714 sni=True,
2715 insecure=True,
2716 expected=200,
2717 )
2718 yield Query(
2719 self.url("target-2/", scheme="https"),
2720 headers={"Host": "tls-context-host-1"},
2721 sni=True,
2722 insecure=True,
2723 expected=404,
2724 )
2725 yield Query(
2726 self.url("target-1/", scheme="http"),
2727 headers={"Host": "tls-context-host-1"},
2728 expected=301,
2729 )
2730
2731 # Host #2 - TLS
2732 yield Query(
2733 self.url("target-1/", scheme="https"),
2734 headers={"Host": "tls-context-host-2"},
2735 sni=True,
2736 insecure=True,
2737 expected=404,
2738 )
2739 yield Query(
2740 self.url("target-2/", scheme="https"),
2741 headers={"Host": "tls-context-host-2"},
2742 sni=True,
2743 insecure=True,
2744 expected=200,
2745 )
2746 yield Query(
2747 self.url("target-2/", scheme="http"),
2748 headers={"Host": "tls-context-host-2"},
2749 expected=301,
2750 )
2751
2752 def check(self):
2753 # XXX If self.results[0].json is empty, the harness won't convert it to a response.
2754 errors = self.results[0].json or []
2755 num_errors = len(errors)
2756 assert num_errors == 0, "expected 0 errors, got {} -\n{}".format(num_errors, errors)
2757
2758 idx = 0
2759
2760 for result in self.results:
2761 if result.status == 200 and result.query.headers and result.tls:
2762 host_header = result.query.headers["Host"]
2763 tls_common_name = result.tls[0]["Subject"]["CommonName"]
2764
2765 assert host_header == tls_common_name, "test %d wanted CN %s, but got %s" % (
2766 idx,
2767 host_header,
2768 tls_common_name,
2769 )
2770
2771 idx += 1
2772
2773 def requirements(self):
2774 # We're replacing super()'s requirements deliberately here. Without a Host header they can't work.
2775 yield (
2776 "url",
2777 Query(
2778 self.url("ambassador/v0/check_ready"),
2779 headers={"Host": "tls-context-host-1"},
2780 insecure=True,
2781 sni=True,
2782 ),
2783 )
2784 yield (
2785 "url",
2786 Query(
2787 self.url("ambassador/v0/check_alive"),
2788 headers={"Host": "tls-context-host-1"},
2789 insecure=True,
2790 sni=True,
2791 ),
2792 )
2793 yield (
2794 "url",
2795 Query(
2796 self.url("ambassador/v0/check_ready"),
2797 headers={"Host": "tls-context-host-2"},
2798 insecure=True,
2799 sni=True,
2800 ),
2801 )
2802 yield (
2803 "url",
2804 Query(
2805 self.url("ambassador/v0/check_alive"),
2806 headers={"Host": "tls-context-host-2"},
2807 insecure=True,
2808 sni=True,
2809 ),
2810 )
View as plain text