Secure a Gateway

Stay organized with collections Save and categorize content based on your preferences.

This page explains how you can secure a Gateway using TLS for the following traffic paths:

  • Client to Gateway traffic by attaching certificates to Gateways using any of the following methods:
    • Kubernetes Secrets
    • SSL certificates
    • Certificate Manager
  • Gateway to Backend Pods by using Kubernetes Service properties.

To learn more about Gateway security, see Gateway security.

The following examples apply to internal, external, single-cluster, and multi-cluster Gateways.

Before you begin

Before you start, make sure you have performed the following tasks:

  • Enable the Google Kubernetes Engine API.
  • Enable Google Kubernetes Engine API
  • If you want to use the Google Cloud CLI for this task, install and then initialize the gcloud CLI.

GKE Gateway controller requirements

  • For Standard, GKE version 1.24 or later.
  • Google Cloud CLI version 407.0.0 or later.
  • The Gateway API is supported on VPC-native clusters only.
  • If you are using the internal GatewayClasses, you must enable a proxy-only subnet.
  • If you are using Istio, you must upgrade Istio to one of the following versions:
    • 1.15.2 or later
    • 1.14.5 or later
    • 1.13.9 or later.
  • You cannot use the Gateway v1alpha APIs with Istio. For more information, see Kubernetes Gateways and Istio Gateways.
  • The Gateway API does not support the networking.gke.io/app-protocols annotation. Use the appProtocol field instead.

Restrictions and limitations

  • You cannot use the networking.gke.io/certmap annotation with a tls.certificateRefs on the same Gateway resource. If you reference a CertificateMap in a Gateway, GKE ignores all other TLS configurations.
  • Certificate Manager supports both self-managed and Google-managed certificates but is not compatible with regional Gateways.
  • Google-managed SSL certificates are not compatible with regional Gateways. For more information about TLS termination methods for each GatewayClass, see GatewayClass TLS support.

Secure a Gateway using a Kubernetes Secret

In this example, you configure a Gateway using a Kubernetes Secret.

Store a certificate in a Kubernetes Secret

You can use a certificate issued and validated by your certificate authority (CA) or create a self-signed certificate. The following steps use a self-signed certificate.

  1. Create a private key:

    openssl genrsa -out PRIVATE_KEY_FILE 2048
    

    Replace PRIVATE_KEY_FILE with the name of your private key file, such as private-key.pem. For more information, see Select or create a private key.

  2. Create an Open SSL configuration file:

    cat <<EOF >CONFIG_FILE
    [req]
    default_bits              = 2048
    req_extensions            = extension_requirements
    distinguished_name        = dn_requirements
    prompt                    = no
    
    [extension_requirements]
    basicConstraints          = CA:FALSE
    keyUsage                  = nonRepudiation, digitalSignature, keyEncipherment
    subjectAltName            = @sans_list
    
    [dn_requirements]
    0.organizationName        = example
    commonName                = store.example.com
    
    [sans_list]
    DNS.1                     = store.example.com
    EOF
    

    Replace CONFIG_FILE with the name for the new config file, such as config-file.cnf.

  3. Create a certificate signing request (CSR) file:

    openssl req -new -key PRIVATE_KEY_FILE \
        -out CSR_FILE \
        -config CONFIG_FILE
    

    Replace CSR_FILE with the name of the new CSR file, such as cert.pem. For more information, see Create a CSR.

  4. Sign the CSR:

    openssl x509 -req \
        -signkey PRIVATE_KEY_FILE \
        -in CSR_FILE \
        -out CERTIFICATE_FILE \
        -extfile CONFIG_FILE \
        -extensions extension_requirements \
        -days 30
    

    Replace CERTIFICATE_FILE with the path and name of the file that the command generates, such as cert-file.pem. For more information, see Sign the CSR.

  5. Create a Kubernetes TLS Secret using the key and the certificate file that you created:

    kubectl create secret tls store-example-com \
        --cert=CERTIFICATE_FILE \
        --key=PRIVATE_KEY_FILE
    

    GKE saves the certificate and key as a Kubernetes resource that you can attach to your Gateway.

Create a Gateway and HTTPRoute

  1. Save the following manifest as external-gateway.yaml:

    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: external-http
    spec:
      gatewayClassName: gke-l7-gxlb
      listeners:
      - name: https
        protocol: HTTPS
        port: 443
        tls:
          mode: Terminate
          certificateRefs:
          - name: store-example-com
    

    This manifest describes a Gateway with the following properties:

    • gatewayClassName: gke-l7-gxlb: deploys a global external HTTP(S) load balancer.
    • protocol: HTTPS and port: 443: required for enabling TLS.
    • tls: references the Kubernetes Secret created in the previous step.
  2. Apply the manifest to the cluster:

    kubectl apply -f external-gateway.yaml
    
  3. Save the following manifest as store-external-route.yaml:

    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store-external
      labels:
        gateway: external-http
    spec:
      parentRefs:
      - name: external-http
      hostnames:
      - "store.example.com"
      rules:
      - backendRefs:
        - name: store-v1
          port: 8080
    

    This manifest describes an HTTPRoute that matches traffic to store.example.com and sends it to the store-v1 Service.

  4. Apply the manifest to the cluster:

    kubectl apply -f store-external-route.yaml
    

Verify the Gateway

Verify that the Gateway works by sending a request over the internet.

  1. Get the IP address of the Gateway:

    kubectl get gateway external-http -o=jsonpath="{.status.addresses[0].value}"
    

    The output is similar to the following:

    10.128.0.12
    

    This output is a public IP address, which means any client with internet access can connect to it.

  2. Access the domain of the Gateway using curl:

    curl https://store.example.com --resolve store.example.com:443:GATEWAY_IP_ADDRESS --cacert CERTIFICATE_FILE -v
    

    Replace the following:

    • GATEWAY_IP_ADDRESS: the IP address of the Gateway load balancer.
    • CERTIFICATE_FILE: the certificate file that you generated. You must Save this file on the machine that you are using to connect to the Gateway. The certificate is required to authenticate the Gateway because the Gateway uses a self-signed certificate.

    The --resolve option resolves the domain name to the IP address of the Gateway, which is required because DNS is not configured for this domain

    The output is similar to the following:

    ...
    * 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: O=example; CN=store.example.com
    *  start date: Apr 19 15:54:50 2021 GMT
    *  expire date: Apr 19 15:54:50 2022 GMT
    *  common name: store.example.com (matched)
    *  issuer: O=example; CN=store.example.com
    *  SSL certificate verify ok.
    ...
    {
      "cluster_name": "gw",
      "host_header": "store.example.com",
      "metadata": "store-v1",
      "node_name": "gke-gw-default-pool-51ccbf30-yya8.c.agmsb-k8s.internal",
      "pod_name": "store-v1-84b47c7f58-tj5mn",
      "pod_name_emoji": "😍",
      "project_id": "agmsb-k8s",
      "timestamp": "2021-04-19T16:30:08",
      "zone": "us-west1-a"
    }
    

    This output includes a successful TLS handshake followed by a response from the application. The TLS connection is terminated at the Gateway and the application responds to the client securely.

Secure a Gateway using an SSL certificate

In this example, you configure a Gateway with a Google-managed SSL certificate.

Create an SSL certificate

  1. Create a Google-managed global SslCertificate resource:

    gcloud compute ssl-certificates create store-example-com \
        --domains=store.example.com \
        --global
    

Create a Gateway and HTTPRoute

  1. Save the following manifest as external-gateway.yaml:

    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: external-http
    spec:
      gatewayClassName: gke-l7-gxlb
      listeners:
      - name: https
        protocol: HTTPS
        port: 443
        tls:
          mode: Terminate
          options:
            networking.gke.io/pre-shared-certs: store-example-com
    

    This manifest describes a Gateway with the following properties:

    • gatewayClassName:gke-l7-gxlb: deploys an external HTTP(S) load balancer.
    • protocol:HTTPS and port:443: required for enabling TLS.
    • tls.mode:Terminate: terminates TLS using your SSL certificate.
  2. Apply the manifest to your cluster:

    kubectl apply -f external-gateway.yaml
    
  3. Save the following HTTPRoute manifest as store-external-route.yaml:

    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store-external
      labels:
        gateway: external-http
    spec:
      parentRefs:
      - name: external-http
      hostnames:
      - "store.example.com"
      rules:
      - backendRefs:
        - name: store-v1
          port: 8080
    
  4. Deploy the HTTPRoute in your cluster:

    kubectl apply -f store-external-route.yaml
    

    It might take several minutes for GKE to deploy the Gateway.

Verify the Gateway

  1. Get the IP address of the Gateway:

    kubectl get gateway external-http -o=jsonpath="{.status.addresses[0].value}"
    

    The output is similar to the following:

    10.128.0.12
    

    This output is a public IP address, which means any client with internet access can connect to it.

  2. Update an A or AAAA record to direct your domain to the IP address of the Gateway.

    This step is only necessary if you are configuring a Google-managed SSL certificate. If you are configuring a self-managed certificate, you can skip this step.

    After the DNS records are updated, it can take up to 10 minutes for your load balancer to begin using the Google-managed certificate.

  3. Verify that the Gateway is working by sending a request over the internet using curl:

    curl https://store.example.com -v
    

    The output is similar to the following:

    ...
    * 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: O=example; CN=store.example.com
    *  start date: Apr 19 15:54:50 2021 GMT
    *  expire date: Apr 19 15:54:50 2022 GMT
    *  common name: store.example.com (matched)
    *  issuer: O=example; CN=store.example.com
    *  SSL certificate verify ok.
    ...
    {
      "cluster_name": "gw",
      "host_header": "store.example.com",
      "metadata": "store-v1",
      "node_name": "gke-gw-default-pool-51ccbf30-yya8.c.agmsb-k8s.internal",
      "pod_name": "store-v1-84b47c7f58-tj5mn",
      "pod_name_emoji": "😍",
      "project_id": "agmsb-k8s",
      "timestamp": "2021-04-19T16:30:08",
      "zone": "us-west1-a"
    }
    

    This output includes a successful TLS handshake and a response from the application. TLS is terminated at the Gateway correctly and the application responds to the client securely.

Secure a Gateway using Certificate Manager

In this example, you configure a Gateway using Certificate Manager.

Create a CertificateMap

  1. Create a CertificateMap:

    gcloud certificate-manager maps create store-example-com-map
    
  2. Load your Google-managed certificate and keys into a Certificate:

    gcloud certificate-manager certificates create store-example-com-cert \
        --certificate-file="cert.pem" \
        --private-key-file="PRIVATE_KEY_FILE"
    
  3. Create a CertificateMapEntry which assigns the certificate to the CertificateMap:

    gcloud certificate-manager maps entries create store-example-com-map-entry \
        --map=store-example-com-map \
        --hostname=store.example.com \
        --certificates=store-example-com-cert
    

Create a Gateway and HTTPRoute

  1. Save the following manifest as cert-map-gateway.yaml:

    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: external-http
      annotations:
        networking.gke.io/certmap: store-example-com-map
    spec:
      gatewayClassName: gke-l7-gxlb
      listeners:
      - name: https
        protocol: HTTPS
        port: 443
    

    This manifest describes a Gateway with the following properties:

    • gatewayClassName: gke-l7-gxlb: deploys an global external HTTP(S) load balancer.
    • protocol: HTTPS and port: 443: required for enabling TLS.

    There is no TLS section because TLS is configured with Certificate Manager using the annotation networking.gke.io/certmap.

  2. Apply the manifest to the cluster:

    kubectl apply -f cert-map-gateway.yaml
    

    It might take several minutes for GKE to deploy the Gateway.

  3. Save the following manifest as cert-map-http-route.yaml:

    apiVersion: gateway.networking.k8s.io/v1beta1
    kind: HTTPRoute
    metadata:
      name: foo
      namespace: default
    spec:
      parentRefs:
      - name: external-http
      hostnames:
      - foo.example.com
      rules:
      - matches:
        - path:
            value: /
        backendRefs:
        - name: foo-v1
          port: 8080
    
  4. Apply the manifest to the cluster:

    kubectl apply -f cert-map-http-route.yaml
    

Verify the Gateway

  1. Get the IP address of the Gateway:

    kubectl get gateway external-http -o=jsonpath="{.status.addresses[0].value}"
    

    The output is similar to the following:

    10.128.0.12
    

    This output is a public IP address, which means any client with internet access can connect to it.

  2. Update an A or AAAA record to direct your domain to the IP address of the Gateway.

    This step is only necessary if you are configuring a Google-managed SSL Certificate. If you are configuring a self-managed certificate, you can skip this step.

    After the DNS records are updated, it can take up to 10 minutes for your load balancer to begin using the Google-managed certificate.

  3. Access the domain of the Gateway using curl:

    curl https://store.example.com -v
    

    The --resolve option resolves the domain name to the IP address of the Gateway, which is required because DNS is not configured for this domain.

    The output is similar to the following:

    ...
    * 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: O=example; CN=store.example.com
    *  start date: Apr 19 15:54:50 2021 GMT
    *  expire date: Apr 19 15:54:50 2022 GMT
    *  common name: store.example.com (matched)
    *  issuer: O=example; CN=store.example.com
    *  SSL certificate verify ok.
    ...
    {
      "cluster_name": "gw",
      "host_header": "store.example.com",
      "metadata": "store-v1",
      "node_name": "gke-gw-default-pool-51ccbf30-yya8.c.agmsb-k8s.internal",
      "pod_name": "store-v1-84b47c7f58-tj5mn",
      "pod_name_emoji": "😍",
      "project_id": "agmsb-k8s",
      "timestamp": "2021-04-19T16:30:08",
      "zone": "us-west1-a"
    }
    

    This output includes a successful TLS handshake and a response from the application. TLS is terminated at the Gateway correctly and the application responds to the client securely.

Secure load balancer to application traffic using TLS

You can encrypt traffic from the load balancer to backend Pods using the ports[].appPrococol field. The supported fields for appProcotol are: HTTP, HTTPS, and HTTP2.

The following manifest describes a Service that specifies the load balancer must use HTTPS traffic to communication with the backend Pods:

apiVersion: v1
kind: Service
metadata:
  name: store-v2
spec:
  selector:
    app: store
    version: v2
  ports:
  - port: 8080
    targetPort: 8080
    appProtocol: HTTPS

The load balancer does not verify the certificate used by backend Pods. It is your responsibility to ensure the certificate used on the backend Pods is valid.

What's next