monzo/egress-operator

GitHub: monzo/egress-operator

一个基于 kubebuilder 构建的 Kubernetes operator,通过生成 Envoy 代理网关 Pod 并结合 CoreDNS 插件重写 DNS 解析,利用 NetworkPolicy 实现对外部服务访问的精细化控制。

Stars: 258 | Forks: 26

# egress-operator 一个用于生成 egress gateway pod 并通过网络策略控制对其访问的 operator,以及一个用于将 egress 流量路由到这些 pod 的 coredns 插件。 其核心思想是,与其通过协议检查来授权 egress 流量, 不如为你使用的每个外部服务创建一个内部 clusterIP,通过 网络策略将其限制为仅允许少数 pod 访问,然后设置你的 dns 服务器 将该外部服务解析为该 clusterIP。 使用 kubebuilder 构建:https://golang.org/doc/install 该 operator 接受非命名空间的 ExternalService 对象,这些对象定义了外部服务的 dns 名称和端口。 在 `egress-operator-system` 命名空间中,它会创建: - 一个用于到该服务的 TCP/UDP 代理的 envoy configmap(在启用 UDP 的下一个 envoy 版本发布之前,UDP 功能暂不可用) - 一个用于运行该配置的 envoy pod 的 deployment - 一个水平 pod 自动扩缩容器 (HPA),用于保持 deployment 处于合理的大小 - 一个针对该 deployment 的 service - 一个网络策略,仅允许其他命名空间中带有 `egress.monzo.com/allowed-: true` 标签的 pod ## 前置条件 1. 你需要拥有一个私有容器仓库来托管 egress-operator 镜像,例如 AWS Elastic Container Repository (ECR) 或 GCP Container Registry (GCR),并且你的集群必须能够访问该仓库。在下面的说明中,这将被称为 `yourrepo`。 2. 你的本地系统必须安装有最新版本的 `golang` 以用于构建代码,你可以按照[此处](https://golang.org/doc/install)的说明进行安装。 3. 你的本地系统必须安装有用于代码生成的 Kubebuilder,你可以按照[此处](https://book.kubebuilder.io/quick-start.html)的说明进行安装。 4. 你的本地系统必须安装有用于构建 Kubernetes 清单的 Kustomize,你可以按照[此处](https://kubernetes-sigs.github.io/kustomize/installation/)的说明进行安装。 5. 你的集群必须运行 CoreDNS 而不是 kube-dns,如果你使用的是托管 Kubernetes 服务,则可能并非如此。[这篇文章](https://medium.com/google-cloud/using-coredns-on-gke-3973598ab561)提供了关于 GCP Kubernetes Engine 的一些帮助,有关 AWS Elastic Kubernetes Service 的指南可以在[此处](https://docs.aws.amazon.com/eks/latest/userguide/coredns.html)找到。 ## 安装说明 ### 针对远程集群进行本地测试 ``` make run ``` 这会创建一个 ExternalService 对象,以观察 controller-manager 在远程集群中创建受管资源。 ### 设置 CoreDNS 插件 CoreDNS 插件会重写由 egress-operator 管理的外部服务主机名的响应。 构建一个包含该插件的 CoreDNS 镜像: ``` cd coredns-plugin make docker-build docker-push IMG=yourrepo/egress-operator-coredns:latest ``` 你需要将 coredns kubedns Deployment 的镜像替换为 `yourrepo/egress-operator-coredns:latest`: ``` kubectl edit deploy coredns -n kube-system # Your Deployment name may vary ``` 并编辑 ConfigMap 中的 coredns Corefile,加入 `egressoperator egress-operator-system cluster.local`: ``` kubectl edit configmap coredns-config -n kube-system # Your ConfigMap name may vary ``` CoreDNS 配置示例: ``` .:53 { egressoperator egress-operator-system cluster.local kubernetes cluster.local forward . /etc/resolv.conf } ``` ### 在集群中设置 controller manager 及其 `CustomResourceDefinition` ``` make docker-build docker-push install IMG=yourrepo/egress-operator:v0.1 make deploy IMG=yourrepo/egress-operator:v0.1 ``` ## 用法 一旦 controller 和 dns 服务器开始运行,请创建 ExternalService 对象,用于指定你想要 捕获其流量的 dns 名称。针对该名称的 Dns 查询将被重写,指向 gateway pod。 默认情况下,你的客户端 pod 需要带有标签 `egress.monzo.com/allowed-gateway: nameofgateway` 才能 到达目标,但出于测试目的,你始终可以编写一个额外的 NetworkPolicy 来选择 gateway pod 并允许所有流量。 一个 ExternalService 示例: ``` apiVersion: egress.monzo.com/v1 kind: ExternalService metadata: name: google spec: dnsName: google.com # optional, defaults to false, instructs dns server to rewrite queries for dnsName hijackDns: true ports: - port: 443 # optional, defaults to TCP protocol: TCP # optional, defaults to 3 minReplicas: 5 # optional, defaults to 12 maxReplicas: 10 # optional, defaults to 50 targetCPUUtilizationPercentage: 30 # optional, if not provided then defaults to 100m, 50Mi, 2, 1Gi resources: requests: cpu: 1 memory: 100Mi limits: cpu: 2 memory: 200Mi ``` ### 阻止非 gateway 流量 该 operator 不会为你阻止任何流量,它只是为通过 egress 网关的流量建立了一些允许的路由。 你需要一个默认拒绝的策略来阻止不通过网关的流量。为此,你可能 需要在每个你想要控制的命名空间中设置一个类似这样的策略: ``` apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-external-egress namespace: your-application-namespace spec: podSelector: {} policyTypes: - Egress egress: - to: - ipBlock: # ensure your internal IP range is allowed here # traffic to external IPs will not be allowed from this namespace. # therefore, pods will have to use egress gateways cidr: 10.0.0.0/8 ``` 如果你已经有一个默认拒绝的 egress 策略,则不需要上述内容。相反,你会希望显式允许 从你的 pod 到所有 gateway pod 的 egress。gateway pod 上的 ingress 策略将确保只有正确的流量被 允许。 ### 配置 operator 的全局配置是使用环境变量设置的。 可以将 Node Selectors 和 Taint tolerations 添加到 gateway pod,以确保 pod 运行在被允许访问互联网的节点上。示例: ``` env: - name: NODE_SELECTOR_KEY value: role - name: NODE_SELECTOR_VALUE value: egress-pods - name: TAINT_TOLERATION_KEY value: egress-pods - name: TAINT_TOLERATION_VALUE value: "true" ``` 生成的 gateway pod 配置如下: ``` spec: nodeSelector: role: egress-pods tolerations: - effect: NoSchedule key: egress-pods value: "true" ... ``` **你还可以配置 [pod 拓扑分布约束](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)** ``` env: - name: ENABLE_POD_TOPOLOGY_SPREAD value: "true" - name: POD_TOPOLOGY_ZONE_MAX_SKEW value: 1 - name: POD_TOPOLOGY_HOSTNAME_MAX_SKEW value: 1 ``` 这会将拓扑分布约束注入到 gateway pod 中,这将 确保 pod 跨区域和主机分布。 ``` spec: topologySpreadConstraints: - labelSelector: matchLabels: app: egress-gateway egress.monzo.com/gateway: egress-gateway-name maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: ScheduleAnyway - labelSelector: matchLabels: app: egress-gateway egress.monzo.com/gateway: egress-gateway-name ``` 你还可以通过[感知拓扑的路由](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/)将服务设置为其端点能够感知网络拓扑。 如果你设置环境变量 `ENABLE_SERVICE_TOPOLOGY_MODE="true"`,这将启用设置注解 `service.kubernetes.io/topology-mode="auto"`。 如果你开启了此功能,你还可以将 `spec.serviceTopologyMode` 设置为任何值,如果你想为特定的 egress 禁用此功能的话。例如, 如果你设置 `spec.serviceTopologyMode: "None"`,这将针对你的 ExternalService 禁用感知拓扑的路由。 | 变量名 | 默认值 | 描述 | |------------------------------------|-------------------------------------------|-----------------------------------------------------------| | ENVOY_IMAGE | `envoyproxy/envoy-alpine:v1.16.5` | 要使用的 Envoy Proxy 镜像的名称 | | TAINT_TOLERATION_KEY | 空,不应用 tolerations | 应用于 gateway pod 的 Toleration 键 | | TAINT_TOLERATION_VALUE | 空,不应用 tolerations | 应用于 gateway pod 的 Toleration 值 | | NODE_SELECTOR_KEY | 空,不添加 node selector | 应用于 gateway pod 的 Node selector 标签键 | | NODE_SELECTOR_VALUE | 空,不添加 node selector | 应用于 gateway pod 的 Node selector 标签值 | | POD_TOPOLOGY_ZONE_MAX_SKEW_KEY | `topology.kubernetes.io/zone` | 区域约束的拓扑键 | | POD_TOPOLOGY_ZONE_MAX_SKEW | 空,不注入区域约束 | 区域约束的 maxSkew 值 | | POD_TOPOLOGY_HOSTNAME_MAX_SKEW_KEY | `kubernetes.io/hostname` | 主机名约束的拓扑键 | | POD_TOPOLOGY_HOSTNAME_MAX_SKEW | 空,不注入主机名约束 | 主机名约束的 maxSkew 值 | | ENABLE_SERVICE_TOPOLOGY_MODE | 空,不添加注解 | 设置为 'true' 以添加拓扑模式 service 注解 |
标签:CoreDNS, Egress网关, Envoy, EVTX分析, Golang, Kubebuilder, MacOS取证, Operator, TCP/UDP代理, 出口流量管理, 子域名突变, 安全编程, 容器, 日志审计, 服务路由, 流量控制, 网络安全, 网络策略, 自动扩容(HPA), 自定义资源(CRD), 请求拦截, 隐私保护