Exposing Services in Kubernetes

支持HackTricks

在Kubernetes中有不同的方法来暴露服务,以便内部端点和外部端点都可以访问它们。这个Kubernetes配置非常关键,因为管理员可能会给攻击者访问他们不应该能够访问的服务的权限。

自动枚举

在开始枚举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

现在,您可以通过以下方案导航 Kubernetes API 以访问服务:

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 被使用时,所有节点(代表虚拟机)上会开放一个指定的端口。指向该特定端口的 流量 会被系统地 路由到服务。通常,由于其缺点,这种方法不推荐使用。

列出所有 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

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

如果您不指定yaml中的nodePort(这是将要打开的端口),将使用30000–32767范围内的端口

LoadBalancer

通过使用云提供商的负载均衡器将服务公开到外部。在GKE上,这将启动一个网络负载均衡器,它将为您提供一个单一的IP地址,该地址将所有流量转发到您的服务。在AWS上,它将启动一个负载均衡器。

您必须为每个公开的服务支付负载均衡器的费用,这可能会很昂贵。

列出所有负载均衡器:

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

外部 IPs

外部 IPs 是由类型为 Load Balancers 的服务暴露的,通常在使用外部云提供商的 Load Balancer 时使用。

要查找它们,请检查 EXTERNAL-IP 字段中有值的负载均衡器。

流入集群的流量使用 external 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-servicecassandra 这样的典型选择器。您可以使用 spec.externalName 参数来指定这些服务。

例如,此服务定义将 prod 命名空间中的 my-service 服务映射到 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 服务返回一个值为 my.database.example.comCNAME 记录。访问 my-service 的方式与其他服务相同,但有一个关键区别,即 重定向发生在 DNS 层面 而不是通过代理或转发。

列出所有 ExternalNames:

kubectl get services --all-namespaces | grep ExternalName

Ingress

与上述所有示例不同,Ingress 不是一种服务类型。相反,它位于**多个服务前面,充当“智能路由器”**或集群的入口点。

您可以使用 Ingress 做很多不同的事情,并且有许多类型的 Ingress 控制器具有不同的功能

默认的 GKE Ingress 控制器将为您启动一个 HTTP(S) 负载均衡器。这将允许您对后端服务进行基于路径和子域的路由。例如,您可以将 foo.yourdomain.com 上的所有请求发送到 foo 服务,将 yourdomain.com/bar/ 路径下的所有请求发送到 bar 服务。

在 GKE 上使用 L7 HTTP 负载均衡器 的 Ingress 对象的 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

列出所有的 ingresses:

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

参考文献

支持 HackTricks

Last updated