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