Multiple hosts on one listener (SNI)

You can set up an HTTPS/TLS listener on the gateway that serves multiple hosts. 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.

SNI listener setup

The steps in this guide show how to set up SNI routing for the httpbin.example.com and bookinfo.example.com domains.

Before you begin

To follow the example that is outlined on this page, complete the following steps:

Set up SNI routing for multiple domains

The following steps show how you can set up a single HTTPS listener on your ingress gateway that serves multiple hosts.

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

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

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

    openssl req -out example_certs1/bookinfo.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/bookinfo.example.com.key -subj "/CN=bookinfo.example.com/O=bookinfo organization"
    openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 1 -in example_certs1/bookinfo.example.com.csr -out example_certs1/bookinfo.example.com.crt
    
  4. Generate a corresponding client certificate and private key that you later use to send requests to your gateway for both of your domains.

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

    ls example_cert*
    

    Example output:

    bookinfo.example.com.crt bookinfo.example.com.key client.example.com.csr   example.com.crt          httpbin.example.com.crt  httpbin.example.com.key
    bookinfo.example.com.csr client.example.com.crt   client.example.com.key   example.com.key          httpbin.example.com.csr
    
  6. Store the credentials for the httpbin.example.com domain in a Kubernetes secret.

    kubectl create -n gloo-mesh-gateways secret tls httpbin-credential \
      --key=example_certs1/httpbin.example.com.key \
      --cert=example_certs1/httpbin.example.com.crt
    
  7. Store the credentials for the bookinfo.example.com domain in a Kubernetes secret.

    kubectl create -n gloo-mesh-gateways secret tls bookinfo-credential \
      --key=example_certs1/bookinfo.example.com.key \
      --cert=example_certs1/bookinfo.example.com.crt
    
  8. Create a virtual gateway and configure HTTPS listeners for both domains on the same port. Each HTTPS listener uses the domain-specific TLS credentials from the secret that you created earlier.

    kubectl apply -f - <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: VirtualGateway
    metadata:
      name: mtls-vgateway
      namespace: gloo-mesh-gateways
    spec:
      listeners:
      - port:
          number: 443
        http: {}
        tls:
          mode: SIMPLE
          secretName: httpbin-credential
        allowedRouteTables:
        - host: httpbin.example.com
      - port:
          number: 443
        http: {}
        tls:
          mode: SIMPLE
          secretName: bookinfo-credential
        allowedRouteTables:
        - host: bookinfo.example.com
      workloads:
      - selector:
          labels:
            istio: ingressgateway
    EOF
    
  9. Create a route table for the httpbin.example.com domain that routes incoming traffic to the httpbin app in your cluster.

    kubectl apply -f - <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: RouteTable
    metadata:
      name: httpbin
      namespace: gloo-mesh-gateways
    spec:
      virtualGateways:
      - name: mtls-vgateway
        namespace: gloo-mesh-gateways
      hosts:
      - 'httpbin.example.com'
      http:
      - matchers:
        - uri:
            prefix: /
        forwardTo:
          destinations:
          - ref:
              name: httpbin
              namespace: httpbin
              cluster: $CLUSTER_NAME
            port:
              number: 8000
    EOF
    
  10. Create a route table for the bookinfo.example.com domain that routes incoming traffic to the productpage app in your cluster.

    kubectl apply -f - <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: RouteTable
    metadata:
      name: bookinfo
      namespace: gloo-mesh-gateways
    spec:
      virtualGateways:
      - name: mtls-vgateway
        namespace: gloo-mesh-gateways
      hosts:
      - 'bookinfo.example.com'
      http:
      - matchers:
        - uri:
            prefix: /
        forwardTo:
          destinations:
          - ref:
              name: productpage
              namespace: bookinfo
              cluster: $CLUSTER_NAME
            port:
              number: 9080
    EOF
    
  11. Send a request to the httpbin.example.com domain with the client certificate that you created earlier. The gateway authorizes the request by using the TLS credentials that you stored in the TLS secret.

    curl -vik -HHost:httpbin.example.com --resolve "httpbin.example.com:443:${INGRESS_GW_IP}" \
    --cacert example_certs1/example.com.crt "https://httpbin.example.com:443/status/200"
    

    Example output:

    ...
    * Added httpbin.example.com:443:34.145.173.107 to DNS cache
    * Hostname httpbin.example.com was found in DNS cache
    *   Trying 34.145.173.107:443...
    * Connected to httpbin.example.com (34.145.173.107) port 443 (#0)
    * ALPN, offering h2
    * ALPN, offering http/1.1
    * successfully set certificate verify locations:
    *  CAfile: example_certs1/example.com.crt
    *  CApath: none
    * TLSv1.2 (OUT), TLS handshake, Client hello (1):
    * TLSv1.2 (IN), TLS handshake, Server hello (2):
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    * TLSv1.2 (IN), TLS handshake, Server key exchange (12):
    * TLSv1.2 (IN), TLS handshake, Server finished (14):
    * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
    * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
    * TLSv1.2 (OUT), TLS handshake, Finished (20):
    * TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
    * TLSv1.2 (IN), TLS handshake, Finished (20):
    * SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
    * ALPN, server accepted to use h2
    * Server certificate:
    *  subject: CN=httpbin.example.com; O=httpbin organization
    *  start date: Dec 29 17:10:18 2022 GMT
    *  expire date: Dec 29 17:10:18 2023 GMT
    *  issuer: O=example Inc.; CN=example.com
    *  SSL certificate verify ok.
    * Using HTTP2, server supports multi-use
    * Connection state changed (HTTP/2 confirmed)
    * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
    * Using Stream ID: 1 (easy handle 0x150012400)
    > GET /status/200 HTTP/2
    > Host:httpbin.example.com
    > user-agent: curl/7.77.0
    > accept: */*
    > 
    * Connection state changed (MAX_CONCURRENT_STREAMS == 2147483647)!
    < HTTP/2 200 
    HTTP/2 200 
    ...
    
  12. Send a request to the bookinfo.example.com domain with the client certificate that you created earlier. The gateway authorizes the request by using the TLS credentials that you stored in the TLS secret.

    curl -vik -HHost:bookinfo.example.com --resolve "bookinfo.example.com:443:${INGRESS_GW_IP}" \
      --cacert example_certs1/example.com.crt "https://bookinfo.example.com:443/productpage"  
    

    Example output:

    ...
    * Added bookinfo.example.com:443:34.145.173.107 to DNS cache
    * Hostname bookinfo.example.com was found in DNS cache
    *   Trying 34.145.173.107:443...
    * Connected to bookinfo.example.com (34.145.173.107) port 443 (#0)
    * ALPN, offering h2
    * ALPN, offering http/1.1
    * successfully set certificate verify locations:
    *  CAfile: example_certs1/example.com.crt
    *  CApath: none
    * TLSv1.2 (OUT), TLS handshake, Client hello (1):
    * TLSv1.2 (IN), TLS handshake, Server hello (2):
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    * TLSv1.2 (IN), TLS handshake, Server key exchange (12):
    * TLSv1.2 (IN), TLS handshake, Server finished (14):
    * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
    * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
    * TLSv1.2 (OUT), TLS handshake, Finished (20):
    * TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
    * TLSv1.2 (IN), TLS handshake, Finished (20):
    * SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
    * ALPN, server accepted to use h2
    * Server certificate:
    *  subject: CN=bookinfo.example.com; O=bookinfo organization
    *  start date: Dec 29 17:10:45 2022 GMT
    *  expire date: Dec 29 17:10:45 2023 GMT
    *  issuer: O=example Inc.; CN=example.com
    *  SSL certificate verify ok.
    * Using HTTP2, server supports multi-use
    * Connection state changed (HTTP/2 confirmed)
    * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
    * Using Stream ID: 1 (easy handle 0x145017400)
    > GET /productpage HTTP/2
    > Host:bookinfo.example.com
    > user-agent: curl/7.77.0
    > accept: */*
    > 
    * Connection state changed (MAX_CONCURRENT_STREAMS == 2147483647)!
    < HTTP/2 200 
    HTTP/2 200 
    ...
    

Cleanup

You can optionally remove the resources that you set up as part of this guide.
rm -r example_certs1
kubectl delete secret httpbin-credential -n gloo-mesh-gateways
kubectl delete secret bookinfo-credential -n gloo-mesh-gateways
kubectl delete virtualgateway mtls-vgateway -n gloo-mesh-gateways
kubectl delete routetable httpbin -n gloo-mesh-gateways
kubectl delete routetable bookinfo -n gloo-mesh-gateways

Next steps