# Esposizione dei Servizi in Kubernetes {{#include ../../banners/hacktricks-training.md}} Ci sono **diversi modi per esporre i 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 i servizi al pubblico, sappi che se puoi elencare namespace, servizi e ingressi, puoi trovare tutto ciò che è esposto al pubblico con: ```bash 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 Kubernetes Proxy: ```bash 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//services/:/` 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: ```yaml 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: ```bash 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** viene utilizzato, 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: ```bash 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: ```yaml 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 provider cloud**. Su GKE, questo avvierà un [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) 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: ```bash 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 ``` ### External IPs > [!TIP] > Gli IP esterni sono esposti dai servizi di tipo Load Balancers 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`) ```yaml 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:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) 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`: ```yaml 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: ```bash 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](https://cloud.google.com/compute/docs/load-balancing/http/) 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](https://cloud.google.com/compute/docs/load-balancing/http/) potrebbe apparire così: ```yaml 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: ```bash 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: ```bash kubectl get ingresses --all-namespaces -o=yaml ``` ### Riferimenti - [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0) - [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/) {{#include ../../banners/hacktricks-training.md}}