Simple HTTPS/TLS

Open an HTTPS listener on your gateway to serve incoming HTTPS traffic. After the gateway receives and accepts HTTPS traffic, 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.
  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...

Create self-signed TLS certificates

Create a self-signed root certificate that you use to sign a server certificate for the HTTPS/TLS listener on your gateway.

In production environments, you might have a TLS certificate and key pair from a certificate management tool that you use. In this case, continue with Set up an HTTPS/TLS listener.

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

  1. Create a self-signed root certificate. The following command creates a root certificate that is valid for a year and that can serve any hostname. You use this certificate to sign the server certificate for the gateway later. For other command options, see the OpenSSL docs.
    # root cert
    openssl req -new -newkey rsa:4096 -x509 -sha256 \
        -days 365 -nodes -out root.crt -keyout root.key \
        -subj "/CN=*/O=root" \
        -addext "subjectAltName = DNS:*"
    
  2. Use the root certificate to sign the server certificate for the gateway.
    # server cert
    cat > "gateway.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 = *
    EOF
    
    openssl genrsa -out "gateway.key" 2048
    openssl req -new -key "gateway.key" -out gateway.csr -subj "/CN=*/O=root" -config "gateway.conf"
    openssl x509 -req \
      -days 3650 \
      -CA root.crt -CAkey root.key \
      -set_serial 0 \
      -in gateway.csr -out gateway.crt \
      -extensions v3_req -extfile "gateway.conf"
    

Set up an HTTPS/TLS listener

Configure your gateway with the server TLS certificate to start serving HTTPS traffic on your gateway.

  1. Create a Kubernetes secret to store the TLS certificate for the gateway. You create the secret in the same cluster and namespace that the Istio ingress gateway pod is deployed to. If you followed the Getting started guide, the ingress gateway pod is deployed to the gloo-mesh-gateways namespace by default.

    kubectl create secret generic gw-ssl-1-secret \
    --from-file=tls.key=gateway.key \
    --from-file=tls.crt=gateway.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 Istio ingress gateway pod 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 an HTTPS listener on your gateway. The following example adds the HTTPS listener to the virtual gateway resource that you created as part of the Getting started guide guide. For more information about virtual gateways, see the API docs.

    kubectl apply -f - <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: VirtualGateway
    metadata:
      name: istio-ingressgateway
      namespace: bookinfo
    spec:
      listeners: 
      - allowedRouteTables:
        - host: '*'
        http: {}
        port:
          number: 443
        tls:
          mode: SIMPLE
          secretName: gw-ssl-1-secret
      - allowedRouteTables:
        - host: '*'
        http: {}
        httpsRedirect: true
        port:
          number: 80
      workloads:
      - selector:
          labels:
            istio: ingressgateway
    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.
    spec.listeners.allowedRouteTables You can specify the hostname 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 TLS mode and secret to use to secure traffic. The SIMPLE TLS mode ensures that incoming TLS connections are terminated at the gateway, and that the unencrypted HTTP traffic is forwarded to the destination. Note that if you set up multiple gateways in multiple namespaces or clusters, each of those clusters and namespaces must have the TLS secret.
    spec.workloads Use a workload selector label for the ingress gateway service that you want the virtual gateway to configure. In this example, you want to configure the Istio ingress gateway that you set up as of the Getting started guide guide.

Verify the secure connection

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

  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_ADDRESS=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo $INGRESS_GW_ADDRESS
    
    export INGRESS_GW_HOSTNAME=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    echo $INGRESS_GW_HOSTNAME
    export INGRESS_GW_ADDRESS=$(dig +short ${INGRESS_GW_HOSTNAME} | head -1)
    echo $INGRESS_GW_ADDRESS
    

  2. Send a request to the httpbin app on the https.example.com domain. Verify that you see a successful TLS handshake, the server TLS certificate that you created earlier, and a 200 HTTP response code from the httpbin app.

    curl -vik -H "X-httpbin: true" --resolve "www.example.com:443:${INGRESS_GW_ADDRESS}" https://www.example.com:443/status/200
    

    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
    * ALPN, server accepted to use h2
    * Server certificate:
    *  subject: CN=*; O=root
    *  start date: Dec 15 19:12:08 2023 GMT
    *  expire date: Dec 12 19:12:08 2033 GMT
    *  issuer: CN=*; O=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 0x150012400)
    > GET /status/200 HTTP/2
    > Host: www.example.com
    > user-agent: curl/7.77.0
    > accept: */*
    > x-httpbin: true
    > 
    * Connection state changed (MAX_CONCURRENT_STREAMS == 2147483647)!
    < HTTP/2 200 
    HTTP/2 200 
    < access-control-allow-credentials: true
    access-control-allow-credentials: true
    < access-control-allow-origin: *
    access-control-allow-origin: *
    < date: Fri, 15 Dec 2023 19:17:20 GMT
    date: Fri, 15 Dec 2023 19:17:20 GMT
    < content-length: 0
    content-length: 0
    < x-envoy-upstream-service-time: 0
    x-envoy-upstream-service-time: 0
    < server: istio-envoy
    server: istio-envoy
    

Verify HTTPS listeners in local test setups

If you test locally by using minikube or kind, or if you have insufficient permissions in your cloud platform, the load balancer service that exposes the Istio ingress gateway is not assigned an external IP address or hostname and remains in a <pending> state. You can follow the steps in this guide to verify that your HTTPS listener works.

  1. To open a connection to the gateway service, run minikube tunnel.

  2. Get the IP address that was assigned to the Istio ingress gateway service.

    export INGRESS_GW_ADDRESS=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo $INGRESS_GW_ADDRESS
    
  3. Send a request to the httpbin app. Verify that you see a successful TLS handshake, the server TLS certificate that you created earlier, and a 200 HTTP response code from the httpbin app.

    curl -vik -H "X-httpbin: true" --resolve www.example.com:443:${INGRESS_GW_ADDRESS} https://www.example.com:443/status/200
    

    Example output:

    * 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=*; O=*
    *  start date: Sep 11 14:53:24 2023 GMT
    *  expire date: Sep 10 14:53:24 2024 GMT
    *  issuer: CN=*; O=*
    *  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 0x158813c00)
    > GET /status/200 HTTP/2
    > Host: www.example.com
    > user-agent: curl/7.77.0
    > accept: */*
    > 
    * Connection state changed (MAX_CONCURRENT_STREAMS == 2147483647)!
    < HTTP/2 200 
    HTTP/2 200 
    < access-control-allow-credentials: true
    access-control-allow-credentials: true
    < access-control-allow-origin: *
    access-control-allow-origin: *
    
  1. Set up a Kubernetes load balancer solution, such as MetalLB to assign an IP address from your local kind network to the Istio ingress gateway service.

  2. Enable port-forwarding for the gateway 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, such 8443.

    • Admin privileges:

      sudo kubectl port-forward -n gloo-mesh-gateways  service/istio-ingressgateway 443
      
    • No admin privileges:

      kubectl port-forward -n gloo-mesh-gateways  service/istio-ingressgateway 8443:443
      
  3. Send a request to the httpbin app. Verify that you see a successful TLS handshake, the server TLS certificate that you created earlier, and a 200 HTTP response code from the httpbin app. Update the command to use the port that you port-forwarded your gateway service to in the previous step.

    curl -vik -H "X-httpbin: true" --resolve www.example.com:443:127.0.0.1 https://www.example.com:443/status/200
    

    Example output:

    * 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=*; O=*
    *  start date: Sep 11 14:53:24 2023 GMT
    *  expire date: Sep 10 14:53:24 2024 GMT
    *  issuer: CN=*; O=*
    *  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: wwww.example.com
    > 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
    < x-envoy-upstream-service-time: 2
    x-envoy-upstream-service-time: 2
       
    * Connection #0 to host www.example.com left intact
    

Next steps