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代理访问此服务:

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

外部 IP

外部 IP 是由类型为负载均衡器的服务暴露的,通常在使用外部云提供商负载均衡器时使用。

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

进入集群的流量,目标 IP 是外部 IP,在服务端口上,将被路由到服务端点之一externalIPs 不受 Kubernetes 管理,是集群管理员的责任。

在服务规范中,可以与任何 ServiceTypes 一起指定 externalIPs。在下面的示例中,"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

列出所有的入口:

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