Simple HTTPS/TLS

Set up TLS certificates for your gateway to serve HTTPS traffic. After HTTPS traffic is received and accepted at the gateway, the gateway terminates the TLS connection, and forwards the unencrypted HTTP request to the destination in the cluster.

HTTPS listener setup

Before you begin

  1. Install Gloo Gateway in a single or multicluster setup, which includes installing Gloo components, an ingress gateway, a workspace, and workspace settings.
  2. Deploy the Bookinfo app without sidecars.
  3. The default openssl version that is included in macOS is LibreSSL, which does not work with these instructions.

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

    1. Check the openssl version that is installed. 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...
  4. Set up environment variables for your certificates. Update the values as needed.
    export DNS_NAME=www.example.com
    export ROOT_CERT_NAME=gateway-root
    export SERVER_CERT_NAME=istio-ingressgateway
    export CLUSTER_NAME=<cluster-name>
    

Create TLS certificate and key pair

In production scenarios, you might have a TLS certificate and key pair from a certificate management tool that you use. In this case, continue to the next section.

To create your own for a test environment, complete the following steps.

  1. Create the root certificate. The following command creates a root key for 10 years for the DNS hostname that you previously set as an environment variable. You can create a separate root key for each hostname. You might also use wildcards in the DNS hostname, such as to make a root key for multiple subdomains (*.example.com), or to create a test root key for any host (*). For more information, see the OpenSSL docs.
    # root cert
    openssl req -new -newkey rsa:4096 -x509 -sha256 \
        -days 3650 -nodes -out ${ROOT_CERT_NAME}.crt -keyout ${ROOT_CERT_NAME}.key \
        -subj "/CN=$DNS_NAME/O=${ROOT_CERT_NAME}" \
        -addext "subjectAltName = DNS:$DNS_NAME"
    
  2. Use the root certificate to create the server certificate.
    # server cert
    cat > "${SERVER_CERT_NAME}.conf" <<EOF
    [req]
    req_extensions = v3_req
    distinguished_name = req_distinguished_name
    [req_distinguished_name]
    [ v3_req ]
    basicConstraints = CA:FALSE
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth, serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS = $DNS_NAME
    EOF
    
    openssl genrsa -out "${SERVER_CERT_NAME}.key" 2048
    openssl req -new -key "${SERVER_CERT_NAME}.key" -out ${SERVER_CERT_NAME}.csr -subj "/CN=$DNS_NAME/O=${SERVER_CERT_NAME}" -config "${SERVER_CERT_NAME}.conf"
    openssl x509 -req \
      -days 3650 \
      -CA ${ROOT_CERT_NAME}.crt -CAkey ${ROOT_CERT_NAME}.key \
      -set_serial 0 \
      -in ${SERVER_CERT_NAME}.csr -out ${SERVER_CERT_NAME}.crt \
      -extensions v3_req -extfile "${SERVER_CERT_NAME}.conf"
    

Configure TLS for the virtual gateway

In your Gloo Gateway VirtualGateway custom resource, add the TLS configuration details, including an HTTPS redirect. Then, Gloo Gateway applies the updates to each ingress gateway that the virtual gateway selects so that the gateways handle HTTPS traffic for your hosts.

  1. Add your server certificate to the Gateway environment by using a Kubernetes Secret. You create the secret in the same cluster and namespace that the ingress gateway is deployed to.

    # create secret from server cert
    kubectl create secret generic gw-ssl-1-secret \
    --from-file=tls.key=${SERVER_CERT_NAME}.key \
    --from-file=tls.crt=${SERVER_CERT_NAME}.crt \
    --dry-run=client -oyaml | kubectl apply -f- \
    --namespace gloo-mesh-gateways
    
  2. If you have ingress gateway proxies in multiple clusters or namespaces, repeat the previous step for each namespace in each cluster that an ingress gateway is installed. The name of the secret must be the same in all clusters, although the secret can be in different namespaces as appropriate.

  3. Configure the VirtualGateway custom resource with the TLS details. Review the following example. For more information, see the API docs.

    kubectl apply -f - <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: VirtualGateway
    metadata:
      name: ${SERVER_CERT_NAME}
      namespace: bookinfo
    spec:
      listeners: 
      - allowedRouteTables:
        - host: $DNS_NAME
        http: {}
        port:
          number: 443
        tls:
          mode: SIMPLE
          secretName: gw-ssl-1-secret
      - allowedRouteTables:
        - host: $DNS_NAME
        http: {}
        httpsRedirect: true
        port:
          number: 80
      workloads:
      - selector:
          labels:
            istio: ingressgateway
          cluster: $CLUSTER_NAME
    EOF
    
    Review the following table to understand this configuration.
    Setting Description
    metadata.namespace Create the virtual gateway in a namespace that is in the same workspace as your ingress gateway, such as the same namespace.
    spec.listeners.allowedRouteTables You can specify the host name that the certificates are valid for, or select the route tables to use.
    spec.listeners.port Select the HTTPS port 443, or you could use the port.name to select HTTPS.
    spec.listeners.tls Configure the mode and secret to use to secure traffic. Remember that each cluster and namespace for each gateway must have the secret.
    spec.listeners.httpsRedirect In the section for port 80, set to true so that any HTTP requests from port 80 are automatically redirected to HTTPS on port 443, with a 301 Moved Permanently response code.
    spec.workloads Use a spec.selector label for the ingress gateway service service that you want the virtual gateway to configure. You installed a gateway in the setup section.

Verify the secure connection

Now that you set up your TLS certificate for your domain, verify that HTTPS requests can be sent successfully.

Production setup

If you have your own domain, certificates, DNS, and load balancer exposed on port 443 set up, you can test the secure connection by curling the HTTPS address.

  1. Save the external address of the ingress gateway. If you deployed your ingress gateway in a different namespace or with a different version, update the command.

    export INGRESS_GW_IP=$(kubectl get svc -n gloo-mesh-gateways  istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo $INGRESS_GW_IP
    
    export INGRESS_GW_IP=$(kubectl get svc -n gloo-mesh-gateways  istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    echo $INGRESS_GW_IP
    

  2. Send an HTTPS request to the domain that you set up for TLS traffic.

    curl -vik --resolve $DNS_NAME:443:$INGRESS_GW_IP https://$DNS_NAME:443 
    
    curl -vik https://$INGRESS_GW_IP:443/ -H "https://$DNS_NAME:443"
    

    Example output:

    * Added www.example.com:443:34.86.217.21 to DNS cache
    * Hostname www.example.com was found in DNS cache
    *   Trying 34.86.217.21:443...
    * Connected to www.example.com (34.86.217.21) port 443 (#0)
    * ALPN, offering h2
    * ALPN, offering http/1.1
    * successfully set certificate verify locations:
    *  CAfile: /etc/ssl/cert.pem
    *  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
    ...
    
  3. Verify that the redirect for HTTP traffic is working.

    curl -vik --resolve $DNS_NAME:80:$INGRESS_GW_IP http://$DNS_NAME:80 
    
    curl -vik http://$INGRESS_GW_IP/ -H "host: http://$DNS_NAME:80"
    

    Example output:

    * Added www.example.com:80:34.86.217.21 to DNS cache
    * Hostname www.example.com was found in DNS cache
    *   Trying 34.86.217.21:80...
    * Connected to www.example.com (34.86.217.21) port 80 (#0)
    > GET / HTTP/1.1
    > Host: www.example.com
    > User-Agent: curl/7.77.0
    > Accept: */*
    > 
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 301 Moved Permanently
    HTTP/1.1 301 Moved Permanently
    < location: https://www.example.com/
    location: https://www.example.com/
    < date: Fri, 22 Jul 2022 16:47:30 GMT
    date: Fri, 22 Jul 2022 16:47:30 GMT
    < server: istio-envoy
    server: istio-envoy
    < content-length: 0
    content-length: 0
    
    < 
    * Connection #0 to host www.example.com left intact
    

Test setup

If you do not have DNS and a load balancer or node port set up, you can still test the secure connection by tweaking a few settings.

  1. Enable port-forwarding for the Gloo Gateway workload, istio-ingressgateway, on port 443. Port-forwarding on 443 requires admin privileges, such as to run sudo on Linux or macOS. If you do not have admin privileges to listen locally on port 443, use a number greater than 1024 instead.

    
       # Note: You need sudo priviliges to listen on port 443 locally:
       sudo kubectl port-forward -n gloo-mesh-gateways  service/istio-ingressgateway 443
       
    
       # Listen on port 8443 locally:
       kubectl port-forward -n gloo-mesh-gateways  service/istio-ingressgateway 8443:443
       
  2. Send a curl request that uses the HTTPS protocol for the domain that you set up for TLS traffic.

    
       # Note: Assumes you are listening on port 443 locally
       curl -vik --resolve $DNS_NAME:443:127.0.0.1 https://$DNS_NAME:443
       
    
       # Note: Assumes you are listening on port 8443 locally
       curl -vik --resolve $DNS_NAME:8443:127.0.0.1 https://$DNS_NAME:8443
       

    Note the TLS information that is used for the request.

    * Added www.example.com:8443:127.0.0.1 to DNS cache
    * Hostname www.example.com was found in DNS cache
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to www.example.com (127.0.0.1) port 8443 (#0)
    * ALPN, offering h2
    * ALPN, offering http/1.1
    * successfully set certificate verify locations:
    *   CAfile: /etc/ssl/cert.pem
      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=www.example.com; O=gw-ssl
    *  start date: Aug  6 18:27:18 2021 GMT
    *  expire date: Aug  4 18:27:18 2031 GMT
    *  issuer: CN=www.example.com; O=gateway-root
    *  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
    * 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 0x7fb3c7010a00)
    > GET /ratings/1 HTTP/2
    > Host: www.example.com:8443
    > User-Agent: curl/7.64.1
    > Accept: */*
    >
    * Connection state changed (MAX_CONCURRENT_STREAMS == 2147483647)!
    < HTTP/2 200
    HTTP/2 200
    < content-type: application/json
    content-type: application/json
    < date: Fri, 06 Aug 2021 21:46:05 GMT
    date: Fri, 06 Aug 2021 21:46:05 GMT
    < x-envoy-upstream-service-time: 2
    x-envoy-upstream-service-time: 2
    < server: istio-envoy
    server: istio-envoy
       
    * Connection #0 to host www.example.com left intact
    

Optional: Set up routes for the Bookinfo app

Verify that you can send HTTPS requests to a sample app by creating routes for the Bookinfo services.

  1. Apply a route table resource, which allows you to define how requests to endpoints should be routed. For example, if you deploy the Bookinfo and httpbin sample apps, you can create the following route table that forwards traffic to the productpage, reviews, ratings and httpbin apps via the virtual gateway. Or, you can create route tables for your own apps by following the guides in Route requests.

    kubectl apply -f- <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: RouteTable
    metadata:
      name: www-example-com
      namespace: bookinfo
    spec:
      hosts:
        - www.example.com
      # Selects the virtual gateway you previously created
      virtualGateways:
        - name: istio-ingressgateway
          namespace: bookinfo
      http:
        # Route for the main productpage app
        - name: productpage
          matchers:
          - uri:
              prefix: /productpage
          forwardTo:
            destinations:
              - ref:
                  name: productpage
                  namespace: bookinfo
                  cluster: $CLUSTER_NAME
                port:
                  number: 9080
        # Routes all /reviews requests to the reviews-v1 or reviews-v2 apps
        - name: reviews
          labels: 
            route: reviews
          matchers:
          - uri:
              prefix: /reviews
          forwardTo:
            destinations:
              - ref:
                  name: reviews
                  namespace: bookinfo
                  cluster: $CLUSTER_NAME
                port:
                  number: 9080
        # Routes all /ratings requests to the ratings-v1 app
        - name: ratings-ingress
          labels:
            route: ratings
          matchers:
          - uri:
              prefix: /ratings
          forwardTo:
            destinations:
              - ref:
                  name: ratings
                  namespace: bookinfo
                  cluster: $CLUSTER_NAME
                port:
                  number: 9080
        # Route for the httpbin app
        - name: httpbin-ingress
          labels:
            route: httpbin
          matchers:
          - headers:
            - name: X-httpbin
          forwardTo:
            destinations:
              - ref:
                  name: httpbin
                  namespace: httpbin
                  cluster: $CLUSTER_NAME
                port:
                  number: 8000
    EOF
    
  2. Verify that you can send HTTPS requests to the product page and reviews app. Both requests must return a 200 HTTP response code.

    • productpage:
      curl -vik https://$DNS_NAME:443/productpage --resolve $DNS_NAME:443:$INGRESS_GW_IP
      

      Example output:

      HTTP/2 200 
      ...
      
    • ratings:
      curl -vik https://$DNS_NAME:443/ratings/1 --resolve $DNS_NAME:443:$INGRESS_GW_IP
      

      Example output:

      HTTP/2 200
      ...
      {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}
      
    • reviews:
      curl -vik https://$DNS_NAME:443/reviews/1 --resolve $DNS_NAME:443:$INGRESS_GW_IP
      curl -vik https://$DNS_NAME:443/reviews/2 --resolve $DNS_NAME:443:$INGRESS_GW_IP
      

      Example output:

      HTTP/2 200 
      ...
      {"id": "1","podname": "reviews-v1-55b668fc65-6pc2c","clustername": "null","reviews": [{  "reviewer": "Reviewer1",  "text": "An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!"},{  "reviewer": "Reviewer2",  "text": "Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare."}]}
      
    • httpbin:
      curl -vik https://$DNS_NAME:443/status/200 -H "X-httpbin: true" --resolve $DNS_NAME:443:$INGRESS_GW_IP
      

      Example output:

      HTTP/2 200 
      
    • productpage:
      curl -vik https://$DNS_NAME:443/productpage --resolve $DNS_NAME:443:127.0.0.1
      

      Example output:

      HTTP/2 200 
      ...
      
    • ratings:
      curl -vik https://$DNS_NAME:443/ratings/1 --resolve $DNS_NAME:443:127.0.0.1
      

      Example output:

      HTTP/2 200
      ...
      {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}
      
    • reviews:
      curl -vik https://$DNS_NAME:443/reviews/1 --resolve $DNS_NAME:443:127.0.0.1
      curl -vik https://$DNS_NAME:443/reviews/2 --resolve $DNS_NAME:443:127.0.0.1
      

      Example output:

      HTTP/2 200 
      ...
      {"id": "1","podname": "reviews-v1-55b668fc65-6pc2c","clustername": "null","reviews": [{  "reviewer": "Reviewer1",  "text": "An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!"},{  "reviewer": "Reviewer2",  "text": "Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare."}]}
      
    • httpbin:
      curl -vik https://$DNS_NAME:443/status/200 -H "X-httpbin: true" --resolve $DNS_NAME:443:127.0.0.1
      

      Example output:

      HTTP/2 200 
      

Next steps