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), 请求拦截, 隐私保护