# 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/<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:
```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 <a href="#id-0d96" id="id-0d96"></a>

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 <a href="#external-ips" id="external-ips"></a>

> [!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}}
