Exposing Services in Kubernetes

Sostieni HackTricks

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

Enumerazione Automatica

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

Tuttavia, questo può essere accessibile utilizzando il Proxy 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 un 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 viene resa disponibile su tutti i Nodi (che rappresentano le macchine virtuali). Il traffico indirizzato a questa porta specifica viene quindi instradato sistematicamente al servizio. Tipicamente, questo metodo non è raccomandato a causa dei suoi svantaggi.

Elenca tutti i 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 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 si specifica il nodePort nel file yaml (è la porta che verrà aperta) verrà utilizzata una porta nell'intervallo 30000–32767.

LoadBalancer

Espone il Servizio esternamente utilizzando un bilanciamento del carico del fornitore di servizi cloud. Su GKE, questo creerà un Network Load Balancer che ti fornirà un singolo indirizzo IP che inoltrerà tutto il traffico al tuo servizio. Su AWS verrà avviato un Load Balancer.

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

Elenca tutti i LoadBalancer:

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

Indirizzi IP esterni

Gli indirizzi IP esterni sono esposti dai servizi di tipo Load Balancer e sono generalmente utilizzati quando viene utilizzato un Load Balancer del Cloud Provider esterno.

Per trovarli, controlla i bilanciatori di carico con valori nel campo EXTERNAL-IP.

Il traffico che entra nel cluster con l'indirizzo IP esterno (come indirizzo IP di destinazione), sulla porta del Servizio, verrà instradato verso uno dei punti finali del Servizio. Gli externalIPs non sono gestiti da Kubernetes e sono responsabilità dell'amministratore del cluster.

Nella specifica del Servizio, gli externalIPs possono essere specificati insieme a uno qualsiasi dei 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 su un nome DNS, non su un selettore tipico come my-service o cassandra. Specificare questi Servizi con il parametro spec.externalName.

Questa definizione di Servizio, ad esempio, mappa il Servizio my-service nello spazio dei nomi prod su 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. L'accesso a my-service funziona allo stesso modo degli altri Servizi ma con la differenza cruciale che il reindirizzamento avviene a livello DNS anziché tramite proxy o inoltro.

Elencare 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 agisce come un "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 routing basato su percorsi e subdomini verso i servizi di backend. Ad esempio, puoi inviare tutto su foo.tuodominio.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'

Anche in questo caso è meglio ottenere le informazioni uno per uno per leggerle meglio:

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

Riferimenti

Sostieni HackTricks

Last updated