Exposing Services in Kubernetes

Soutenez HackTricks

Il existe différentes façons d'exposer des services dans Kubernetes afin que les endpoints internes et externes puissent y accéder. Cette configuration Kubernetes est assez critique car l'administrateur pourrait donner accès à des attaquants à des services auxquels ils ne devraient pas pouvoir accéder.

Énumération Automatique

Avant de commencer à énumérer les façons dont K8s offre pour exposer des services au public, sachez que si vous pouvez lister les espaces de noms, les services et les ingresses, vous pouvez trouver tout ce qui est exposé au public avec :

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 service ClusterIP est le service Kubernetes par défaut. Il vous donne un service à l'intérieur de votre cluster auquel d'autres applications à l'intérieur de votre cluster peuvent accéder. Il n'y a pas d'accès externe.

Cependant, cela peut être accédé en utilisant le Proxy Kubernetes :

kubectl proxy --port=8080

Maintenant, vous pouvez naviguer à travers l'API Kubernetes pour accéder aux services en utilisant ce schéma :

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

Par exemple, vous pourriez utiliser l'URL suivante :

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

pour accéder à ce service :

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

Cette méthode nécessite que vous exécutiez kubectl en tant qu'utilisateur authentifié.

Listez tous les 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

Lorsque NodePort est utilisé, un port désigné est rendu disponible sur tous les nœuds (représentant les machines virtuelles). Le trafic dirigé vers ce port spécifique est ensuite systématiquement routé vers le service. En général, cette méthode n'est pas recommandée en raison de ses inconvénients.

Listez tous les 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 exemple de spécification 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

Si vous ne spécifiez pas le nodePort dans le fichier yaml (c'est le port qui sera ouvert) un port dans la plage 30000–32767 sera utilisé.

LoadBalancer

Expose le Service de manière externe en utilisant l'équilibreur de charge du fournisseur de cloud. Sur GKE, cela va mettre en place un Network Load Balancer qui vous donnera une seule adresse IP qui redirigera tout le trafic vers votre service. Sur AWS, cela lancera un équilibreur de charge.

Vous devez payer pour un LoadBalancer par service exposé, ce qui peut être coûteux.

Listez tous les 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

Adresses IP externes

Les adresses IP externes sont exposées par les services de type Load Balancers et sont généralement utilisées lorsqu'un Load Balancer externe du fournisseur de cloud est utilisé.

Pour les trouver, vérifiez les Load Balancers avec des valeurs dans le champ EXTERNAL-IP.

Le trafic qui entre dans le cluster avec l'adresse IP externe (comme adresse IP de destination), sur le port du Service, sera routé vers l'un des points de terminaison du Service. Les externalIPs ne sont pas gérées par Kubernetes et relèvent de la responsabilité de l'administrateur du cluster.

Dans la spécification du Service, les externalIPs peuvent être spécifiées avec n'importe quel ServiceTypes. Dans l'exemple ci-dessous, "mon-service" peut être accédé par les clients sur "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

Depuis la documentation : Les Services de type ExternalName font correspondre un Service à un nom DNS, et non à un sélecteur typique tel que mon-service ou cassandra. Vous spécifiez ces Services avec le paramètre spec.externalName.

Cette définition de Service, par exemple, fait correspondre le Service mon-service dans l'espace de noms prod à ma.base.de.données.exemple.com :

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

Lorsque vous recherchez l'hôte my-service.prod.svc.cluster.local, le Service DNS du cluster renvoie un enregistrement CNAME avec la valeur my.database.example.com. Accéder à my-service fonctionne de la même manière que pour les autres Services mais avec la différence cruciale que la redirection se produit au niveau du DNS plutôt que par l'intermédiaire d'un proxy ou d'une redirection.

Listez tous les ExternalNames:

kubectl get services --all-namespaces | grep ExternalName

Ingress

Contrairement à tous les exemples ci-dessus, Ingress N'EST PAS un type de service. Au lieu de cela, il se trouve devant plusieurs services et agit comme un "routeur intelligent" ou un point d'entrée dans votre cluster.

Vous pouvez faire beaucoup de choses différentes avec un Ingress, et il existe de nombreux types de contrôleurs Ingress qui ont des capacités différentes.

Le contrôleur Ingress par défaut de GKE va mettre en place un répartiteur de charge HTTP(S) pour vous. Cela vous permettra de faire du routage basé sur le chemin et sur les sous-domaines vers les services backend. Par exemple, vous pouvez envoyer tout ce qui se trouve sur foo.votredomaine.com vers le service foo, et tout ce qui est sous le chemin votredomaine.com/bar/ vers le service bar.

Le YAML pour un objet Ingress sur GKE avec un répartiteur de charge HTTP L7 pourrait ressembler à ceci:

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

Listez tous les ingress :

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

Bien que dans ce cas, il soit préférable d'obtenir les informations de chacun un par un pour les lire plus facilement :

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

Références

Soutenez HackTricks

Last updated