...

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

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

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

View as plain text