Exposing Services in Kubernetes

Supporta HackTricks

Ci sono diversi modi per esporre servizi in Kubernetes in modo che sia gli endpoint interni che gli endpoint esterni possano accedervi. Questa configurazione di Kubernetes è piuttosto critica poiché l'amministratore potrebbe dare accesso a attaccanti a servizi a cui non dovrebbero poter accedere.

Enumerazione Automatica

Prima di iniziare a enumerare i modi in cui K8s offre di esporre servizi al pubblico, sappi che se puoi elencare namespace, servizi e ingressi, puoi trovare tutto ciò che è esposto al pubblico 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 servizio ClusterIP è il servizio predefinito di Kubernetes. Ti offre un servizio interno al tuo cluster a cui altre app all'interno del tuo cluster possono accedere. Non c'è accesso esterno.

Tuttavia, questo può essere accessibile utilizzando il Proxy di Kubernetes:

kubectl proxy --port=8080

Ora puoi navigare attraverso l'API di Kubernetes per accedere ai servizi utilizzando questo schema:

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

Ad esempio, potresti utilizzare il seguente URL:

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

per accedere a questo servizio:

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

Questo metodo richiede di eseguire kubectl come utente autenticato.

Elenca tutti i ClusterIP:

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 viene utilizzato NodePort, una porta designata è resa disponibile su tutti i nodi (che rappresentano le macchine virtuali). Il traffico diretto a questa porta specifica viene quindi sistematicamente instradato al servizio. Tipicamente, questo metodo non è raccomandato a causa dei suoi svantaggi.

Elenca tutti i NodePort:

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 esempio di specifica 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 non specifichi il nodePort nel yaml (è la porta che sarà aperta) verrà utilizzata una porta nel range 30000–32767.

LoadBalancer

Espone il Servizio esternamente utilizzando il bilanciatore di carico di un fornitore di cloud. Su GKE, questo avvierà un Network Load Balancer che ti fornirà un singolo indirizzo IP che inoltrerà tutto il traffico al tuo servizio. In AWS lancerà un Load Balancer.

Devi pagare per un LoadBalancer per ogni servizio esposto, il che può essere costoso.

Elenca tutti i 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

IP esterni

Gli IP esterni sono esposti dai servizi di tipo Load Balancer e sono generalmente utilizzati quando si utilizza un Load Balancer di un Cloud Provider esterno.

Per trovarli, controlla i load balancer con valori nel campo EXTERNAL-IP.

Il traffico che entra nel cluster con l'IP esterno (come IP di destinazione), sulla porta del Servizio, sarà instradato a uno degli endpoint del Servizio. externalIPs non sono gestiti da Kubernetes e sono responsabilità dell'amministratore del cluster.

Nella specifica del Servizio, externalIPs possono essere specificati insieme a qualsiasi tipo di ServiceTypes. Nell'esempio seguente, "my-service" può essere accessibile dai client su "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

Dalla documentazione: I servizi di tipo ExternalName mappano un servizio a un nome DNS, non a un selettore tipico come my-service o cassandra. Si specificano questi servizi con il parametro spec.externalName.

Questa definizione di servizio, ad esempio, mappa il servizio my-service nel namespace prod a my.database.example.com:

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

Quando si cerca l'host my-service.prod.svc.cluster.local, il servizio DNS del cluster restituisce un record CNAME con il valore my.database.example.com. Accedere a my-service funziona allo stesso modo degli altri servizi, ma con la differenza cruciale che la reindirizzazione avviene a livello DNS piuttosto che tramite proxy o inoltro.

Elenca tutti gli ExternalNames:

kubectl get services --all-namespaces | grep ExternalName

Ingress

A differenza di tutti gli esempi sopra, Ingress NON è un tipo di servizio. Invece, si trova davanti a più servizi e funge da “router intelligente” o punto di ingresso nel tuo cluster.

Puoi fare molte cose diverse con un Ingress, e ci sono molti tipi di controller Ingress che hanno capacità diverse.

Il controller ingress predefinito di GKE avvierà un HTTP(S) Load Balancer per te. Questo ti permetterà di fare sia il routing basato su percorso che su sottodominio ai servizi di backend. Ad esempio, puoi inviare tutto su foo.yourdomain.com al servizio foo, e tutto sotto il percorso yourdomain.com/bar/ al servizio bar.

Il YAML per un oggetto Ingress su GKE con un L7 HTTP Load Balancer potrebbe apparire così:

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

Elenca tutti gli ingressi:

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

Sebbene in questo caso sia meglio ottenere le informazioni di ciascuno uno per uno per leggerle meglio:

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

Riferimenti

Supporta HackTricks

Last updated