EgressGateway 是一个不依赖 eBPF 数据面的 Kubernetes 固定出口 IP 控制器。它通过 EgressGatewayPolicy 选择 Pod、目的 CIDR、网关节点和传输模式,由 Controller 生成每个 Linux 节点的 EgressGatewayNodeConfig,再由 Node Agent 配置 Linux 策略路由、iptables,以及按需创建 GRE 或 WireGuard 链路。
flowchart TD
P["EgressGatewayPolicy"] --> C["Controller"]
C --> N["每节点 EgressGatewayNodeConfig"]
N --> S["源节点 Agent:ip rule + route table"]
N --> G["网关节点 Agent:FORWARD + SNAT"]
S -->|"Direct-L3 / GRE / WireGuard"| G
G -->|"固定 egress IP"| I["外部网络"]
它不加载、附加或管理 eBPF 程序,可以与 Cilium 一起运行,但替代的是 Cilium Egress Gateway 的策略路径,不替代 CNI。推荐 Cilium 使用 legacy host routing、iptables masquerading,并关闭 Cilium Egress Gateway policy;Cilium 仍然负责正常的 Pod 网络。
Pod 访问集群外部服务时,出口地址通常不是工作负载的稳定属性:Overlay 网络一般由 Pod 所在节点做 SNAT,Underlay 网络可能直接使用 Pod 地址。Pod 被重新调度、节点扩缩容或网络故障切换后,外部看到的地址就会变化,导致数据库白名单、第三方 API 白名单、审计追踪和故障诊断都需要跟着改。规模越大,靠节点名或 Pod IP 手工维护出口地址越不可行。
另一类现实约束是,集群不一定能使用依赖 eBPF 的 Cilium CEGP,也不一定具备 WireGuard 内核模块:可能是内核版本、发行版、云厂商内核、运维安全基线或现有 CNI 的 datapath 选择。Calico、Flannel、Weave 以及许多自研/轻量 CNI 仍把流量交给 Linux 路由、转发和 netfilter;它们需要一个不接管 CNI、也不要求 eBPF 的固定出口层。
因此,本项目把需求边界收敛为:通过一个简化的 EgressGatewayPolicy 选择 Pod、目的 CIDR、网关节点和 wireguard、direct、gre 之一;Controller 只读取 Kubernetes 通用对象,Agent 只管理自有链路、策略路由和 EGW-* iptables 链。只要 CNI 让选中 Pod 的流量经过宿主机 Linux L3、FORWARD 和 POSTROUTING 路径,就可以复用这层固定出口能力。
本项目的设计受到 SpiderNet EgressGateway 的固定出口目标启发,但取舍不同:SpiderNet 覆盖 IPv6、HA、多实例、命名空间默认出口和 Spiderpool 等更多功能;本项目当前只保留两个 CRD(用户侧 EgressGatewayPolicy、内部 EgressGatewayNodeConfig),聚焦 IPv4、单网关、预配置 egress IP 和更少的 CNI 专用耦合。这里的“兼容更多”指兼容标准 Linux netfilter 边界,并不等于已经实现 SpiderNet 的所有高级功能。
兼容性由数据包路径决定,不由 CNI 名称决定。完整矩阵、检查命令和失败诊断见 docs/cni-compatibility.md。
| CNI/模式 | 当前结论 | 必要条件 |
|---|---|---|
| Calico 标准 Linux/iptables datapath | 设计可行,待 Calico e2e | bpfEnabled=false;EGW-FORWARD jump 必须稳定处于 FORWARD 首位且合法流量继续进入 Calico policy;Calico 使用的 iptables backend 与 Agent 一致 |
| Flannel VXLAN/host-gw/普通 Linux datapath | 设计可行,待 Flannel e2e | 确认转发、PodCIDR、iptables backend 和上游 SNAT 路径;FORWARD 默认 DROP 时必须由 EGW 后续的 KUBE/Flannel/组合 policy 或主机规则显式允许 |
| Weave 或其他 iptables/netfilter CNI | 条件支持,待专用 e2e | Pod 流量必须经过宿主机 FORWARD/POSTROUTING,且没有 CNI 规则覆盖 EGW-* 的预期顺序 |
| Cilium 1.13.18 legacy host routing + iptables | Direct/GRE 已在 cgw1 e2e 验证 | Kubernetes 1.28.15、KPR Disabled、Host Routing Legacy、iptables masquerading;关闭 Cilium CEGP/BPF masquerade;当前验证覆盖跨节点 PBR、GRE 协议 47、独立 egress IP SNAT、RETURN 后续放行、fail-closed 和 repair,不代表 Cilium native |
| Cilium native + iptables | 设计可行,待 e2e | 仍需证明选中流量经过 host netfilter;不要把 native 模式等同于本项目已验证的 cgw1 legacy 模式 |
| Cilium/Calico eBPF、host-netfilter bypass、特殊 DSR | 不支持 | 这类模式可能绕过本项目依赖的 Linux netfilter 路径 |
| Spiderpool/underlay/secondary-IP 等特殊模式 | 条件支持,未做专用集成 | 需要先证明目标地址会回到宿主机路由/iptables;可能需要 CNI 自己的 hijack/路由配置 |
没有 Node PodCIDR 的自定义 IPAM 仍可以使用,但应通过 controller.additionalExcludedCIDRs 配置完整 Pod 网段;否则 Controller 只能为当前 Pod 添加 /32 排除项。
所有“设计可行”都必须满足同一组通用契约:每个选中 Pod 有唯一 IPv4 status.podIP;源节点和网关节点的 host namespace 都能路由到这些 Pod IP(尤其是 SNAT 回复回源路径);Pod 出站经过宿主机 L3/RPDB 和 netfilter;CNI/kube-proxy 与 Agent 使用同一个 iptables backend;CNI 不在 EGW SNAT 后二次改写;0.0.0.0/0 策略配置完整的 Pod/Service/Node/internal 排除范围。SR-IOV/DPDK/direct device、eBPF/TC/XDP bypass 和没有宿主机回程路由的 underlay 不在当前支持范围。
本项目不替代 CNI NetworkPolicy。EGW-FORWARD 先拒绝排除范围、错误出口和专用 tunnel 入口的未知流量;Direct 物理入口只拒绝策略声明的 Pod/目标错误路径,不会对整块物理接口安装 default-deny。合法流量使用 RETURN 继续执行后续 Calico/Cilium/主机 FORWARD policy,不会由本项目直接 ACCEPT。有 NetworkPolicy 的集群必须同时验证 allow/deny、首位 jump 稳定性和网关计数器,不能只看固定出口 IP。
EgressGatewayNodeConfig 是 Controller 与 Agent 之间的内部 CRD,不是用户配置入口,禁止手工创建或修改。若内部 desired state 无法解析,或 NodeConfig 消失后宿主机状态因外部 RPDB/路由表冲突无法安全清理,Agent 的最后手段会把本节点 EGW-FORWARD 置为全 FORWARD DROP,直到有效配置恢复或清理成功。这会临时中断该节点所有普通 Pod 转发,但避免旧隧道和网关本地 Pod 回落到普通出口;hostNetwork/宿主机 OUTPUT 本来就不在支持范围内。
重要: Docker Hub 的
layzer/egressgateway:v0.0.1和远端 OCI Chart0.0.1是历史 WireGuard-only 版本,不能用于验证 Direct/GRE。当前发布候选是镜像v0.0.2-rc.2、Chart0.0.2-rc.2,以下 digest 已从远端重新拉取验证。
- 历史镜像:
layzer/egressgateway:v0.0.1(不含本轮修复) - 当前镜像:
docker.io/layzer/egressgateway:v0.0.2-rc.2,OCI index digestsha256:52d882c6d95806c7bcdc2ba5e670d7d3bc09dddc2053825e8520a83f8a0a72bf - 平台 manifest:
linux/amd64为sha256:369c7c441e47410dd62cf9121d339808654410aaaef21f19a35d98323ffd5154,linux/arm64为sha256:6e9c4d4abc8588d12340c7bdb45f858d9f5eac23573bff3e8dcba1f2e2d3e32b - 当前 Chart:
oci://registry-1.docker.io/layzer/egressgateway0.0.2-rc.2,OCI digestsha256:3bf0dea5cee629d2ebe015091ae8a7ebc73c13572df649df57ac60e79c148cf8 - 本地 Chart 包:
dist/egressgateway-0.0.2-rc.2.tgz,SHA25665ad6247332a492b57fe9e2e9da11556835832a52a1e6549a958420c40cfef83 - 支持架构:
linux/amd64、linux/arm64 - 历史 Helm chart:
egressgateway0.0.1(不含本轮 CRD/代码修复) - Kubernetes:
1.28或更高版本 - 目标系统:Linux 节点;Controller 可以运行在任意满足 Kubernetes 调度条件的 Linux 节点
- 每个集群只能安装一套 EgressGateway;CRD、NodeConfig、
egw-*链路、RPDB 表和EGW-*iptables 链都是集群/节点级资源,不能用第二个 Helm release 并行运行。
这是一个发布候选版本。它支持单个策略选择一个 Ready 网关节点,不提供自动网关故障转移或已有连接迁移。首次观察和编程存在最终一致性窗口,要求零首包泄漏的工作负载应在策略同时达到 Accepted=True、Resolved=True、Programmed=True 后再启动,或在 CNI/admission 层增加启动门禁。Programmed=True 单独只表示当前期望状态已经下发;在网关失效时,它也可能表示 Blackhole 已成功应用。
当前版本支持:
- Kubernetes
1.28+、Linuxamd64/arm64、IPv4、普通单网络 Pod。 - 每个 Pod 最多一个生效的
EgressGatewayPolicy。 - 每个策略恰好一个 Ready 的 Linux 网关节点。
- 每个策略一个已经配置到网关接口上的 egress IP。
- 每个策略显式选择
wireguard、direct或gre;旧策略省略transport时保持 WireGuard。 - Direct 通过同二层网关 InternalIP 下一跳转发,不创建 tunnel;GRE 使用 IP 协议 47;WireGuard 使用 UDP 51820。
- 当前 RETURN 版本已在 cgw1 的 Cilium 1.13.18 legacy host routing + iptables 环境验证 Direct 和 GRE;Cilium native、Calico、Flannel、麒麟 arm64 4.19 仍需各自集群 e2e。Direct 不依赖额外 tunnel 模块,GRE 是否可用仍以目标内核实际创建 link 和协议 47 连通测试为准。
当前版本不支持 IPv6、hostNetwork Pod 选择、Multus secondary IP、自动网关故障转移、既有 NAT 连接迁移、L7 策略和单策略高可用。网关节点、egress IP 或目的策略发生变化时,已有 NAT 连接可能被重置。
规划器有固定容量上限,用于避免选择器或 Service 数量把 iptables 规则扩展到不可控规模:
- 32 个 workload selector、256 个目的 CIDR、1024 个显式排除 CIDR、1024 个选中 Pod。
- 4096 个解析后的排除项、100000 条估算网关规则。
- 集群最多 1024 个策略;每个源节点最多 8192 条源规则、100000 条源路由。
- 每个网关节点最多 150000 条估算防火墙规则和 1024 个 WireGuard/GRE peer。
- 每个 NodeConfig spec 序列化后最多 1000000 字节。
超出上限、策略冲突、没有可用网关或 Agent 未 Ready 时,Controller 会对可识别的选中流量安装黑洞,而不是回落到普通节点出口。容量拒绝时,新增而未被旧 scope 接受的 Pod/目的范围不由本控制器保护,可能继续使用普通出口;应修正选择器/CIDR 或拆分策略。
- Docker Engine
24+,并启用 Docker Buildx;多架构构建需要 builder 支持linux/amd64和linux/arm64。 - Go
1.25.12(仅在本地编译二进制或运行 Go 测试时需要;Docker 构建会在构建阶段使用固定版本)。 - Helm
3.8+(需要 OCI registry 支持;Helm 4 也可使用本文命令)。 kubectl,版本与目标 Kubernetes 集群兼容。- 具备推送权限的 Docker Hub 账号;镜像命名空间必须是
layzer,或将命令中的命名空间替换为实际账号。
- Kubernetes API Server
1.28+,允许安装本 chart 中的两个 CRD,并允许 Controller 创建/更新EgressGatewayNodeConfig。 - Controller 需要读取 Namespaces、Nodes、Pods、Services,可选读取 Cilium
v2.CiliumNode。 - Agent DaemonSet 使用
hostNetwork: true、UID 0、NET_ADMIN和NET_RAW,挂载以下宿主机路径:/var/lib/egressgateway:持久化 WireGuard 私钥,文件权限为0600;/run/xtables.lock:与宿主机 iptables 操作同步;/proc/sys/net/ipv4:读取和设置 IPv4 转发相关 sysctl。
- 命名空间的 Pod Security、准入策略和节点安全策略必须允许上述 hostNetwork、hostPath、capability 和 root 权限。若集群启用了 Pod Security Admission,请先为安装命名空间配置合适的安全级别。
- 所有模式都需要
iproute2和iptables。只有wireguard需要内核 WireGuard 与wireguard-tools;只有gre需要内核能创建grelink;direct不需要额外 tunnel 模块。 net.ipv4.ip_forward=1。net.ipv4.conf.all.rp_filter以及 endpoint、egress、远端/本地 Pod 实际路由涉及的接口必须为0或2,不能是严格模式1;Agent 会按内核 RouteGet 结果逐接口拒绝严格值。- WireGuard 节点间放行 UDP 51820;GRE 节点间放行 IP 协议 47;Direct 的源节点必须能在同一二层直接解析并到达网关 InternalIP,不能经过另一个 IP gateway。
- egress IP 已经配置在策略声明的网关接口上。Direct 的 gateway endpoint 还必须存在于
transport.interface(省略时等于 egress interface)。Agent 会为每个 destination CIDR 选择一个未被 exclusion 覆盖的确定性代表地址,并在 Ready 前验证出口;endpoint 和 Pod 回程不能递归指向自有egw-*链路。复杂 PBR/ECMP/VRF 仍必须逐目标 e2e。 - 上游网络允许该 egress IP(包括反欺骗、ARP/NDP、路由和安全组);本控制器不会为 egress IP 自动做地址配置。
agent.iptablesMode=auto在 Agent 启动时解析未限定名iptables/restore/save 的真实路径,映射到对应的显式iptables-legacy或iptables-nft命令族,随后固定使用该 backend;Cilium 探测同时检查 NAT 和 filter 表。运行期间切换 system alternatives 不会切换已运行的 Agent;CNI backend 变化时必须同步更新 Helm 值并滚动 Agent。若 generic 命令由 wrapper 提供、无法安全映射,Agent 会拒绝启动,此时必须显式设置相同的legacy或nft。- 没有其他组件占用 RPDB 优先级/路由表
20000-29999、路由协议99、整个egw-接口名前缀或EGW-POSTROUTING/EGW-FORWARD链。Agent 当前创建egw-in和egw-<10 位十六进制>;会拒绝 protocol 非99的外部 rule/table 状态且不会覆盖它。同为 protocol99的第三方状态无法可靠区分,属于不支持的冲突配置。 - Controller 能观察完整 Pod 地址空间。它读取
Node.spec.podCIDR[s],可选读取 CiliumNode 的spec.ipam.podCIDRs,也可以通过controller.additionalExcludedCIDRs配置;发现范围外的当前 Pod IP 会安全地加入/32排除项。 - 每个选中 Pod IP 在源节点和网关节点 host namespace 都必须命中非 default 的可达路由;Agent 在 Ready 前验证本地 Pod CNI route 和网关远端 Pod 回程,本项目不为 underlay、secondary IP、SR-IOV/DPDK 设备自动创建路由。
EGW 的 RPDB priority 位于 20000-29999。数值更小、且会终止查找的 CNI/自定义 ip rule 可以在 EGW 之前截获选中流量;当前预检只保护本项目 priority/table 的所有权,不能证明任意第三方 PBR 的最终效果。目标集群必须以 ip route get DESTINATION from POD_IP 和真实连通性测试确认命中预期路径,否则该组合不属于已认证支持。
Controller 和 Agent 的安装不需要 eBPF。CNI 的其他 datapath 只有在仍把选中流量交给宿主机 netfilter 时才可共存;若 CNI 通过 eBPF/DSR/host-netfilter bypass 直接处理流量,本项目无法拦截。若要替代 Cilium CEGP,必须关闭另一套 Egress Gateway,避免重复改写。
IMAGE=layzer/egressgateway
TAG=v0.0.2-rc.2
docker buildx inspect --bootstrap
docker buildx build \
--platform linux/amd64 \
--load \
--build-arg VERSION="$TAG" \
-t "$IMAGE:$TAG-amd64" .
docker run --rm --platform linux/amd64 "$IMAGE:$TAG-amd64" version
docker buildx build \
--platform linux/arm64 \
--load \
--build-arg VERSION="$TAG" \
-t "$IMAGE:$TAG-arm64" .
docker run --rm --platform linux/arm64 "$IMAGE:$TAG-arm64" versionDockerfile 使用 TARGETARCH 交叉编译,镜像运行时包含:
ca-certificatesiproute2iptables(legacy 和 nft 命令族)wireguard-tools
不要覆盖历史 v0.0.1;为当前源码选择新的不可变 tag。也不要把密码或 PAT 直接写进 shell 历史、脚本或 README。使用 registry 的实际用户名和标准输入登录:
read -r -s DOCKERHUB_TOKEN
printf '%s' "$DOCKERHUB_TOKEN" | docker login \
--username DOCKERHUB_USERNAME \
--password-stdin
unset DOCKERHUB_TOKEN
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg VERSION=v0.0.2-rc.2 \
--build-arg COMMIT=unversioned \
--build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--push \
-t layzer/egressgateway:v0.0.2-rc.2 .
docker buildx imagetools inspect layzer/egressgateway:v0.0.2-rc.2最后一条命令应显示 linux/amd64 和 linux/arm64 两个平台。若账号只提供邮箱,请在 Docker Hub 账号设置中确认对应的登录用户名;邮箱不一定等于 Docker Hub namespace。
Chart 版本和镜像 tag 有意分开。当前源码必须使用高于历史 0.0.1 的新 semver,并引用上一步的新镜像 tag;不要把修复后内容覆盖到同一个不可变版本号。
mkdir -p dist
helm lint charts/egressgateway
helm template egressgateway charts/egressgateway \
--namespace kube-system \
--kube-version 1.28.15 >/tmp/egressgateway-candidate.yaml
helm package charts/egressgateway \
--version 0.0.2-rc.2 \
--app-version v0.0.2-rc.2 \
--destination dist
read -r -s DOCKERHUB_TOKEN
printf '%s' "$DOCKERHUB_TOKEN" | helm registry login registry-1.docker.io \
--username DOCKERHUB_USERNAME \
--password-stdin
unset DOCKERHUB_TOKEN
helm push dist/egressgateway-0.0.2-rc.2.tgz \
oci://registry-1.docker.io/layzer
helm show chart \
oci://registry-1.docker.io/layzer/egressgateway \
--version 0.0.2-rc.2推送成功后,从 OCI 安装:
helm upgrade --install egressgateway \
oci://registry-1.docker.io/layzer/egressgateway \
--version 0.0.2-rc.2 \
--namespace kube-system \
--create-namespace \
--set image.repository=layzer/egressgateway \
--set image.tag=v0.0.2-rc.2如果 chart 或镜像仓库是私有的,还需要配置 imagePullSecrets,并在所有运行 Agent 的节点可用。
Helm 不会在 helm upgrade 时升级 chart crds/ 目录中的既有 CRD。从早期版本升级当前源码前,必须先应用本工作区生成的两个 CRD 并等待 Established;否则 API Server 可能拒绝或裁剪 Direct/GRE 字段:
kubectl apply -f config/crd/bases/
kubectl wait --for=condition=Established --timeout=60s \
crd/egressgatewaypolicies.networking.sealos.io \
crd/egressgatewaynodeconfigs.networking.sealos.io旧 CRD 若由 Helm 创建,首次执行可能提示缺少 last-applied-configuration;kubectl apply 会补上该注解。不要改用新的 server-side field manager:现有 Helm manager 通常拥有 .spec.versions,SSA 会发生字段所有权冲突;也不要用 --force-conflicts 抢占 CRD。
如果不从新 OCI 版本安装,也可以直接使用工作区 chart;镜像必须是从当前源码构建并推送的新 tag:
helm upgrade --install egressgateway ./charts/egressgateway \
--namespace kube-system \
--create-namespace \
--set image.repository=layzer/egressgateway \
--set image.tag=v0.0.2-rc.2先为每个策略选择的网关节点加标签。一个策略只能匹配一个 Ready Linux 网关节点:
kubectl label node GATEWAY_NODE \
networking.sealos.io/egress-gateway=true关键 chart 参数如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
wireGuardPort |
51820 |
Agent WireGuard UDP 端口 |
controller.metricsPort / healthPort |
8080 / 8081 |
Controller 指标和探针端口 |
agent.metricsPort / healthPort |
19962 / 9963 |
Agent 指标和探针端口;避开常见 CNI/hostNetwork 指标端口 |
agent.mtu |
1380 |
WireGuard/GRE 链路 MTU;Direct 不创建 tunnel |
agent.iptablesMode |
auto |
auto、legacy 或 nft |
agent.repairInterval |
30s |
Agent 失败后的修复间隔 |
agent.keyHostPath |
/var/lib/egressgateway |
WireGuard 私钥目录 |
controller.additionalExcludedCIDRs |
[] |
额外不进入策略路由的 IPv4 CIDR |
当策略使用 0.0.0.0/0 时,必须确保所有集群 Pod CIDR、Service/节点内部地址和应用内部网络都在排除范围内。Controller 会自动发现 Node PodCIDR、可用的 CiliumNode PodCIDR、当前 Pod /32、节点地址、Service ClusterIP、loopback/link-local/multicast/reserved 范围;没有 PodCIDR 的 CNI 应显式设置完整 Pod 网段:
helm upgrade egressgateway ./charts/egressgateway \
--namespace kube-system \
--reuse-values \
--set-string 'controller.additionalExcludedCIDRs[0]=10.244.0.0/16'RFC1918 网段不会被无条件自动排除,因为它们也可能是用户希望固定出口的合法目的地址。
先把示例中的 egress IP、网卡、endpoint、选择器和目标 CIDR 改成真实值。egressIP 必须已经存在于网关节点的 interface 上,本控制器不会自动添加地址;interface 不能使用项目保留的 egw- 前缀。
tunnelEndpointIP 是三种模式共同使用的网关节点 IPv4 InternalIP,必须出现在所选 Node 的 status.addresses 中。当网关恰好只有一个 IPv4 InternalIP 时可以省略;存在多个时必须显式配置,否则策略会保持 Resolved=False 并黑洞已识别的选中流量。
transport.mode |
用途 | 强制前提 |
|---|---|---|
direct |
同二层、无 tunnel、最低开销;适合缺少 WireGuard 的麒麟 4.19 节点 | 源节点直连 gateway InternalIP;transport.interface 是网关 underlay 入口,省略时使用 egress interface |
gre |
节点跨 VLAN/三层路由 | 内核能创建 GRE;网络放行 IP 协议 47;每个源 Node 只有一个 IPv4 InternalIP |
wireguard |
需要加密的节点间 tunnel | 内核 WireGuard、UDP 51820;省略整个 transport 时使用此模式 |
apiVersion: networking.sealos.io/v1alpha1
kind: EgressGatewayPolicy
metadata:
name: fixed-internet-egress
spec:
selectors:
- namespaceSelector:
matchLabels:
networking.sealos.io/fixed-egress: "true"
podSelector:
matchLabels:
networking.sealos.io/fixed-egress: "true"
destinationCIDRs:
- 0.0.0.0/0
excludedCIDRs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
egressGateway:
nodeSelector:
matchLabels:
networking.sealos.io/egress-gateway: "true"
tunnelEndpointIP: 10.0.0.12
transport:
mode: direct
interface: eth0
interface: eth0
egressIP: 203.0.113.10GRE 只替换 transport 段,不设置物理入口:
transport:
mode: greWireGuard 显式写法如下,旧对象完全省略 transport 也等价:
transport:
mode: wireguardkubectl label namespace APP_NAMESPACE \
networking.sealos.io/fixed-egress=true
kubectl -n APP_NAMESPACE label pod APP_POD \
networking.sealos.io/fixed-egress=true
kubectl apply -f config/samples/networking_v1alpha1_egressgatewaypolicy.yaml生产环境不要使用示例中的 203.0.113.10;替换为已分配、可路由且通过上游反欺骗检查的真实地址。
kubectl get egressgatewaypolicies
kubectl describe egressgatewaypolicy fixed-internet-egress
kubectl get egressgatewaynodeconfigs
kubectl -n kube-system rollout status deploy/egressgateway-controller --timeout=5m
kubectl -n kube-system rollout status ds/egressgateway-agent --timeout=5m
kubectl -n kube-system logs deploy/egressgateway-controller
kubectl -n kube-system logs ds/egressgateway-agent --prefix策略只有同时满足 Accepted=True、Resolved=True 和 Programmed=True 才表示可用路径的控制面编程完成。Programmed=True 单独表示当前期望状态已被 Agent 应用:当 Accepted=False 或 Resolved=False 时,它可能表示 Blackhole 已正确下发。Agent Ready 包含 transport capability、iptables backend、egress/Direct endpoint address、代表目标出口、underlay endpoint、Pod 回程、RPDB/table ownership 和路径接口 rp_filter 的本机检查;三条件全 True 仍不证明 WireGuard 已握手、GRE 协议 47 已被网络放行、CIDR 内所有动态路由、上游 ACL/NAT 或真实应用回包,不能替代端到端数据包测试。
在选中 Pod 内访问两个独立的公网地址,并与配置的固定 egress IP 比较;再从未选中 Pod 重复一次:
kubectl -n TEST_NAMESPACE exec TEST_POD -- sh -c \
'curl -4fsS https://api.ipify.org; echo; curl -4fsS https://ifconfig.me/ip; echo'目标集群的内核、CNI 链顺序、回程 conntrack、上游路由、反欺骗和多网卡行为必须在 Linux 集群上验证。完整检查清单见 docs/testing.md。
Agent 只管理以下自有资源:
- 整个
egw-接口名前缀;WireGuard 使用egw-in/egw-<hash>,GRE 使用点到点egw-<hash>,Direct 不创建接口; - RPDB 优先级和
20000-29999路由表,路由规则协议99; EGW-POSTROUTING、EGW-FORWARD及其从内置链跳转的规则;- 每个节点的
/var/lib/egressgateway/wireguard.key。
它不会 flush Cilium、kube-proxy 或内置 iptables 链。SNAT 使用内核 conntrack,回复包在同一网关反向 NAT 后沿现有 Pod 网络返回源节点,不会在网关之间复制 conntrack 状态。
Controller、Agent 和可运行 Agent 的节点应被视为同一个集群级可信计算基。Agent 拥有 hostNetwork、UID 0、NET_ADMIN、NET_RAW、hostPath 和宿主机 sysctl 权限;不要在不可信节点或可能被工作负载攻破的节点上运行它。限制 EgressGatewayPolicy 的写权限,只授予网络管理员。
不要先卸载 Helm。删除 DaemonSet 不会自动运行 Agent 的宿主机清理,Helm 也会保留 CRD。使用仓库提供的 fail-fast helper:
NAMESPACE=kube-system RELEASE=egressgateway \
./hack/cleanup.sh脚本会停止 Controller、写入并等待空的 NodeConfig revision,在每个节点执行清理,确认成功后才 orphan/delete Agent Pod 并卸载 Helm。只有确认所有节点的 RPDB、路由、WireGuard/GRE 链路和 EGW-* iptables 链都已删除后,才删除 CRD;已有的 EgressGatewayPolicy 必须先删除。
从 OCI chart 安装时,chart 会创建一个带有同一 fail-fast helper 的 ConfigMap。若本地没有仓库源码,可在卸载前导出它再执行:
kubectl -n kube-system get configmap \
-l app.kubernetes.io/instance=egressgateway,app.kubernetes.io/component=cleanup \
-o jsonpath='{.items[0].data.cleanup\.sh}' \
>/tmp/egressgateway-cleanup.sh
chmod +x /tmp/egressgateway-cleanup.sh
NAMESPACE=kube-system RELEASE=egressgateway \
/tmp/egressgateway-cleanup.sh若只需要清理某一个 iptables 后端,可设置 IPTABLES_MODE=legacy 或 IPTABLES_MODE=nft;默认 auto 会尝试清理可用后端。
make generate
make helm-crds
go test ./...
go test -race ./...
./hack/cleanup_test.sh
go vet ./...
helm lint charts/egressgateway
helm template test charts/egressgateway \
--namespace kube-system \
--kube-version 1.28.15 >/tmp/egressgateway.yaml本地测试覆盖规划、状态聚合、命令渲染、资源所有权和 reconcile 顺序;Linux 容器测试覆盖 GRE/netlink 结构,但不能替代真实节点的 CNI 链、协议 47、同二层 Direct、上游路由或 SNAT 测试。发布前必须完成两架构镜像 smoke test、Helm package 校验和目标集群的 docs/testing.md 测试。