...

Text file src/github.com/emissary-ingress/emissary/v3/python/tests/kat/t_hosts.py

Documentation: github.com/emissary-ingress/emissary/v3/python/tests/kat

     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