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.
Before you begin
- Install Gloo Gateway in a single or multicluster setup, which includes installing Gloo components, an ingress gateway, a workspace, and workspace settings.
- Deploy the Bookinfo app.
-
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. Theopenssl
version must be at least 1.1.- Check the
openssl
version that is installed. If you see LibreSSL in the output, continue to the next step.openssl version
- Install the OpenSSL version (not LibreSSL). For example, you might use Homebrew.
brew install openssl
- 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...
- For example,
- Check the
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.
- 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:*"
- 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.
-
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
-
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.
-
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.
Review the following table to understand this configuration.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
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.
-
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
-
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.
-
To open a connection to the gateway service, run
minikube tunnel
. -
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
-
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: *
-
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.
-
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
-
-
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
- Review other routing examples, such as header matching, redirects, or direct responses that you can configure for your API Gateway.
- Explore other policies that you can apply to your routes and upstream services. For example, you might apply the proxy protocol policy to your API Gateway so that it preserves connection information such as the originating client IP address.