Configura Google Cloud Armor a través de Ingress

En esta página, se muestra cómo usar un recurso personalizado de BackendConfig para configurar Google Cloud Armor en Google Kubernetes Engine (GKE).

Descripción general

En un clúster de GKE, el tráfico entrante se controla mediante el balanceo de cargas de HTTP(S) externo, que es un componente de Cloud Load Balancing. Por lo general, el balanceador de cargas de HTTP(S) se configura mediante un controlador Ingress de GKE, que obtiene la información de configuración de un objeto Ingress de Kubernetes. El objeto Ingress se asocia a uno o más objetos de servicio. Cada objeto de servicio contiene datos de enrutamiento que se usan para direccionar una solicitud entrante a un pod o puerto específico.

A partir de la versión 1.10.5-gke.3 de Kubernetes, tienes la posibilidad de agregar una configuración adicional al balanceador de cargas externo; para ello, debes vincular un puerto de servicio a un recurso personalizado llamado BackendConfig.

El controlador de Ingress de GKE lee la información de configuración de BackendConfig y configura el balanceador de cargas externo según corresponda. Un BackendConfig tiene información de configuración específica de Cloud Load Balancing. Los recursos de Ingress y servicio de Kubernetes no ofrecen una manera de configurar características específicas de los proveedores, como Google Cloud Armor. BackendConfig te permite establecer esa configuración.

A continuación, se detalla el panorama general de cómo configurar un BackendConfig en este ejercicio:

  1. Crea un BackendConfig.
  2. Crea un servicio y asocia uno de sus puertos al BackendConfig.
  3. Crea un Ingress y asócialo con el par (servicio, puerto).

Antes de comenzar

Antes de comenzar, asegúrate de haber realizado las siguientes tareas:

Establece la configuración de gcloud predeterminada mediante uno de los siguientes métodos:

  • Usa gcloud init si deseas ver una explicación sobre cómo configurar parámetros predeterminados.
  • Usa gcloud config para establecer el ID, la zona y la región del proyecto de manera individual.

Usa gcloud init

Si recibes el error One of [--zone, --region] must be supplied: Please specify location, completa esta sección.

  1. Ejecuta gcloud init y sigue las instrucciones:

    gcloud init

    Si usas SSH en un servidor remoto, usa la marca --console-only para evitar que el comando abra un navegador:

    gcloud init --console-only
  2. Sigue las instrucciones a fin de autorizar a gcloud para que use tu cuenta de Google Cloud.
  3. Crea una configuración nueva o selecciona una existente.
  4. Elige un proyecto de Google Cloud.
  5. Elige una zona predeterminada de Compute Engine.

Usa gcloud config

  • Establece tu ID del proyecto predeterminado:
    gcloud config set project project-id
  • Si trabajas con clústeres zonales, establece tu zona de procesamiento predeterminada:
    gcloud config set compute/zone compute-zone
  • Si trabajas con clústeres regionales, establece tu región de procesamiento predeterminada:
    gcloud config set compute/region compute-region
  • Actualiza gcloud a la versión más reciente:
    gcloud components update

Crea un espacio de nombres

Crea un espacio de nombres de Kubernetes para los objetos en esta guía:

kubectl create namespace cloud-armor-how-to

Cómo crear una implementación

En este manifiesto de implementación, se declara que quieres ejecutar dos réplicas de la aplicación web hello-app:

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: cloud-armor-how-to
  name: my-deployment
spec:
  selector:
    matchLabels:
      app: hello-app
  replicas: 2
  template:
    metadata:
      labels:
        app: hello-app
    spec:
      containers:
      - name: hello-app-container
        image: gcr.io/google-samples/hello-app:1.0
        ports:
        - containerPort: 8080

Copia el manifiesto en un archivo llamado my-deployment.yaml y crea la implementación:

kubectl apply -f my-deployment.yaml

Crea una regla y una política de seguridad de Google Cloud Armor

Las políticas de seguridad de Google Cloud Armor admiten lo siguiente:

  • Listas de IP denegadas y permitidas
  • Códigos de región denegados y permitidos (Beta)
  • Reglas preconfiguradas para mitigar los ataques de secuencias de comandos entre sitios (XSS) o inyección de SQL (SQLi).
  • Lenguajes de reglas personalizadas (Beta)

Crea una política de seguridad de Google Cloud Armor:

gcloud beta compute security-policies create ca-how-to-security-policy \
    --description "policy for Google Cloud Armor how-to topic"

Crea una regla para la política de seguridad:

gcloud beta compute security-policies rules create 1000 \
    --security-policy ca-how-to-security-policy \
    --description "Deny traffic from 192.0.2.0/24." \
    --src-ip-ranges "192.0.2.0/24" \
    --action "deny-404"

Observa la política de seguridad:

gcloud beta compute security-policies describe ca-how-to-security-policy

Resultado:

...
kind: compute#securityPolicy
name: ca-how-to-security-policy
rules:
- action: deny(404)
  description: Deny traffic from 192.0.2.0/24.
  kind: compute#securityPolicyRule
  match:
    config:
      srcIpRanges:
      - 192.0.2.0/24
    versionedExpr: SRC_IPS_V1
  preview: false
  priority: 1000
  ...

Cómo crear un BackendConfig

A continuación, se detalla un manifiesto para un BackendConfig. El manifiesto especifica una política de seguridad:

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  namespace: cloud-armor-how-to
  name: my-backend-config
spec:
  securityPolicy:
    name: "ca-how-to-security-policy"

Copia el manifiesto en un archivo denominado my-backend-config.yaml y crea el BackendConfig:

kubectl apply -f my-backend-config.yaml

Observa el BackendConfig:

kubectl get backendconfig my-backend-config --namespace cloud-armor-how-to --output yaml

El resultado muestra la política de seguridad especificada:

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: my-backend-config
  namespace: cloud-armor-how-to
  ...
spec:
  securityPolicy:
    name: ca-how-to-security-policy

Cómo crear un servicio

Aquí hay un manifiesto para un servicio:

apiVersion: v1
kind: Service
metadata:
  namespace: cloud-armor-how-to
  name: my-service
  labels:
    app: hello-app
  annotations:
    cloud.google.com/backend-config: '{"ports": {"80":"my-backend-config"}}'
spec:
  type: NodePort
  selector:
    app: hello-app
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080

Guarda el manifiesto en un archivo denominado my-service.yaml y crea el servicio:

kubectl apply -f my-service.yaml

Observa el servicio:

kubectl get service my-service --namespace cloud-armor-how-to --output yaml

El resultado es similar al siguiente ejemplo:

apiVersion: v1
kind: Service
metadata:
  annotations:
    cloud.google.com/backend-config: '{"ports": {"80":"my-backend-config"}}'
  labels:
    app: hello-app
  name: my-service
  namespace: cloud-armor-how-to
  ...
spec:
  clusterIP: 10.19.249.137
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 32629
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: hello-app
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

A los fines de este ejercicio, estos son los aspectos importantes a tener en cuenta sobre tu servicio:

  • El puerto 80 del servicio está asociado a una BackendConfig con el nombre my-backend-config. En la anotación cloud.google.com/backend-config, se especifica lo siguiente.

  • El servicio tiene el tipo NodePort. Este es el tipo requerido para los servicios que se van a asociar a un Ingress.

  • Cualquier pod que tenga la etiqueta app: hello-app es miembro del servicio. Esto se especifica en el campo selector.

  • El tráfico dirigido al servicio en el puerto TCP 80 se enruta al puerto TCP 8080 en uno de los pods miembro. Esto se especifica en los campos port y targetPort.

Cómo reservar una dirección IP externa estática

Reserva una dirección IP externa estática:

gcloud compute addresses create cloud-armor-how-to-address --global

Observa una dirección IP externa estática:

gcloud compute addresses list --filter "name=cloud-armor-how-to-address"

Resultado:

NAME                        REGION  ADDRESS        STATUS
cloud-armor-how-to-address          203.0.113.2    RESERVED

Cómo crear un Ingress

Este es un manifiesto para un Ingress:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: cloud-armor-how-to
  name: my-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "cloud-armor-how-to-address"
spec:
  backend:
    serviceName: my-service
    servicePort: 80

Copia el manifiesto en un archivo con el nombre my-ingress.yaml y crea el Ingress:

kubectl create -f my-ingress.yaml

Espera unos minutos a que el controlador de Ingress de Kubernetes configure un balanceador de cargas de Cloud y, luego, observa el Ingress:

kubectl get ingress my-ingress --output yaml --namespace cloud-armor-how-to

Resultado:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  namespace: cloud-armor-how-to
  ...
spec:
  backend:
    serviceName: my-service
    servicePort: 80
status:
  loadBalancer:
    ingress:
    - ip: 203.0.113.2

A los fines de este ejercicio, estas son las cuestiones importantes que debes tener en cuenta sobre tu Ingress:

  • La dirección IP para el tráfico entrante aparece en loadBalancer:ingress:.

  • El Ingress tiene una regla que se aplica a las solicitudes HTTP entrantes desde cualquier host. Esto se debe a que no existe un campo host en la regla. Por eso, de manera predeterminada, se aplica la regla a todos los hosts.

  • Todas las solicitudes entrantes se tratan de la misma manera, independientemente de la ruta de URL. Esto se especifica mediante el valor path /*.

  • Las solicitudes entrantes se enrutan a un pod miembro de my-service. En este ejercicio, los pods miembros tienen la etiqueta app: hello-app.

  • Las solicitudes se enrutan al pod en el puerto de destino especificado en my-service. En este ejercicio, el puerto de destino del pod es 8080.

Cómo ver la aplicación web

Espera unos minutos. Luego, en tu navegador ingresa tu dirección IP externa estática.

En la página se muestra la respuesta de la aplicación web hello-app que se está ejecutando en uno de los pods de tu implementación:

Hello, world!
Version: 1.0.0
Hostname: my-deployment-574ddbdf88-f9fbj

Usa curl para ver la aplicación web:

curl -v static-address

donde static-address es tu dirección IP externa estática.

El resultado es la respuesta de hello-app:

Hello, world!
Version: 1.0.0
Hostname: my-deployment-574ddbdf88-zpb94

Cómo desvincular la política de seguridad

Para desvincular la política de seguridad desde un Ingress, deja vacío el nombre de la política en BackendConfig. A continuación, se muestra un ejemplo de la desconexión de una política de seguridad:

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  namespace: cloud-armor-how-to
  name: my-backend-config
spec:
  securityPolicy:
    name: ""

Soluciona problemas

No se encontró el BackendConfig

Este error se muestra cuando un BackendConfig para un puerto de servicio se especifica en la anotación del servicio, pero no se pudo encontrar ese recurso BackendConfig. Esto puede ocurrir si no creaste el recurso BackendConfig, si lo creaste en el espacio de nombres equivocado o si escribiste mal la referencia en la anotación del servicio.

kubectl get event
KIND    ... SOURCE
Ingress ... loadbalancer-controller

MESSAGE
Error during sync: error getting BackendConfig for port 80 on service "default/my-service":
no BackendConfig for service port exists

No se encontró la política de seguridad

Después de crear el objeto Ingress, si la política de seguridad no está asociada de forma correcta al servicio de balanceador de cargas, evalúa el evento de Kubernetes para ver si hay un error de configuración. Específicamente, si tu BackendConfig especifica una política inexistente, se emitirá un evento de advertencia periódicamente. Para solucionar este problema, asegúrate de especificar la política de seguridad correcta, por nombre, en tu BackendConfig.

kubectl get event
KIND    ... SOURCE
Ingress ... loadbalancer-controller

MESSAGE
Error during sync: The given security policy "my-policy" does not exist.

Limpieza

Luego de completar los ejercicios de esta página, sigue estos pasos para quitar los recursos y evitar cargos no deseados en tu cuenta:

Borra los objetos de Kubernetes que creaste para este ejercicio:

kubectl delete ingress my-ingress --namespace cloud-armor-how-to
kubectl delete service my-service --namespace cloud-armor-how-to
kubectl delete backendconfig my-backend-config --namespace cloud-armor-how-to
kubectl delete deployment my-deployment --namespace cloud-armor-how-to
kubectl delete namespace cloud-armor-how-to

Borra tu dirección IP externa estática:

gcloud compute addresses delete cloud-armor-how-to-address --global

Borra la política de seguridad:

gcloud beta compute security-policies delete ca-how-to-security-policy

Qué sigue