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.
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 without sidecars.
-
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
- 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.
- 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"
- 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.
-
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
-
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.
-
Configure the
VirtualGateway
custom resource with the TLS details. Review the following example. For more information, 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: ${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
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.
-
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
-
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 ...
-
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.
-
Enable port-forwarding for the Gloo Gateway workload,
istio-ingressgateway
, on port 443. Port-forwarding on 443 requires admin privileges, such as to runsudo
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
-
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.
-
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
andhttpbin
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 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 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 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 port: number: 8000 EOF
-
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
- 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.