Skip to content

Ambassador does not properly handling web browser connection coalescing for HTTP/2 connections #2403

@iNoahNothing

Description

@iNoahNothing

When you have multiple domains use the same certificate (e.g. the server has a certificate that can be used for domain domain.com and subdomains a.domain.com and b.domain.com) and the server supports HTTP/2, the browser will reuse the same connection for requests to domain.com, a.domain.com, and b.domain.com. See this blog post for more info.

If you have created an individual virtual_host for each of these domains in Ambassador (via Host resources or TLSContexts) Ambassador will reuse the same virtual_host, but with a different host in the request and you will get a 404.

In more detail, if you create a TLSContext like the below:

---
apiVersion: getambassador.io/v2
kind: TLSContext
metadata:
  name: context
spec:
  alpn_protocols: h2,http/1.1
  hosts:
  - domain.com
  - a.domain.com
  - b.domain.com
  secret: ambassador-cert

You will get an Ambassador configured where:

  • Ambassador will create three virtual_hosts, one for each of the hosts.
  • ambassador-cert is pointing at a certificate that works for domain.com, a.domain.com and b.domain.com so Ambassador is able to use the same certificate for each of these virtual_hosts
  • alpn_protocols: h2,http/1.1 is set so the browser will use HTTP/2 for the connection to Ambassador

Now, when you send a request to https://a.domain.com/ambassador/v0/diag/ in a web browser, it opens a single HTTP/2 connection to Ambassador with :authority: a.domain.com. Ambassador then looks for a route in virtual_host: a.domain.com, find the route to /ambassador/v0, and correctly sends the request to the diagnostics page.

Now if you change the url to https://b.domain.com/ambassador/v0/diag/, the browser will reuse this same HTTP/2 connection to Ambassador but with :authority: b.domain.com. Ambassador then, reusing the same connection to virtual_host: a.domain.com, looks for a route in virtual_host: a.domain.com but since the :authority headers do not match any routes, returns a 404.

To Reproduce

Reproduction is pretty simple.

  1. Deploy Ambassador

  2. Get a certificate for *.domain.com

  3. Create a TLSContext that uses that certificate and sets

    • alpn_protocols: h2,http/1.1
    • hosts: [ a.domain.com, b.domain.com]
  4. Send a request to https://a.domain.com/ambassador/v0/diag/ in a browser and get the diag page

  5. Change the url to https://b.domain.com/ambassador/v0/diag/ and get a 404

Workaround

Since this issue revolves around how Ambassador is creating virtual_hosts and using the same certificate, a couple of possible workarounds exist that could be used until this is resolved.

  1. Create a different certificate and TLSContext for each domain

    ---
    apiVersion: getambassador.io/v2
    kind: TLSContext
    metadata
      name: domain-context
    spec:
      alpn_protocols: h2,http/1.1
      hosts:
      - domain.com
      secret: domain-cert
    ---
    apiVersion: getambassador.io/v2
    kind: TLSContext
    metadata
      name: a-domain-context
    spec:
      alpn_protocols: h2,http/1.1
      hosts:
      - a.domain.com
      secret: a-domain-cert
    ---
    apiVersion: getambassador.io/v2
    kind: TLSContext
    metadata
      name: b-domain-context
    spec:
      alpn_protocols: h2,http/1.1
      hosts:
      - b.domain.com
      secret: b-domain-cert
    

    This will make is so the browser does not reuse the same connection for a.domain.com and b.domain.com since it cannot use the same certificate.

  2. Use a wildcard in the TLSContext so that domain.com, a.domain.com, and b.domain.com use the same virtual_host

    ---
    apiVersion: getambassador.io/v2
    kind: TLSContext
    metadata
      name: wild-context
    spec:
      alpn_protocols: h2,http/1.1
      hosts:
      - "*"
      secret: wild-cert
    

    Now, when the browser reuses the connection, Ambassador will use the same virtual_host which will match for all :authoritys

Metadata

Metadata

Assignees

No one assigned

    Labels

    p:mediumIssue is of medium importancepinnedIssue cannot be marked stalestaleIssue is stale and will be closedt:bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions