Secure a Gateway


This page explains how you can secure a Gateway using various security features:

  • SSL Policies to ensure the Gateway is using the required secure protocols and algorithms

  • Certificates to secure Client-to-Gateway and Gateway-to-Backends traffic with TLS

  • Google Cloud Armor security policy to protect Services from DDoS attacks

  • Identity-Aware Proxy (IAP) to provide a layer of authentication and authorization before allowing access to a Service

To learn more about Gateway security, see Gateway security.

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. If you previously installed the gcloud CLI, get the latest version by running gcloud components update.

GKE Gateway controller requirements

  • For Standard, GKE version 1.24 or later.
  • For Autopilot, GKE version 1.26 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.
  • Your cluster must have the HttpLoadBalancing add-on enabled.
  • 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.
  • If you are using Shared VPC, then in the host project, you need to assign the Compute Network User role to the GKE Service account for the service project.

Restrictions and limitations

In addition to the GKE Gateway controller restrictions and limitations, the following limitations apply specifically to Gateway security:

  • 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 will treat this as an error.
  • Certificate Manager supports both self-managed and Google-managed certificates but is not compatible with regional Gateways.
  • When using Google-managed SSL certificates, you must create the SSL certificates outside of GKE before you attach them to your Gateway.

  • Google-managed SSL certificates are not compatible with regional Gateways. For more information about TLS termination methods for each GatewayClass, see GatewayClass TLS support.

  • The Gateway controller does not support the ManagedCertificate resource.

  • The Gateway controller does not support the networking.gke.io/managed-certificates annotation.

  • The appProtocol field in the Service configuration only accepts capital letters for the protocol value (HTTP, HTTPS or HTTP2). Using lowercase letters results in using HTTP as the protocol with the backends.

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-global-external-managed
      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-global-external-managed: deploys a global external Application 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:

    203.0.113.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"
      # Several lines of output omitted here.
    }
    

    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-global-external-managed
      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-global-external-managed: deploys a global external Application 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:

    203.0.113.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 self-managed certificate and key 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-global-external-managed
      listeners:
      - name: https
        protocol: HTTPS
        port: 443
    

    This manifest describes a Gateway with the following properties:

    • gatewayClassName: gke-l7-global-external-managed: deploys a global external Application 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:

    203.0.113.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 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[].appProtocol field. The supported fields for appProtocol 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.

Secure client to load balancer traffic using SSL policies

When your applications are exposed through an external gateway that uses HTTPS, it is important to use the latest protocols or specify the minimum SSL or TLS version. You can secure the client to load balancer traffic by using SSL policies.

To know more about SSL policies that can be attached to your Gateway and how to create them, see Configure SSL Policies to secure client to load balancer traffic.

Protect your backends using Google Cloud Armor

Google Cloud Armor security policies help you protect your load-balanced applications from web-based attacks. Once you have configured a Google Cloud Armor security policy, you can reference it in a GCPBackendPolicy applied to your Kubernetes Services.

To configure Google Cloud Armor policies with Gateway, see Configure Google Cloud Armor security policy to secure your backend Services.

Authenticate requests to your backends using Identity-Aware Proxy

Identity-Aware Proxy helps you protect your backends from unwanted traffic by authenticating clients sending requests to your applications and enforcing role-based traffic authorization. After you enable Identity-Aware Proxy for GKE, you can reference your OAuth credentials in a GCPBackendPolicy applied to your Kubernetes Services.

To configure Identity-Aware Proxy with Gateway, see Configure Identity-Aware Proxy.

What's next