Exposing Services in Kubernetes

Support HackTricks

Є різні способи експонування сервісів у Kubernetes, щоб як внутрішні, так і зовнішні кінцеві точки могли до них отримати доступ. Ця конфігурація Kubernetes є досить критичною, оскільки адміністратор може надати доступ зловмисникам до сервісів, до яких їм не слід мати доступ.

Automatic Enumeration

Перед тим, як почати перераховувати способи, які K8s пропонує для експонування сервісів публічно, знайте, що якщо ви можете перерахувати простори імен, сервіси та вхідні точки, ви можете знайти все, що експоновано публічно за допомогою:

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

Служба ClusterIP є за замовчуванням службою Kubernetes. Вона надає вам службу всередині вашого кластера, до якої можуть отримати доступ інші додатки всередині вашого кластера. Зовнішнього доступу немає.

Однак, до неї можна отримати доступ за допомогою Kubernetes Proxy:

kubectl proxy --port=8080

Тепер ви можете переходити через API Kubernetes для доступу до сервісів, використовуючи цю схему:

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

Наприклад, ви можете використовувати наступну URL-адресу:

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

щоб отримати доступ до цього сервісу:

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

Цей метод вимагає, щоб ви запускали kubectl як авторизований користувач.

Перелічіть всі 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

Коли використовується NodePort, призначений порт стає доступним на всіх вузлах (які представляють віртуальні машини). Трафік, спрямований на цей конкретний порт, систематично перенаправляється на сервіс. Зазвичай цей метод не рекомендується через його недоліки.

Список усіх NodePort:

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

Приклад специфікації 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

Якщо ви не вкажете nodePort у yaml (це порт, який буде відкритий), буде використано порт у діапазоні 30000–32767.

LoadBalancer

Відкриває Сервіс зовні за допомогою балансувальника навантаження постачальника хмари. У GKE це запустить Network Load Balancer, який надасть вам одну IP-адресу, що буде пересилати весь трафік до вашого сервісу. У AWS це запустить Load Balancer.

Вам потрібно платити за LoadBalancer за кожен відкритий сервіс, що може бути дорогим.

Список усіх 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

Зовнішні IP

Зовнішні IP відкриваються сервісами типу Load Balancers і зазвичай використовуються, коли використовується зовнішній балансувальник навантаження від постачальника хмари.

Щоб їх знайти, перевірте балансувальники навантаження з значеннями у полі EXTERNAL-IP.

Трафік, що входить у кластер з зовнішнім IP (як цільовий IP), на порту сервісу, буде направлений на один з кінцевих точок сервісу. externalIPs не керуються Kubernetes і є відповідальністю адміністратора кластера.

У специфікації сервісу externalIPs можуть бути вказані разом з будь-яким з ServiceTypes. У наведеному нижче прикладі, "my-service" може бути доступний клієнтам на "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

З документації: Сервіси типу ExternalName відображають Сервіс на DNS-ім'я, а не на типовий селектор, такий як my-service або cassandra. Ви вказуєте ці Сервіси за допомогою параметра spec.externalName.

Це визначення Сервісу, наприклад, відображає Сервіс my-service в просторі імен prod на my.database.example.com:

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

Коли ви шукаєте хост my-service.prod.svc.cluster.local, служба DNS кластера повертає запис CNAME зі значенням my.database.example.com. Доступ до my-service працює так само, як і з іншими службами, але з важливою різницею, що перенаправлення відбувається на рівні DNS, а не через проксування або пересилання.

Список усіх ExternalNames:

kubectl get services --all-namespaces | grep ExternalName

Ingress

На відміну від усіх вищезазначених прикладів, Ingress НЕ є типом служби. Натомість, він знаходиться попереду кількох служб і діє як “розумний маршрутизатор” або точка входу у ваш кластер.

Ви можете робити багато різних речей з Ingress, і існує багато типів контролерів Ingress, які мають різні можливості.

Контролер Ingress за замовчуванням GKE створить для вас HTTP(S) Load Balancer. Це дозволить вам здійснювати маршрутизацію як на основі шляху, так і на основі піддомену до бекенд-служб. Наприклад, ви можете відправити все на foo.yourdomain.com до служби foo, а все під шляхом yourdomain.com/bar/ до служби bar.

YAML для об'єкта Ingress на GKE з L7 HTTP Load Balancer може виглядати так:

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

Список всіх вхідних точок:

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

Хоча в цьому випадку краще отримувати інформацію про кожен окремо, щоб легше її читати:

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

References

Підтримати HackTricks

Last updated