8.6. 使用 Egress Firewall 来限制对外部资源的访问
作为 OpenShift Container Platform 集群管理员,您可以使用出口防火墙策略限制集群内的部分或所有 Pod 可以访问的外部 IP 地址。出口防火墙策略支持以下情况:
- pod 只能连接到内部主机,且无法启动到公共互联网的连接。
- pod 只能连接到公共互联网,且无法启动到 OpenShift Container Platform 集群外的内部主机的连接。
- pod 无法访问指定的内部子网或主机。
可通过以 CIDR 格式或指定 DNS 名称指定 IP 地址范围来设置出口策略。例如,您可以允许 <project_A>
访问指定 IP 范围,但拒绝对 <project_B>
相同的访问。或者,您可以限制应用程序开发人员从(Python)pip 镜像进行更新,并强制更新仅来自批准的源。
您必须 启用了ovs-multitenant 或 ovs-networkpolicy 插件,才能通过出口策略限制 pod 访问。
如果您使用 ovs-multitenant 插件,egress 策略仅与每个项目的一个策略兼容,且无法用于共享网络的项目,如全局项目。
项目管理员既不能创建 EgressNetworkPolicy
对象,也不能编辑您在项目中创建的对象。另外还有一些其他限制可以创建 EgressNetworkPolicy
:
-
default
项目(以及通过oc adm pod-network make-projects-global
使全局化的任何其他项目)不能具有出口策略。 -
如果您将两个项目合并在一起(通过
oc adm pod-network join-projects
),则无法 在任何 合并的项目中使用出口策略。 - 项目不能有多个出口策略对象。
违反这些限制会导致项目的出口策略出现问题,并可能导致所有外部网络流量被丢弃。
使用 oc
命令或 REST API 配置出口策略。您可以使用 oc [create|replace|delete]
来操作 EgressNetworkPolicy 对象
。api/swagger-spec/oapi-v1.json 文件包含对象的实际工作方式的 API 级别详情。
配置出口策略:
- 导航到您要影响的项目。
使用您要使用的策略配置创建一个 JSON 文件,如下例所示:
{ "kind": "EgressNetworkPolicy", "apiVersion": "v1", "metadata": { "name": "default" }, "spec": { "egress": [ { "type": "Allow", "to": { "cidrSelector": "1.2.3.0/24" } }, { "type": "Allow", "to": { "dnsName": "www.foo.com" } }, { "type": "Deny", "to": { "cidrSelector": "0.0.0.0/0" } } ] } }
当上例添加到项目中时,它允许到 IP 范围
1.2.3.0/24
的流量和域名www.foo.com
,但拒绝访问所有其他外部 IP 地址。流量到其他 pod 的流量不受影响,因为策略仅适用于 外部 流量。EgressNetworkPolicy
中的规则按顺序检查,第一个匹配的规则会生效。如果上面示例中的三个规则被颠倒,则不允许流量到1.2.3.0/24
和www.foo.com
,因为首先要检查0.0.0.0/0
规则,它将匹配和拒绝所有流量。域名更新根据本地非授权服务器返回的域的 TTL(time to live)值进行轮询。pod 也应该在必要时从同一本地名称服务器解析域,否则出口网络策略控制器和 pod 感知的域的 IP 地址会有所不同,出口网络策略可能无法按预期强制执行。由于出口网络策略控制器和 pod 会异步轮询相同的本地名称服务器,因此可能会有一个竞争条件,pod 在出口控制器前可能会获取更新的 IP。由于此限制,仅建议在
EgressNetworkPolicy
中使用域名来更改 IP 地址的域。注意出口防火墙始终允许 pod 访问 pod 所在的用于 DNS 解析的节点的外部接口。如果您的 DNS 解析不是由本地节点上的某个内容处理,那么如果您在 pod 中使用域名,则需要添加出口防火墙规则,以允许访问 DNS 服务器的 IP 地址。
使用 JSON 文件创建 EgressNetworkPolicy 对象:
$ oc create -f <policy>.json
通过创建 路由 来公开服务将忽略 EgressNetworkPolicy
。出口网络策略服务端点过滤在节点 kubeproxy
上完成。当涉及路由器时,kubeproxy
被绕过,且不会应用出口网络策略强制。管理员可以通过限制对创建路由的访问来防止此缺陷。
8.6.1. 使用 Egress 路由器允许外部资源识别 Pod 流量
OpenShift Container Platform 出口路由器使用一个私有源 IP 地址运行一项服务,该服务将流量重定向到指定的远程服务器。服务允许容器集与设置为仅允许从白名单 IP 地址访问的服务器进行通信。
出口路由器并不适用于每个传出的连接。创建大量出口路由器可以推送网络硬件的限制。例如,为每个项目或应用创建出口路由器可能会超出网络接口可以处理的本地 MAC 地址数,然后再回退到软件中的 MAC 地址过滤。
目前,出口路由器与 Amazon AWS、Azure Cloud 或其他不支持第 2 层操作的云平台不兼容,因为它们与 macvlan 流量不兼容。
部署注意事项
Egress 路由器向节点的主网络接口添加第二个 IP 地址和 MAC 地址。如果您没有在裸机上运行 OpenShift Container Platform,您可能需要配置虚拟机监控程序或云供应商来允许额外的地址。
- Red Hat OpenStack Platform
如果要在 Red Hat OpenStack Platform 上部署 OpenShift Container Platform,则需要将 OpenStack 环境中的 IP 和 MAC 地址列入白名单,否则 通信将失败 :
neutron port-update $neutron_port_uuid \ --allowed_address_pairs list=true \ type=dict mac_address=<mac_address>,ip_address=<ip_address>
- Red Hat Enterprise Virtualization
-
如果您使用 Red Hat Enterprise Virtualization,您应该将
EnableMACAntiSpoofingFilterRules
设置为false
。 - VMware vSphere
- 如果您使用 VMware vSphere,请参阅 VMWare 文档来保证 vSphere 标准交换器的安全。通过从 vSphere Web 客户端中选择主机的虚拟交换机来查看和更改 VMWare vSphere 默认设置。
具体来说,请确保启用了以下功能:
出口路由器模式
出口路由器可以三种不同的模式运行: 重定向模式、HTTP 代理模式和 DNS 代理模式。重定向模式可用于除 HTTP 和 HTTPS 以外的所有服务。对于 HTTP 和 HTTPS 服务,请使用 HTTP 代理模式。对于使用 IP 地址或域名的基于 TCP 的服务,请使用 DNS 代理模式。
8.6.1.1. 以重定向模式部署 Egress Router Pod
在重定向模式中,出口路由器设置 iptables 规则,将流量从其自身 IP 地址重定向到一个或多个目标 IP 地址。希望使用保留源 IP 地址的客户端 pod 必须修改来连接到出口路由器,而不是直接连接到目标 IP。
使用以下内容创建 pod 配置:
apiVersion: v1 kind: Pod metadata: name: egress-1 labels: name: egress-1 annotations: pod.network.openshift.io/assign-macvlan: "true" 1 spec: initContainers: - name: egress-router image: registry.redhat.io/openshift3/ose-egress-router securityContext: privileged: true env: - name: EGRESS_SOURCE 2 value: 192.168.12.99/24 - name: EGRESS_GATEWAY 3 value: 192.168.12.1 - name: EGRESS_DESTINATION 4 value: 203.0.113.25 - name: EGRESS_ROUTER_MODE 5 value: init containers: - name: egress-router-wait image: registry.redhat.io/openshift3/ose-pod nodeSelector: site: springfield-1 6
- 1
- 在主网络接口上创建一个 Macvlan 网络接口,并将它移到 pod 的网络项目中,然后再启动 egress-router 容器。保留
"true"
上的引号。省略它们会导致错误。要在主接口以外的网络接口上创建 Macvlan 接口,请将注解值设置为该接口的名称。例如:eth1
。 - 2
- 节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。(可选)您可以包含子网长度和
/24
后缀,以便能够设置指向本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用EGRESS_GATEWAY
变量指定的主机,且子网上没有其他主机。 - 3
- 值与节点使用的默认网关相同。
- 4
- 用于将流量定向到的外部服务器。使用这个示例,到 pod 的连接会被重定向到 203.0.113.25,源 IP 地址为 192.168.12.99。
- 5
- 这将告知出口路由器镜像,它被部署为"init 容器"。以前的 OpenShift Container Platform 版本(和出口路由器镜像)不支持这个模式,它必须作为普通容器运行。
- 6
- pod 仅部署到带有标签
site=springfield-1
的节点。
使用上述定义创建 pod:
$ oc create -f <pod_name>.json
检查 pod 是否已创建:
$ oc get pod <pod_name>
通过创建服务指向出口路由器,确保其他 pod 可以找到 pod 的 IP 地址:
apiVersion: v1 kind: Service metadata: name: egress-1 spec: ports: - name: http port: 80 - name: https port: 443 type: ClusterIP selector: name: egress-1
您的 pod 现在可以连接到此服务。使用保留的出口 IP 地址将其连接重新指向外部服务器的对应端口。
出口路由器设置由从 openshift3/ose-egress-router 镜像创建的"init 容器"执行,该容器则特权运行,以便它配置 Macvlan 接口并设置 iptables
规则。设置 iptables
规则后,它会退出,openshift 3/ose-pod 容器将运行(什么都不运行),直到 pod 被终止为止。
环境变量告知 egress-router 镜像要使用的地址;它将配置 Macvlan 接口,以将 EGRESS_SOURCE 用作
其 IP 地址,并将 EGRESS_GATEWAY
作为其网关。
设置 NAT 规则,以便将到 Pod 集群 IP 地址上的任何 TCP 或 UDP 端口的连接重定向到 EGRESS_DESTINATION
上的相同端口。
如果集群中只有部分节点能够声明指定的源 IP 地址并使用指定的网关,您可以指定一个 nodeName
或 nodeSelector
来表示哪些节点可以接受。
8.6.1.2. 重定向至多个目标
在上例中,任何端口上的出口 pod(或其对应服务)的连接都会重定向到单个目标 IP。您还可以根据端口配置不同的目标 IP:
apiVersion: v1 kind: Pod metadata: name: egress-multi labels: name: egress-multi annotations: pod.network.openshift.io/assign-macvlan: "true" spec: initContainers: - name: egress-router image: registry.redhat.io/openshift3/ose-egress-router securityContext: privileged: true env: - name: EGRESS_SOURCE 1 value: 192.168.12.99/24 - name: EGRESS_GATEWAY value: 192.168.12.1 - name: EGRESS_DESTINATION 2 value: | 80 tcp 203.0.113.25 8080 tcp 203.0.113.26 80 8443 tcp 203.0.113.26 443 203.0.113.27 - name: EGRESS_ROUTER_MODE value: init containers: - name: egress-router-wait image: registry.redhat.io/openshift3/ose-pod
EGRESS_DESTINATION
每行可以是三种类型之一:
-
<port> <protocol> <IP_address>
- 这表示到给定<port>
的传入连接应该被重新定向到给定<IP_address>
上的同一端口。<protocol>
是tcp
或udp
。在上例中,第一行将流量从本地端口 80 重定向到 203.0.113.25 上的端口 80。 -
<port> <protocol> <IP_address> <remote_port>
- 如上所示,除了连接被重新定向到<IP_address>
上的一个不同的<remote_port>
中。在上例中,第二行和第三行将本地端口 8080 和 8443 重定向到 203.0.113.26 上的远程端口 80 和 443。 -
<fallback_IP_address>
- 如果EGRESS_DESTINATION
的最后一行是一个 IP 地址,那么任何其他端口上的任何连接都将重定向到该 IP 地址上的对应端口(例如,上例中的 203.0.113.27)。如果没有回退 IP 地址,则其他端口上的连接将直接被拒绝。)
8.6.1.3. 使用 ConfigMap 指定 EGRESS_DESTINATION
对于大量或经常更改的目标映射集合,您可以使用 ConfigMap 外部维护列表,并让出口路由器 pod 从那里读取它。这将具有项目管理员能够编辑 ConfigMap 的优点,而他们可能无法直接编辑 Pod 定义,因为它包含特权容器。
创建一个包含
EGRESS_DESTINATION
数据的文件:$ cat my-egress-destination.txt # Egress routes for Project "Test", version 3 80 tcp 203.0.113.25 8080 tcp 203.0.113.26 80 8443 tcp 203.0.113.26 443 # Fallback 203.0.113.27
请注意,您可以将空行和注释放在此文件中
从文件创建 ConfigMap 对象:
$ oc delete configmap egress-routes --ignore-not-found $ oc create configmap egress-routes \ --from-file=destination=my-egress-destination.txt
此处
egress-routes
是所创建的 ConfigMap 对象的名称,my-egress-destination.txt
是正在从中读取数据的文件名。如上所示,创建一个出口路由器 pod 定义,但在 environment 部分为
EGRESS_DESTINATION
指定 ConfigMap:... env: - name: EGRESS_SOURCE 1 value: 192.168.12.99/24 - name: EGRESS_GATEWAY value: 192.168.12.1 - name: EGRESS_DESTINATION valueFrom: configMapKeyRef: name: egress-routes key: destination - name: EGRESS_ROUTER_MODE value: init ...
- 1
- 节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。(可选)您可以包含子网长度和
/24
后缀,以便能够设置指向本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用EGRESS_GATEWAY
变量指定的主机,且子网上没有其他主机。
当 ConfigMap 更改时,出口路由器不会自动更新。重新启动容器集以获取更新。
8.6.1.4. 部署 Egress Router HTTP 代理 Pod
在 HTTP 代理模式中,出口路由器作为 HTTP 代理在端口 8080
上运行。这只适用于与基于 HTTP 或基于 HTTPS 的服务进行通信的客户端,但通常需要较少的更改才能使客户端容器集正常工作。通过设置环境变量,可以告知程序使用 HTTP 代理。
使用以下示例创建 pod:
apiVersion: v1 kind: Pod metadata: name: egress-http-proxy labels: name: egress-http-proxy annotations: pod.network.openshift.io/assign-macvlan: "true" 1 spec: initContainers: - name: egress-router-setup image: registry.redhat.io/openshift3/ose-egress-router securityContext: privileged: true env: - name: EGRESS_SOURCE 2 value: 192.168.12.99/24 - name: EGRESS_GATEWAY 3 value: 192.168.12.1 - name: EGRESS_ROUTER_MODE 4 value: http-proxy containers: - name: egress-router-proxy image: registry.redhat.io/openshift3/ose-egress-http-proxy env: - name: EGRESS_HTTP_PROXY_DESTINATION 5 value: | !*.example.com !192.168.1.0/24 *
- 1
- 在主网络接口上创建一个 Macvlan 网络接口,然后将它移到 pod 的网络项目中,然后再启动 egress-router 容器。保留
"true"
上的引号。省略它们会导致错误。 - 2
- 节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。(可选)您可以包含子网长度和
/24
后缀,以便能够设置指向本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用EGRESS_GATEWAY
变量指定的主机,且子网上没有其他主机。 - 3
- 值与节点本身使用的默认网关相同。
- 4
- 这会告知出口路由器镜像,它被部署为 HTTP 代理的一部分,因此不应设置 iptables 重定向规则。
- 5
- 一个字符串或 YAML 多行字符串指定如何配置代理。请注意,这作为 HTTP 代理容器中的环境变量指定,而不是与 init 容器中的其他环境变量指定。
您可以为
EGRESS_HTTP_PROXY_DESTINATION
值指定以下任意一项:您还可以使用*
,这表示"允许连接到所有远程目的地"。配置中的每行都指定允许或者拒绝的连接组:-
IP 地址(如
192.168.1.1
)允许连接到该 IP 地址。 -
CIDR 范围(如
192.168.1.0/24
)允许连接到那个 CIDR 范围。 -
主机名(如
www.example.com
)允许代理到该主机。 -
前面带有
*.
(例如,*.example.com
)的域名允许代理到那个域及其所有子域。 -
一个
!
后跟上述任意一个项会拒绝连接,而不是允许连接 -
如果最后一行是
*
,则允许任何未被拒绝的任何内容。否则,未被允许的任何内容都将被拒绝。
通过创建服务指向出口路由器,确保其他 pod 可以找到 pod 的 IP 地址:
apiVersion: v1 kind: Service metadata: name: egress-1 spec: ports: - name: http-proxy port: 8080 1 type: ClusterIP selector: name: egress-1
- 1
- 确保
http
端口始终设置为8080
。
通过设置
http_proxy
或https_proxy
变量,将客户端 pod(而不是出口代理 pod)配置为使用 HTTP 代理:... env: - name: http_proxy value: http://egress-1:8080/ 1 - name: https_proxy value: http://egress-1:8080/ ...
- 1
- 第 2 步中创建的服务。
注意不需要在所有设置中使用
http
环境变量。如果以上内容没有创建可以正常工作设置,请查阅 pod 中运行的工具或软件的文档。_proxy
和 https_proxy
您还可以使用 ConfigMap 指定 EGRESS_HTTP_PROXY_DESTINATION
,类似于 上面的重定向出口路由器示例。
8.6.1.5. 部署 Egress Router DNS 代理 Pod
在 DNS 代理模式中,出口路由器作为基于 TCP 服务的 DNS 代理运行,从其自身 IP 地址到一个或多个目标 IP 地址。希望使用保留的客户端 pod,必须修改源 IP 地址以连接到出口路由器,而不是直接连接到目标 IP。这样可确保外部目的地将流量视为来自已知来源的流量。
使用以下示例创建 pod:
apiVersion: v1 kind: Pod metadata: name: egress-dns-proxy labels: name: egress-dns-proxy annotations: pod.network.openshift.io/assign-macvlan: "true" 1 spec: initContainers: - name: egress-router-setup image: registry.redhat.io/openshift3/ose-egress-router securityContext: privileged: true env: - name: EGRESS_SOURCE 2 value: 192.168.12.99/24 - name: EGRESS_GATEWAY 3 value: 192.168.12.1 - name: EGRESS_ROUTER_MODE 4 value: dns-proxy containers: - name: egress-dns-proxy image: registry.redhat.io/openshift3/ose-egress-dns-proxy env: - name: EGRESS_DNS_PROXY_DEBUG 5 value: "1" - name: EGRESS_DNS_PROXY_DESTINATION 6 value: | # Egress routes for Project "Foo", version 5 80 203.0.113.25 100 example.com 8080 203.0.113.26 80 8443 foobar.com 443
- 1
- 使用
pod.network.openshift.io/assign-macvlan 注释
在主网络接口上创建一个 Macvlan 网络接口,然后在启动 egress-router-setup 容器前将它移到 pod 的网络命名空间。保留"true"
上的引号。省略它们会导致错误。 - 2
- 节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。(可选)您可以包含子网长度和
/24
后缀,以便能够设置指向本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用EGRESS_GATEWAY
变量指定的主机,且子网上没有其他主机。 - 3
- 值与节点本身使用的默认网关相同。
- 4
- 这会告知出口路由器镜像,它被部署为 DNS 代理的一部分,因此不应设置 iptables 重定向规则。
- 5
- 可选。设置此变量将在 stdout 上显示 DNS 代理日志输出。
- 6
- 这会将 YAML 语法用于多行字符串。详情请查看以下。
注意EGRESS_DNS_PROXY_DESTINATION
的每一行可以通过以下两种方式之一设置:-
<port> <remote_address>
- 这表示到给定<port>
的传入连接应该代理到给定<remote_address>
上的相同 TCP 端口,<remote_address>
可以是 IP 地址或 DNS 名称。如果是 DNS 名称,DNS 解析会在运行时进行。在上例中,第一行将 TCP 流量代理从本地端口 80 到 203.0.113.25 上的端口 80。第二行将 TCP 流量代理从本地端口 100 至 example.com 上的端口 100。 -
<port> <remote_address> <remote_port>
- 如上所示,除了连接代理到<remote_address>
上的不同<remote_port>
。在上例中,第三行代理本地端口 8080 到 203.0.113.26 上的远程端口 80,第四行将本地端口 8443 代理到 foobar.com 上的远程端口 443。
通过创建服务指向出口路由器,确保其他 pod 可以找到 pod 的 IP 地址:
apiVersion: v1 kind: Service metadata: name: egress-dns-svc spec: ports: - name: con1 protocol: TCP port: 80 targetPort: 80 - name: con2 protocol: TCP port: 100 targetPort: 100 - name: con3 protocol: TCP port: 8080 targetPort: 8080 - name: con4 protocol: TCP port: 8443 targetPort: 8443 type: ClusterIP selector: name: egress-dns-proxy
Pod 现在可以连接至此服务。使用保留的出口 IP 地址将其连接代理到外部服务器的对应端口。
您还可以使用 ConfigMap 指定 EGRESS_DNS_PROXY_DESTINATION
,类似于 上面的重定向出口路由器示例。
8.6.1.6. 为 Egress Router Pod 启用故障切换
通过使用复制控制器,您可以确保始终有一个出口路由器 pod 副本以防止停机。
使用以下方法创建复制控制器配置文件:
apiVersion: v1 kind: ReplicationController metadata: name: egress-demo-controller spec: replicas: 1 1 selector: name: egress-demo template: metadata: name: egress-demo labels: name: egress-demo annotations: pod.network.openshift.io/assign-macvlan: "true" spec: initContainers: - name: egress-demo-init image: registry.redhat.io/openshift3/ose-egress-router env: - name: EGRESS_SOURCE 2 value: 192.168.12.99/24 - name: EGRESS_GATEWAY value: 192.168.12.1 - name: EGRESS_DESTINATION value: 203.0.113.25 - name: EGRESS_ROUTER_MODE value: init securityContext: privileged: true containers: - name: egress-demo-wait image: registry.redhat.io/openshift3/ose-pod nodeSelector: site: springfield-1
使用定义创建 pod:
$ oc create -f <replication_controller>.json
要验证,请检查是否创建了复制控制器 pod:
$ oc describe rc <replication_controller>