Exposing Services in Kubernetes

Apoya a HackTricks

Existen diferentes formas de exponer servicios en Kubernetes para que tanto los endpoints internos como los externos puedan acceder a ellos. Esta configuración en Kubernetes es bastante crítica, ya que el administrador podría dar acceso a atacantes a servicios a los que no deberían poder acceder.

Enumeración Automática

Antes de comenzar a enumerar las formas en que K8s ofrece para exponer servicios al público, ten en cuenta que si puedes listar los namespaces, servicios e ingresses, puedes encontrar todo lo expuesto al público con:

kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do
echo "Namespace: $ns"
kubectl get service -n "$ns"
kubectl get ingress -n "$ns"
echo "=============================================="
echo ""
echo ""
done | grep -v "ClusterIP"
# Remove the last '| grep -v "ClusterIP"' to see also type ClusterIP

ClusterIP

Un servicio ClusterIP es el servicio por defecto de Kubernetes. Te proporciona un servicio dentro de tu clúster al que otras aplicaciones dentro de tu clúster pueden acceder. No hay acceso externo.

Sin embargo, se puede acceder a esto utilizando el Proxy de Kubernetes:

kubectl proxy --port=8080

Ahora, puedes navegar a través de la API de Kubernetes para acceder a los servicios utilizando este esquema:

http://localhost:8080/api/v1/proxy/namespaces/<NOMBRE-ESPACIO>/services/<NOMBRE-SERVICIO>:<NOMBRE-PUERTO>/

Por ejemplo, podrías usar la siguiente URL:

http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/

para acceder a este servicio:

apiVersion: v1
kind: Service
metadata:
name: my-internal-service
spec:
selector:
app: my-app
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP

Este método requiere que ejecutes kubectl como un usuario autenticado.

Listar todos los ClusterIPs:

kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep ClusterIP

NodePort

Cuando se utiliza NodePort, se pone a disposición un puerto designado en todos los Nodos (que representan las Máquinas Virtuales). El tráfico dirigido a este puerto específico se enruta sistemáticamente hacia el servicio. Normalmente, este método no se recomienda debido a sus inconvenientes.

Listar todos los NodePorts:

kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep NodePort

Un ejemplo de especificación NodePort:

apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
selector:
app: my-app
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30036
protocol: TCP

Si no especificas el nodePort en el archivo yaml (es el puerto que se abrirá) se usará un puerto en el rango 30000–32767.

LoadBalancer

Expone el Servicio externamente utilizando el balanceador de carga del proveedor de la nube. En GKE, esto iniciará un Network Load Balancer que te proporcionará una única dirección IP que reenviará todo el tráfico a tu servicio. En AWS se lanzará un Balanceador de Carga.

Debes pagar por un Balanceador de Carga por cada servicio expuesto, lo cual puede resultar costoso.

Listar todos los Balanceadores de Carga:

kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,EXTERNAL-IP:.status.loadBalancer.ingress[*],PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep LoadBalancer

IPs Externas

Las IPs externas son expuestas por servicios de tipo Load Balancers y generalmente se utilizan cuando se está utilizando un Load Balancer externo del proveedor de la nube.

Para encontrarlas, verifica los balanceadores de carga con valores en el campo EXTERNAL-IP.

El tráfico que ingresa al clúster con la IP externa (como IP de destino), en el puerto del Servicio, será ruteado a uno de los endpoints del Servicio. Las externalIPs no son gestionadas por Kubernetes y son responsabilidad del administrador del clúster.

En la especificación del Servicio, las externalIPs pueden ser especificadas junto con cualquiera de los ServiceTypes. En el ejemplo a continuación, "mi-servicio" puede ser accedido por clientes en "80.11.12.10:80" (externalIP:puerto)

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10

ExternalName

Desde la documentación: Los Servicios de tipo ExternalName mapean un Servicio a un nombre DNS, no a un selector típico como my-service o cassandra. Especificas estos Servicios con el parámetro spec.externalName.

Esta definición de Servicio, por ejemplo, mapea el Servicio my-service en el espacio de nombres prod a my.database.example.com:

apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com

Cuando se busca el host my-service.prod.svc.cluster.local, el Servicio de DNS del clúster devuelve un registro CNAME con el valor my.database.example.com. Acceder a my-service funciona de la misma manera que otros Servicios pero con la diferencia crucial de que la redirección ocurre a nivel de DNS en lugar de a través de un proxy o reenvío.

Lista todos los ExternalNames:

kubectl get services --all-namespaces | grep ExternalName

Ingress

A diferencia de todos los ejemplos anteriores, Ingress NO es un tipo de servicio. En cambio, se sitúa frente a múltiples servicios y actúa como un "enrutador inteligente" o punto de entrada a tu clúster.

Puedes hacer muchas cosas diferentes con un Ingress, y hay muchos tipos de controladores de Ingress que tienen diferentes capacidades.

El controlador de Ingress predeterminado de GKE creará un Balanceador de carga HTTP(S) para ti. Esto te permitirá realizar enrutamiento basado en ruta y subdominio hacia los servicios backend. Por ejemplo, puedes enviar todo en foo.tudominio.com al servicio foo, y todo bajo la ruta tu dominio.com/bar/ al servicio bar.

El YAML para un objeto Ingress en GKE con un Balanceador de carga HTTP L7 podría verse así:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: other
servicePort: 8080
rules:
- host: foo.mydomain.com
http:
paths:
- backend:
serviceName: foo
servicePort: 8080
- host: mydomain.com
http:
paths:
- path: /bar/*
backend:
serviceName: bar
servicePort: 8080

Enumera todos los ingresos:

kubectl get ingresses --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,RULES:spec.rules[*],STATUS:status'

Aunque en este caso es mejor obtener la información de uno por uno para leerla mejor:

kubectl get ingresses --all-namespaces -o=yaml

Referencias

Apoya a HackTricks

Last updated