Exposing Services in Kubernetes

Support HackTricks

Istnieją różne sposoby na udostępnienie usług w Kubernetes, aby zarówno wewnętrzne, jak i zewnętrzne punkty końcowe mogły uzyskać do nich dostęp. Ta konfiguracja Kubernetes jest dość krytyczna, ponieważ administrator może dać dostęp atakującym do usług, do których nie powinni mieć dostępu.

Automatic Enumeration

Zanim zaczniesz enumerować sposoby, w jakie K8s oferuje udostępnienie usług publicznie, wiedz, że jeśli możesz wymienić przestrzenie nazw, usługi i ingressy, możesz znaleźć wszystko, co jest udostępnione publicznie za pomocą:

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

Usługa ClusterIP jest domyślną usługą Kubernetes. Daje ci usługę wewnątrz twojego klastra, do której mogą uzyskać dostęp inne aplikacje w twoim klastrze. Nie ma dostępu zewnętrznego.

Jednakże, można uzyskać do niej dostęp za pomocą Proxy Kubernetes:

kubectl proxy --port=8080

Teraz możesz nawigować przez API Kubernetes, aby uzyskać dostęp do usług, używając tego schematu:

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

Na przykład możesz użyć następującego adresu URL:

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

aby uzyskać dostęp do tej usługi:

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

Ta metoda wymaga, abyś uruchomił kubectl jako uwierzytelniony użytkownik.

Wyświetl wszystkie 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

Gdy NodePort jest wykorzystywany, wyznaczony port jest udostępniany na wszystkich Węzłach (reprezentujących Wirtualne Maszyny). Ruch kierowany do tego konkretnego portu jest następnie systematycznie przekierowywany do usługi. Zazwyczaj ta metoda nie jest zalecana z powodu jej wad.

Lista wszystkich NodePortów:

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

Przykład specyfikacji 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

Jeśli nie określisz nodePort w yaml (to jest port, który zostanie otwarty), zostanie użyty port w zakresie 30000–32767.

LoadBalancer

Ekspozycja usługi na zewnątrz za pomocą load balancera dostawcy chmury. Na GKE uruchomi to Network Load Balancer, który da ci jeden adres IP, który przekieruje cały ruch do twojej usługi. W AWS uruchomi Load Balancer.

Musisz płacić za LoadBalancer za każdą eksponowaną usługę, co może być kosztowne.

Lista wszystkich 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

Zewnętrzne IP

Zewnętrzne IP są eksponowane przez usługi typu Load Balancers i są zazwyczaj używane, gdy korzysta się z zewnętrznego Load Balancera dostawcy chmury.

Aby je znaleźć, sprawdź load balancery z wartościami w polu EXTERNAL-IP.

Ruch, który wchodzi do klastra z zewnętrznym IP (jako IP docelowe), na porcie usługi, będzie przekierowywany do jednego z punktów końcowych usługi. externalIPs nie są zarządzane przez Kubernetes i są odpowiedzialnością administratora klastra.

W specyfikacji usługi externalIPs mogą być określone wraz z dowolnym z ServiceTypes. W poniższym przykładzie, "my-service" może być dostępne dla klientów na "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

Z dokumentacji: Usługi typu ExternalName mapują usługę do nazwy DNS, a nie do typowego selektora, takiego jak my-service lub cassandra. Te usługi określasz za pomocą parametru spec.externalName.

Ta definicja usługi, na przykład, mapuje usługę my-service w przestrzeni nazw prod do my.database.example.com:

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

Kiedy wyszukujesz hosta my-service.prod.svc.cluster.local, usługa DNS klastra zwraca rekord CNAME o wartości my.database.example.com. Uzyskiwanie dostępu do my-service działa w ten sam sposób, co inne usługi, ale z kluczową różnicą, że przekierowanie odbywa się na poziomie DNS zamiast przez proxy lub przekazywanie.

Wymień wszystkie ExternalNames:

kubectl get services --all-namespaces | grep ExternalName

Ingress

W przeciwieństwie do wszystkich powyższych przykładów, Ingress NIE jest typem usługi. Zamiast tego, znajduje się przed wieloma usługami i działa jako „inteligentny router” lub punkt wejścia do twojego klastra.

Możesz zrobić wiele różnych rzeczy z Ingress, a istnieje wiele typów kontrolerów Ingress, które mają różne możliwości.

Domyślny kontroler ingress GKE uruchomi dla Ciebie HTTP(S) Load Balancer. To pozwoli Ci na routowanie oparte na ścieżkach oraz subdomenach do usług zaplecza. Na przykład, możesz wysłać wszystko na foo.yourdomain.com do usługi foo, a wszystko pod ścieżką yourdomain.com/bar/ do usługi bar.

YAML dla obiektu Ingress na GKE z L7 HTTP Load Balancer może wyglądać tak:

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

Wypisz wszystkie ingressy:

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

Chociaż w tym przypadku lepiej jest uzyskać informacje o każdym z osobna, aby lepiej je przeczytać:

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

References

Wsparcie HackTricks

Last updated