Exposing Services in Kubernetes

Support HackTricks

Existem diferentes maneiras de expor serviços no Kubernetes para que tanto endpoints internos quanto endpoints externos possam acessá-los. Esta configuração do Kubernetes é bastante crítica, pois o administrador pode dar acesso a atacantes a serviços que eles não deveriam conseguir acessar.

Enumeração Automática

Antes de começar a enumerar as maneiras que o K8s oferece para expor serviços ao público, saiba que se você pode listar namespaces, serviços e ingresses, pode encontrar tudo exposto ao público com:

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

Um serviço ClusterIP é o padrão do Kubernetes. Ele fornece um serviço interno no seu cluster que outros aplicativos dentro do seu cluster podem acessar. Não há acesso externo.

No entanto, isso pode ser acessado usando o Proxy do Kubernetes:

kubectl proxy --port=8080

Agora, você pode navegar pela API do Kubernetes para acessar serviços usando este esquema:

http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/

Por exemplo, você poderia usar a seguinte URL:

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

para acessar este serviço:

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 requer que você execute kubectl como um usuário autenticado.

Liste todos os 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

Quando NodePort é utilizado, uma porta designada é disponibilizada em todos os Nós (representando as Máquinas Virtuais). O tráfego direcionado a esta porta específica é então sistematicamente routed to the service. Normalmente, este método não é recomendado devido às suas desvantagens.

Liste todos os 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

Um exemplo de especificação 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

Se você não especificar o nodePort no yaml (é a porta que será aberta), uma porta na faixa de 30000–32767 será usada.

LoadBalancer

Expõe o Serviço externamente usando o balanceador de carga de um provedor de nuvem. No GKE, isso irá iniciar um Network Load Balancer que lhe dará um único endereço IP que encaminhará todo o tráfego para seu serviço. No AWS, ele lançará um Load Balancer.

Você tem que pagar por um LoadBalancer por serviço exposto, o que pode ser caro.

Liste todos os LoadBalancers:

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 Externos

IPs externos são expostos por serviços do tipo Load Balancers e geralmente são usados quando um Load Balancer de Provedor de Nuvem externo está sendo utilizado.

Para encontrá-los, verifique os load balancers com valores no campo EXTERNAL-IP.

O tráfego que ingressa no cluster com o IP externo (como IP de destino), na porta do Serviço, será routado para um dos endpoints do Serviço. externalIPs não são gerenciados pelo Kubernetes e são de responsabilidade do administrador do cluster.

Na especificação do Serviço, externalIPs podem ser especificados junto com qualquer um dos ServiceTypes. No exemplo abaixo, "my-service" pode ser acessado por clientes em "80.11.12.10:80" (externalIP:port)

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

Da documentação: Serviços do tipo ExternalName mapeiam um Serviço para um nome DNS, não para um seletor típico como my-service ou cassandra. Você especifica esses Serviços com o parâmetro spec.externalName.

Esta definição de Serviço, por exemplo, mapeia o Serviço my-service no namespace prod para my.database.example.com:

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

Ao procurar o host my-service.prod.svc.cluster.local, o Serviço DNS do cluster retorna um registro CNAME com o valor my.database.example.com. Acessar my-service funciona da mesma forma que outros Serviços, mas com a diferença crucial de que a redireção acontece no nível DNS em vez de via proxy ou encaminhamento.

Liste todos os ExternalNames:

kubectl get services --all-namespaces | grep ExternalName

Ingress

Ao contrário de todos os exemplos acima, Ingress NÃO é um tipo de serviço. Em vez disso, ele fica na frente de vários serviços e atua como um “roteador inteligente” ou ponto de entrada para o seu cluster.

Você pode fazer muitas coisas diferentes com um Ingress, e existem muitos tipos de controladores de Ingress que têm diferentes capacidades.

O controlador de ingress padrão do GKE irá criar um HTTP(S) Load Balancer para você. Isso permitirá que você faça roteamento baseado em caminho e subdomínio para serviços de backend. Por exemplo, você pode enviar tudo em foo.seudominio.com para o serviço foo, e tudo sob o caminho seudominio.com/bar/ para o serviço bar.

O YAML para um objeto Ingress no GKE com um L7 HTTP Load Balancer pode parecer assim:

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

Liste todos os ingressos:

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

Embora neste caso seja melhor obter as informações de cada um individualmente para lê-las melhor:

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

Referências

Suporte ao HackTricks

Last updated