Simplificando la Gestión de API en Kubernetes: Una Guía Práctica de Kuadrant.io

Gemini Generated Image 5apdq45apdq45apd

En el panorama actual de los microservicios en Kubernetes, la gestión eficaz de las API, la seguridad y la implementación de políticas de uso justo representan desafíos significativos para los equipos de desarrollo e infraestructura.

Kuadrant.io emerge como una solución open-source diseñada para abordar estas complejidades. El proyecto permite gestionar, asegurar y limitar el acceso a servicios expuestos en Kubernetes, aprovechando la infraestructura estándar como Istio/Envoy y la Kubernetes Gateway API.

Las Capacidades Clave de Kuadrant

Kuadrant ofrece una capa de gestión de API centrada en el ecosistema nativo de la nube:

  • Seguridad Declarativa: Facilita la aplicación de autenticación y autorización (por ejemplo, OAuth2/OIDC) a las APIs. Esto se logra sin requerir modificaciones en el código fuente de los microservicios subyacentes.
  • Gestión de Tráfico y Uso: Proporciona un mecanismo robusto de Limitación de Tasa (Rate Limiting) para prevenir sobrecargas en los backends y asegurar un consumo equitativo de los recursos de la API.
  • Integración Nativa: Utiliza Custom Resource Definitions (CRDs) que se integran directamente con Kubernetes, ofreciendo una experiencia de configuración familiar.
    Componentes Fundamentales de la Arquitectura de Kuadrant

Componentes Fundamentales de la Arquitectura de Kuadrant

Kuadrant opera a través de varios componentes principales que se ejecutan dentro del clúster de Kubernetes y trabajan en conjunto con el Ingress Controller (como Istio o Envoy) para aplicar las políticas:

  • Kuadrant Controller: Actúa como el cerebro de la Gestión de API. Su función es observar los recursos de política de Kuadrant (como RateLimitPolicy o AuthPolicy) y traducirlos a la configuración específica necesaria para el data plane.
  • Authorino: Es el Servicio de Autorización. Maneja la autenticación (OIDC, OAuth2, claves API) y la autorización, comunicándose con el Ingress.
  • Limitador: Es el Servicio de Limitación de Tasa. Se encarga de mantener el estado de las cuentas de las tasas de uso (rate limits) y es el que decide si una solicitud debe ser permitida o rechazada (devolviendo HTTP 429).
  • CRDs (Custom Resource Definitions): Definen los recursos declarativos de Kuadrant (RateLimitPolicy, AuthPolicy, etc.), permitiendo a los usuarios gestionar las políticas de API de forma nativa en Kubernetes.
  • Data Plane: El Ingress Controller subyacente (e.g., Istio, Envoy) funciona como el punto de aplicación (enforcement point), interceptando el tráfico y consultando a Authorino y Limitador para obtener la decisión de política.

Puesta en Acción

Prerequisitos

Para probar Kuadrant, es necesario contar con un clúster de Kubernetes. En esta publicación se usará kind en forma local.

Para utilizar Kuadrant, se requiere el tipo de servicio LoadBalancer para los Gateways. Dado que kind no cuenta con un mecanismo nativo para asignar direcciones IP a este tipo de servicios, se puede seguir esta guía para configurar un proveedor de LoadBalancer en kind.

Son necesarios los clientes de linea de comandos kubectl y helm

Establecer las variables de entorno

export KUADRANT_GATEWAY_NS=api-gateway # Namespace for the example Gateway
export KUADRANT_GATEWAY_NAME=external # Name for the example Gateway
export KUADRANT_DEVELOPER_NS=node-api # Namespace for an example node-api app
export KUADRANT_CLUSTER_ISSUER_NAME=selfsigned # Name for the ClusterIssuer

Instalar Istio Service Mesh

En este ejemplo se utilizará Istio como proveedor de Gateway API, y Sail Operator proporcionará la instalación de la Gateway API de Istio.

helm install sail-operator \
        --create-namespace \
        --namespace istio-system \
        --wait \
        --timeout=300s \
        https://github.com/istio-ecosystem/sail-operator/releases/download/0.1.0/sail-operator-0.1.0.tgz
kubectl apply -f -<<EOF
apiVersion: sailoperator.io/v1alpha1
kind: Istio
metadata:
  name: default
spec:
  # Supported values for sail-operator v0.1.0 are [v1.22.4,v1.23.0]
  version: v1.23.0
  namespace: istio-system
  # Disable autoscaling to reduce dev resources
  values:
    pilot:
      autoscaleEnabled: false
EOF

Desplegar Gateway API CRDs

Para habilitar los CRDs, se debe ejecutar el siguiente comando en una terminal que tenga una sesión iniciada en el clúster.

kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml

Desplegar el Operador Cert Manager

Cert Manager gestiona los certificados TLS para los componentes y los Gateways. Esta herramienta consume los recursos Certificate creados por el operador de Kuadrant en respuesta a la TLSPolicy.

helm repo add jetstack https://charts.jetstack.io --force-update
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.15.3 \
  --set crds.enabled=true

Después de instalar el operador, crear un nuevo recurso ClusterIssuer, para el manejo de certificados para este ejemplo auto firmados.

kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: ${KUADRANT_CLUSTER_ISSUER_NAME}
spec:
  selfSigned: {}
EOF

Desplegar el Operador Kuadrant

helm repo add kuadrant https://kuadrant.io/helm-charts/ --force-update
helm install \
 kuadrant-operator kuadrant/kuadrant-operator \
 --create-namespace \
 --namespace kuadrant-system

Tras instalar el operador, crear un recurso Kuadrant para instalar los componentes del operador.

kubectl apply -f -<<EOF
kind: Kuadrant
apiVersion: kuadrant.io/v1beta1
metadata:
 name: kuadrant
 namespace: kuadrant-system
spec: {}
EOF

Desplegar API ejemplo.

Se desplegará una API sencilla de Node.js en Kubernetes que devuelve una lista de libros. En los pasos subsiguientes, se conectará, asegurará y protegerá dicha API utilizando Kuadrant.

kubectl create ns ${KUADRANT_DEVELOPER_NS}
kubectl create deployment ${KUADRANT_DEVELOPER_NS} --image=quay.io/vravula_redhat/node-api:test -n ${KUADRANT_DEVELOPER_NS}
kubectl expose deployment ${KUADRANT_DEVELOPER_NS} --port=8080 --target-port=8080 -n ${KUADRANT_DEVELOPER_NS}

Desplegar Gateway y HTTPRoute

Este es el gateway mediante el cual se permite el tráfico de entrada (ingress) al clúster. En el siguiente YAML, el Gateway representa la instanciación de un balanceador de carga lógico, mientras que la GatewayClass define la plantilla que se utiliza al crear un Gateway.

Usaremos como DNS el servicio nip.io para resolver el nombre de los servicios ofrecidos por kind.

kubectl create ns ${KUADRANT_GATEWAY_NS}
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
 name: ${KUADRANT_GATEWAY_NAME}
 namespace: ${KUADRANT_GATEWAY_NS}
spec:
 gatewayClassName: istio
 listeners:
   - allowedRoutes:
       namespaces:
         from: All
     name: api
     hostname: "*.nip.io"
     port: 443
     protocol: HTTPS
     tls:
       mode: Terminate
       certificateRefs:
         - name: api-${KUADRANT_GATEWAY_NAME}-tls
           kind: Secret
EOF

Desplegar HTTPRoute direccionando hacia el servicio en la API de ejemplo.

kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
 name: ${KUADRANT_DEVELOPER_NS}
 namespace: ${KUADRANT_DEVELOPER_NS}
spec:
 parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: ${KUADRANT_GATEWAY_NAME}
     namespace: ${KUADRANT_GATEWAY_NS}
 rules:
   - backendRefs:
       - group: ''
         kind: Service
         name: ${KUADRANT_DEVELOPER_NS}
         namespace: ${KUADRANT_DEVELOPER_NS}
         port: 8080
         weight: 1
     matches:
       - path:
           type: PathPrefix
           value: /
EOF

Verificar el estado del Gateway para asegurar que se encuentre Accepted (aceptado) y Programmed (programado)

kubectl get gateway ${KUADRANT_GATEWAY_NAME} -n ${KUADRANT_GATEWAY_NS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}{"\n"}{.status.conditions[?(@.type=="Programmed")].message}'

Al revisar el estado del listener, se observará que aún no está programado ni listo para aceptar tráfico debido a una configuración TLS incorrecta. Esto se solucionará en el siguiente paso mediante la TLSPolicy.

kubectl get gateway ${KUADRANT_GATEWAY_NAME} -n ${KUADRANT_GATEWAY_NS} -o=jsonpath='{.status.listeners[0].conditions[?(@.type=="Programmed")].message}'

Crear una nueva TLSPolicy

Una TLSPolicy apunta a los recursos Gateways de la Gateway API para proporcionar TLS a los listeners del gateway, gestionando el ciclo de vida de los certificados TLS mediante CertManager.

kubectl apply -f - <<EOF
apiVersion: kuadrant.io/v1
kind: TLSPolicy
metadata:
  name: ${KUADRANT_GATEWAY_NAME}-tls
  namespace: ${KUADRANT_GATEWAY_NS}
spec:
  targetRef:
    name: ${KUADRANT_GATEWAY_NAME}
    group: gateway.networking.k8s.io
    kind: Gateway
  issuerRef:
    group: cert-manager.io
    kind: ClusterIssuer
    name: ${KUADRANT_CLUSTER_ISSUER_NAME}
EOF

Asegurarse de que el estado de la TLSPolicy sea Enforced.

kubectl get tlspolicy ${KUADRANT_GATEWAY_NAME}-tls -n ${KUADRANT_GATEWAY_NS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}{"\n"}{.status.conditions[?(@.type=="Enforced")].message}'

Obtener el hostname del Gateway, ejecute el siguiente comando en la terminal.

export INGRESS_HOST=$(kubectl get gtw ${KUADRANT_GATEWAY_NAME} -n ${KUADRANT_GATEWAY_NS} -o jsonpath='{.status.addresses[0].value}')

Verificar la dirección del Gateway

echo $INGRESS_HOST

Nota: Si no se visualiza un valor o si se presenta un mensaje de estado de error que indica que está "esperando la asignación de dirección", se debe verificar si el clúster cuenta con un balanceador de carga predeterminado que pueda asignar una dirección al Gateway.

Realizar una llamada al Gateway. Se debería poder acceder a la API, la cual se encuentra protegida por la TLSPolicy. Es importante notar el uso de https en la URL, ya que esto demuestra que la TLSPolicy se ha aplicado correctamente. Agregaremos el dominio nip.io a nuestras llamadas que corresponde a la configuración del Gateway.

curl -v -k https://$INGRESS_HOST.nip.io/books

Auth policy: Crear un Deny all auth policy a nivel de gateway.

La creación de una política de autenticación de denegación total (deny-all) a nivel del gateway establece una política de autenticación de confianza cero (zero-trust). Con Kuadrant, se pueden añadir políticas tanto a nivel del Gateway como a nivel del HTTPRoute (servicio).

kubectl apply -f - <<EOF
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
 name: ${KUADRANT_GATEWAY_NAME}-deny-all
 namespace: ${KUADRANT_GATEWAY_NS}
spec:
 rules:
   authorization:
     deny-all:
       metrics: false
       opa:
         allValues: false
         rego: allow = false
       priority: 0
   response:
     unauthorized:
       body:
         value: |
           {
             "error": "Forbidden",
             "message": "Access denied by default by the gateway operator. If you are the administrator of the service, create a specific auth policy for the route."
           }
       headers:
         content-type:
           value: application/json
 targetRef:
   group: gateway.networking.k8s.io
   kind: Gateway
   name: ${KUADRANT_GATEWAY_NAME}
EOF

Asegurarse de que el estado de la AuthPolicy sea Enforced.

kubectl get authpolicy ${KUADRANT_GATEWAY_NAME}-deny-all -n ${KUADRANT_GATEWAY_NS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}{"\n"}{.status.conditions[?(@.type=="Enforced")].message}'

Al intentar acceder a la API nuevamente, se debería obtener un error 403 y un mensaje de error que indica lo siguiente:

"error": "Forbidden","message":"Access denied by default by the gateway operator. If you are the administrator of the service, create a specific auth policy for the route."

Esto demuestra que el gateway es seguro por defecto y aplica una política de confianza cero (zero trust).

curl -k -w "%{http_code}" https://$INGRESS_HOST.nip.io/books

Crear una política de autenticación basada en APIkey para la API.

Si un equipo de desarrollo desea proporcionar un acceso basado en claves (API key) a su API, se puede crear una política de autenticación específica para dicha API basada en llaves.

Crear algunas claves API de muestra como secretos.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
 name: api-key-regular-user
 namespace: kuadrant-system
 labels:
   authorino.kuadrant.io/managed-by: authorino
stringData:
 api_key: iamaregularuser
type: Opaque
---
apiVersion: v1
kind: Secret
metadata:
 name: api-key-admin-user
 namespace: kuadrant-system
 labels:
   authorino.kuadrant.io/managed-by: authorino
 annotations:
   kuadrant.io/groups: admins
stringData:
 api_key: iamanadmin
type: Opaque
EOF

Nota: Este es solo un ejemplo sencillo con claves basadas en cadenas. También puede utilizar un proveedor OIDC para autenticar a los usuarios.

Crear una nueva politica para el acceso con api key.

kubectl apply -f - <<EOF
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
 name: ${KUADRANT_DEVELOPER_NS}-allow-key
 namespace: ${KUADRANT_DEVELOPER_NS}
spec:
 targetRef:
   group: gateway.networking.k8s.io
   kind: HTTPRoute
   name: ${KUADRANT_DEVELOPER_NS}
 rules:
   authentication:
     "api-key-authn":
       apiKey:
         selector: {}
       credentials:
         queryString:
           name: apikey
EOF

Asegurarse de que el estado de la AuthPolicy sea Enforced.

kubectl get authpolicy ${KUADRANT_DEVELOPER_NS}-allow-key -n ${KUADRANT_DEVELOPER_NS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}{"\n"}{.status.conditions[?(@.type=="Enforced")].message}'

Ahora acceda a la API sin la clave. Debería ver un error 401 y deberá denegar el acceso.

curl -k -w "%{http_code}\n" https://$INGRESS_HOST.nip.io/books

Ahora accede con la clave API y debería devolverte una lista de libros junto con el código HTTP 200.

curl -k -w "\n%{http_code}\n" "https://$INGRESS_HOST.nip.io/books?apikey=iamaregularuser"

Crear una política de control de tráfico(rate limit policy) a nivel de Gateway

La política de límite de frecuencia se aplica a los recursos de red de la API de Gateway, como HTTPRoutes y Gateways, utilizando estos recursos para obtener contexto adicional, es decir, qué carga de tráfico (atributos HTTP, nombres de host, atributos de usuario, etc.) se debe limitar.

kubectl apply -f - <<EOF
apiVersion: kuadrant.io/v1
kind: RateLimitPolicy
metadata:
 name: ${KUADRANT_GATEWAY_NAME}-rlp-lowlimits
 namespace: ${KUADRANT_GATEWAY_NS}
spec:
 limits:
   default-limits:
     rates:
       - limit: 3
         window: 10s
 targetRef:
   group: gateway.networking.k8s.io
   kind: Gateway
   name: ${KUADRANT_GATEWAY_NAME}
EOF

Asegurarse de que el estado de la AuthPolicy sea Enforced.

kubectl get ratelimitpolicy ${KUADRANT_GATEWAY_NAME}-rlp-lowlimits -n ${KUADRANT_GATEWAY_NS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}{"\n"}{.status.conditions[?(@.type=="Enforced")].message}'

Ahora acceda a la API más de 3 veces en 10 segundos para infringir los límites de frecuencia y debería ver un 429 con un mensaje de Demasiadas solicitudes después de las primeras 3 llamadas.

for i in $(seq 1 10);
do
    curl -k -w "\n%{http_code}\n" "https://$INGRESS_HOST.nip.io/books?apikey=iamaregularuser"
done

Conclusión

La gestión de APIs en Kubernetes ya no tiene que ser un ejercicio complejo de configuración de proxies y lógica dispersa. Kuadrant.io ofrece una visión unificada y declarativa para proteger y monetizar los servicios en la nube, alineando la seguridad y la gestión del tráfico con los principios nativos de Kubernetes.

El futuro de la gestión de APIs en Kubernetes es nativo y declarativo.

Puedes explorar ladocumentación oficial de Kuadrant.io para profundizar en sus capacidades y entender cómo puede transformar la gestión de APIs en entornos de Kubernetes.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *