About SNI

Each host comes with its own TLS certificates that the gateway uses to authenticate and authorize client requests.

Serving multiple hosts on a single listener is also referred to as Server Name Indication (SNI) routing. SNI is an extension to the TLS protocol and allows clients to indicate which hostname they want to connect to at the start of the TLS handshake. After the HTTPS/TLS traffic is accepted at the gateway, the TLS connection is terminated, and the unencrypted HTTP/TCP request is forwarded to the destination.

About this guide

In this guide, you learn how to set up an HTTPS Gateway that serves two different domains, httpbin.example.com and petstore.example.com on the same port 443. When sending a request to the Gateway, you indicate the hostname you want to connect to. Based on the selected hostname, the Gateway presents the hostname-specific certificate.

Before you begin

  1. Follow the Get started guide to install Gloo Gateway and deploy the httpbin sample app.

  2. Make sure that you have the OpenSSL version of openssl, not LibreSSL. The openssl version must be at least 1.1.

    1. Check your openssl version. If you see LibreSSL in the output, continue to the next step.
        openssl version
        
    2. Install the OpenSSL version (not LibreSSL). For example, you might use Homebrew.
        brew install openssl
        
    3. Review the output of the OpenSSL installation for the path of the binary file. You can choose to export the binary to your path, or call the entire path whenever the following steps use an openssl command.
      • For example, openssl might be installed along the following path: /usr/local/opt/openssl@3/bin/
      • To run commands, you can append the path so that your terminal uses this installed version of OpenSSL, and not the default LibreSSL. /usr/local/opt/openssl@3/bin/openssl req -new -newkey rsa:4096 -x509 -sha256 -days 3650...
  3. Decide whether to set up an HTTP listener inline on the Gateway resource or as a separate ListenerSet resource. Note that ListenerSets are an experimental feature in the upstream Kubernetes Gateway API project, and subject to change. For more information, see the Listener overview.

Deploy sample apps

Deploy the Petstore sample app. This app is used alongside the httpbin app from the Get started guide to demonstrate the SNI routing capabilities.

  1. Deploy the Petstore app.

      kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo/v1.16.x/example/petstore/petstore.yaml
      

    Example output:

      deployment.apps/petstore created
    service/petstore created
      
  2. Verify that the Petstore app is up and running.

      kubectl get pods
      

    Example output:

      NAME                        READY   STATUS    RESTARTS   AGE
    petstore-66cddd5bb4-x7vdd   1/1     Running   0          26s
      

Set up TLS certificates for multiple domains

Create TLS certificates for the httpbin.example.com and petstore.example.com domains that are signed by a self-signed root CA.

  1. Create a root certificate and private key to sign the certificates for your services.

      mkdir example_certs
    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs/example.com.key -out example_certs/example.com.crt
      
  2. Generate a TLS certificate and key for the httpbin.example.com domain.

      openssl req -out example_certs/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
    openssl x509 -req -sha256 -days 365 -CA example_certs/example.com.crt -CAkey example_certs/example.com.key -set_serial 0 -in example_certs/httpbin.example.com.csr -out example_certs/httpbin.example.com.crt
      
  3. Generate a TLS certificate and key for the petstore.example.com domain.

      openssl req -out example_certs/petstore.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs/petstore.example.com.key -subj "/CN=petstore.example.com/O=petstore organization"
    openssl x509 -req -sha256 -days 365 -CA example_certs/example.com.crt -CAkey example_certs/example.com.key -set_serial 1 -in example_certs/petstore.example.com.csr -out example_certs/petstore.example.com.crt
      
  4. Verify that you have the required certificates and keys.

      ls example_cert*
      

    Example output:

      petstore.example.com.crt petstore.example.com.key  example.com.crt          httpbin.example.com.crt  httpbin.example.com.key
    petstore.example.com.csr example.com.key          httpbin.example.com.csr
      
  5. Store the credentials for the httpbin.example.com domain in a Kubernetes secret.

      kubectl create -n gloo-system secret tls httpbin-credential \
      --key=example_certs/httpbin.example.com.key \
      --cert=example_certs/httpbin.example.com.crt
      
  6. Store the credentials for the petstore.example.com domain in a Kubernetes secret.

      kubectl create -n gloo-system secret tls petstore-credential \
      --key=example_certs/petstore.example.com.key \
      --cert=example_certs/petstore.example.com.crt
      

Set up SNI routing

Set up an SNI Gateway that serves multiple hosts on the same port.

  1. Create an SNI Gateway. The Gateway defines two hosts on the same HTTPS listener. Each host is configured with the host-specific TLS certificate that you set up earlier.

  2. Create an HTTPRoute that routes incoming requests on the httpbin.example.com domain to the httpbin app.

  3. Create an HTTPRoute that routes incoming requests on the petstore.example.com domain to the petstore app.

  4. Get the external address of the Gateway and save it in an environment variable. Note that it might take a few seconds for the Gateway address to become available.

  5. Send a request to the httpbin.example.com domain with the client certificate that you created earlier. Verify that the gateway presents the TLS certificate for the httpbin.example.com domain during the TLS handshake.

    Example output:

      * Added httpbin.example.com:443:3.XXX.XXX.XX to DNS cache
    * Hostname httpbin.example.com was found in DNS cache
    *   Trying 3.128.214.17:443...
    * Connected to httpbin.example.com (3.XXX.XXX.XX) port 443
    * ALPN: curl offers h2,http/1.1
    * (304) (OUT), TLS handshake, Client hello (1):
    * (304) (IN), TLS handshake, Server hello (2):
    * (304) (IN), TLS handshake, Unknown (8):
    * (304) (IN), TLS handshake, Certificate (11):
    * (304) (IN), TLS handshake, CERT verify (15):
    * (304) (IN), TLS handshake, Finished (20):
    * (304) (OUT), TLS handshake, Finished (20):
    * SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF
    * ALPN: server accepted h2
    * Server certificate:
    *  subject: CN=httpbin.example.com; O=httpbin organization
    *  issuer: O=example Inc.; CN=example.com
    *  SSL certificate verify result: unable to get local issuer certificate (20), continuing   anyway.
    * using HTTP/2
    * [HTTP/2] [1] OPENED stream for https://httpbin.example.com:443/headers
    * [HTTP/2] [1] [:method: GET]
    * [HTTP/2] [1] [:scheme: https]
    * [HTTP/2] [1] [:authority: httpbin.example.com]
    * [HTTP/2] [1] [:path: /headers]
    * [HTTP/2] [1] [user-agent: curl/8.7.1]
    * [HTTP/2] [1] [accept: */*]
    > GET /headers HTTP/2
    > Host: httpbin.example.com
    > User-Agent: curl/8.7.1
    > Accept: */*
    > 
    * Request completely sent off
    < HTTP/2 200 
    HTTP/2 200 
    ...
        "Accept": [
          "*/*"
        ],
        "Host": [
          "httpbin.example.com"
        ],
        "User-Agent": [
          "curl/8.7.1"
        ],
        "X-Envoy-Expected-Rq-Timeout-Ms": [
          "15000"
        ],
        "X-Forwarded-Proto": [
          "https"
        ],
        "X-Request-Id": [
          "33654cba-7198-4b7c-a850-8629fd230145"
        ]
      }
    }
      
  6. Send a request to the petstore.example.com domain with the client certificate that you created earlier. Verify that the gateway presents the TLS certificate for the petstore.example.com domain during the TLS handshake.

    Example output:

      * Added petstore.example.com:443:3.XXX.XXX.XX to DNS cache
    * Hostname petstore.example.com was found in DNS cache
    *   Trying 3.128.214.17:443...
    * Connected to petstore.example.com (3.XXX.XXX.XX) port 443
    * ALPN: curl offers h2,http/1.1
    * (304) (OUT), TLS handshake, Client hello (1):
    * (304) (IN), TLS handshake, Server hello (2):
    * (304) (IN), TLS handshake, Unknown (8):
    * (304) (IN), TLS handshake, Certificate (11):
    * (304) (IN), TLS handshake, CERT verify (15):
    * (304) (IN), TLS handshake, Finished (20):
    * (304) (OUT), TLS handshake, Finished (20):
    *  SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF
    * ALPN: server accepted h2
    * Server certificate:
    *  subject: CN=petstore.example.com; O=petstore organization
    *  issuer: O=example Inc.; CN=example.com
    *  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
    * using HTTP/2
    * [HTTP/2] [1] OPENED stream for https://petstore.example.com:443/api/pets
    * [HTTP/2] [1] [:method: GET]
    * [HTTP/2] [1] [:scheme: https]
    * [HTTP/2] [1] [:authority: petstore.example.com]
    * [HTTP/2] [1] [:path: /api/pets]
    * [HTTP/2] [1] [user-agent: curl/8.7.1]
    * [HTTP/2] [1] [accept: */*]
    > GET /api/pets HTTP/2
    > Host: petstore.example.com
    > User-Agent: curl/8.7.1
    > Accept: */*
    > 
    * Request completely sent off
    < HTTP/2 200 
    HTTP/2 200 
    ...
    
    [{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]
      

Cleanup

You can optionally remove the resources that you set up as part of this guide.

  1. Remove the routing resources for the HTTPS route, including the Kubernetes secret that holds the TLS certificate and key.

  2. Remove the example_certs directory that stores your TLS credentials.

      rm -rf example_certs