Wildcard 主机的 Egress

控制出口流量任务和 配置一个 Egress 网关示例描述如何配置特定主机的出口流量, 如:edition.cnn.com。本示例描述如何为通用域中的一组特定主机开启出口流量, 譬如 *.wikipedia.org,无需单独配置每一台主机。

背景

假定您想要为 Istio 中所有语种的 wikipedia.org 站点开启出口流量。每个语种的 wikipedia.org 站点均有自己的主机名,譬如:英语和德语对应的主机分别为 en.wikipedia.orgde.rikipedia.org。您希望通过通用配置项开启所有 Wikipedia 站点的出口流量,无需单独配置每个语种的站点。

Istio 支持 Kubernetes Gateway API, 并计划将其作为未来流量管理的默认 API。 以下说明指导您在网格中配置流量管理时如何选择使用 Gateway API 或 Istio 配置 API。 请按照您的首选项遵循 Gateway APIIstio APIs 页签中的指示说明。

请注意,Kubernetes Gateway API CRD 不会默认安装在大多数 Kubernetes 集群上, 因此请确保在使用 Gateway API 之前已安装好这些 CRD:

  1. $ kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
  2. { kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml; }

开始之前

  • 安装 Istio,启用访问日志记录,并采用默认阻止出站流量策略。
  1. $ istioctl install --set profile=demo --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY

您可以在 demo 配置文件以外的 Istio 配置上运行此任务, 只要您确保部署 Istio Egress 网关开启 Envoy 的访问日志应用默认阻止出站流量策略

  1. $ istioctl install --set profile=minimal -y \
  2. --set values.pilot.env.PILOT_ENABLE_ALPHA_GATEWAY_API=true \
  3. --set meshConfig.accessLogFile=/dev/stdout \
  4. --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY
  • 部署 curl 示例应用程序,以用作发送请求的测试源。 如果您开启了 Sidecar 自动注入, 运行以下命令以部署示例应用程序:

    Zip

    1. $ kubectl apply -f @samples/curl/curl.yaml@

    否则,在使用以下命令部署 curl 应用程序之前,手动注入 Sidecar:

    Zip

    1. $ kubectl apply -f <(istioctl kube-inject -f @samples/curl/curl.yaml@)

    您可以在任意 Pod 上使用 curl 作为测试源。

  • SOURCE_POD 环境变量设置为您的源 Pod 的名称:

    1. $ export SOURCE_POD=$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})

引导流量流向 Wildcard 主机

访问通用域中一组主机的第一个也是最简单的方法,是使用一个 wildcard 主机配置一个简单的 ServiceEntry, 直接从 Sidecar 调用服务。当直接调用服务时(譬如:不是通过一个 Egress 网关),一个 wildcard 主机的配置与任何其他主机(如:全域名主机)没有什么不同,只是当通用域中有许多台主机时,这样比较方便。

请注意,恶意应用程序很容易绕过以下配置。为了实现安全的出口流量控制,可以通过出口网关引导流量。

请注意,DNS 解析不能用于通配符主机。这就是为什么 NONE 分辨率(因为它是默认)用于以下服务条目。

  1. *.wikipedia.org 定义一个 ServiceEntry

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1
    3. kind: ServiceEntry
    4. metadata:
    5. name: wikipedia
    6. spec:
    7. hosts:
    8. - "*.wikipedia.org"
    9. ports:
    10. - number: 443
    11. name: https
    12. protocol: HTTPS
    13. EOF
  2. 发送 HTTPS 请求至 https://en.wikipedia.org and https://de.wikipedia.org:

    1. $ kubectl exec -it $SOURCE_POD -c curl -- sh -c 'curl -s https://en.wikipedia.org/wiki/Main_Page | grep -o "<title>.*</title>"; curl -s https://de.wikipedia.org/wiki/Wikipedia:Hauptseite | grep -o "<title>.*</title>"'
    2. <title>Wikipedia, the free encyclopedia</title>
    3. <title>Wikipedia Die freie Enzyklopädie</title>

清理将流量引导至 Wildcard 主机的规则

  1. $ kubectl delete serviceentry wikipedia

配置到 Wildcard 主机的 Egress 网关流量规则

当一台唯一的服务器为所有 wildcard 主机提供服务时,基于 Egress 网关访问 wildcard 主机的配置与普通主机类似, 除了:配置的路由目标不能与配置的主机相同,如:wildcard 主机,需要配置为通用域集合的唯一服务器主机。

  1. *.wikipedia.org 创建一个 Egress Gateway,并创建路由规则引导经过 Egress 网关的流量以及从 Egress 网关到外部服务的流量。
  1. $ kubectl apply -f - <<EOF
  2. apiVersion: networking.istio.io/v1
  3. kind: Gateway
  4. metadata:
  5. name: istio-egressgateway
  6. spec:
  7. selector:
  8. istio: egressgateway
  9. servers:
  10. - port:
  11. number: 443
  12. name: https
  13. protocol: HTTPS
  14. hosts:
  15. - "*.wikipedia.org"
  16. tls:
  17. mode: PASSTHROUGH
  18. ---
  19. apiVersion: networking.istio.io/v1
  20. kind: DestinationRule
  21. metadata:
  22. name: egressgateway-for-wikipedia
  23. spec:
  24. host: istio-egressgateway.istio-system.svc.cluster.local
  25. subsets:
  26. - name: wikipedia
  27. ---
  28. apiVersion: networking.istio.io/v1
  29. kind: VirtualService
  30. metadata:
  31. name: direct-wikipedia-through-egress-gateway
  32. spec:
  33. hosts:
  34. - "*.wikipedia.org"
  35. gateways:
  36. - mesh
  37. - istio-egressgateway
  38. tls:
  39. - match:
  40. - gateways:
  41. - mesh
  42. port: 443
  43. sniHosts:
  44. - "*.wikipedia.org"
  45. route:
  46. - destination:
  47. host: istio-egressgateway.istio-system.svc.cluster.local
  48. subset: wikipedia
  49. port:
  50. number: 443
  51. weight: 100
  52. - match:
  53. - gateways:
  54. - istio-egressgateway
  55. port: 443
  56. sniHosts:
  57. - "*.wikipedia.org"
  58. route:
  59. - destination:
  60. host: www.wikipedia.org
  61. port:
  62. number: 443
  63. weight: 100
  64. EOF
  1. $ kubectl apply -f - <<EOF
  2. apiVersion: gateway.networking.k8s.io/v1
  3. kind: Gateway
  4. metadata:
  5. name: wikipedia-egress-gateway
  6. annotations:
  7. networking.istio.io/service-type: ClusterIP
  8. spec:
  9. gatewayClassName: istio
  10. listeners:
  11. - name: tls
  12. hostname: "*.wikipedia.org"
  13. port: 443
  14. protocol: TLS
  15. tls:
  16. mode: Passthrough
  17. allowedRoutes:
  18. namespaces:
  19. from: Same
  20. ---
  21. apiVersion: gateway.networking.k8s.io/v1alpha2
  22. kind: TLSRoute
  23. metadata:
  24. name: direct-wikipedia-to-egress-gateway
  25. spec:
  26. parentRefs:
  27. - kind: ServiceEntry
  28. group: networking.istio.io
  29. name: wikipedia
  30. rules:
  31. - backendRefs:
  32. - name: wikipedia-egress-gateway-istio
  33. port: 443
  34. ---
  35. apiVersion: gateway.networking.k8s.io/v1alpha2
  36. kind: TLSRoute
  37. metadata:
  38. name: forward-wikipedia-from-egress-gateway
  39. spec:
  40. parentRefs:
  41. - name: wikipedia-egress-gateway
  42. hostnames:
  43. - "*.wikipedia.org"
  44. rules:
  45. - backendRefs:
  46. - kind: Hostname
  47. group: networking.istio.io
  48. name: www.wikipedia.org
  49. port: 443
  50. ---
  51. apiVersion: networking.istio.io/v1
  52. kind: ServiceEntry
  53. metadata:
  54. name: wikipedia
  55. spec:
  56. hosts:
  57. - "*.wikipedia.org"
  58. ports:
  59. - number: 443
  60. name: https
  61. protocol: HTTPS
  62. EOF
  1. 为目标服务器 www.wikipedia.org 创建一个 ServiceEntry

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1
    3. kind: ServiceEntry
    4. metadata:
    5. name: www-wikipedia
    6. spec:
    7. hosts:
    8. - www.wikipedia.org
    9. ports:
    10. - number: 443
    11. name: https
    12. protocol: HTTPS
    13. resolution: DNS
    14. EOF
  2. 发送 HTTPS 请求至 https://en.wikipedia.orghttps://de.wikipedia.org

    1. $ kubectl exec "$SOURCE_POD" -c curl -- sh -c 'curl -s https://en.wikipedia.org/wiki/Main_Page | grep -o "<title>.*</title>"; curl -s https://de.wikipedia.org/wiki/Wikipedia:Hauptseite | grep -o "<title>.*</title>"'
    2. <title>Wikipedia, the free encyclopedia</title>
    3. <title>Wikipedia Die freie Enzyklopädie</title>
  3. 检查 Egress 网关代理访问 *.wikipedia.org 的计数器统计值。

  1. $ kubectl exec "$(kubectl get pod -l istio=egressgateway -n istio-system -o jsonpath='{.items[0].metadata.name}')" -c istio-proxy -n istio-system -- pilot-agent request GET clusters | grep '^outbound|443||www.wikipedia.org.*cx_total:'
  2. outbound|443||www.wikipedia.org::208.80.154.224:443::cx_total::2
  1. $ kubectl exec "$(kubectl get pod -l gateway.networking.k8s.io/gateway-name=wikipedia-egress-gateway -o jsonpath='{.items[0].metadata.name}')" -c istio-proxy -- pilot-agent request GET clusters | grep '^outbound|443||www.wikipedia.org.*cx_total:'
  2. outbound|443||www.wikipedia.org::208.80.154.224:443::cx_total::2

清理到 Wildcard 主机的 Egress 网关流量

  1. $ kubectl delete serviceentry www-wikipedia
  2. $ kubectl delete gateway istio-egressgateway
  3. $ kubectl delete virtualservice direct-wikipedia-through-egress-gateway
  4. $ kubectl delete destinationrule egressgateway-for-wikipedia
  1. $ kubectl delete se wikipedia
  2. $ kubectl delete se www-wikipedia
  3. $ kubectl delete gtw wikipedia-egress-gateway
  4. $ kubectl delete tlsroute direct-wikipedia-to-egress-gateway
  5. $ kubectl delete tlsroute forward-wikipedia-from-egress-gateway

任意域的 Wildcard 配置

上一节中的配置之所以有效,是因为所有 *.wikipedia.org 站点都可能由任何一个 wikipedia.org 服务器提供服务。 然而,实际情况并非总是如此。例如,您可能想要配置出口控制以访问更通用的 Wildcard 域, 例如 *.com*.org。为任意 Wildcard 域名配置流量给 Istio 网关带来了挑战; Istio 网关只能将流量路由配置到预定义的主机、预定义的 IP 地址或请求的原始目标 IP 地址。

在上一节中,您配置了虚拟服务用于将流量定向预定义主机 www.wikipedia.org。 然而,在一般情况下,您不知道可以为请求中收到的任意主机提供服务的主机或 IP 地址, 这使得请求的原始目标地址成为路由请求的唯一值。不幸的是,当使用出口网关时, 由于原始请求被重定向到网关,因此请求的原始目标地址丢失,导致目标 IP 地址变成网关的 IP 地址。

尽管这样做并不简单且有些脆弱,因为它依赖于 Istio 实现细节,但您可以使用 Envoy 过滤器通过使用 SNI 配置网关以支持任意域 HTTPS 或任何 TLS 请求中的值,用于标识将请求路由到的原始目的地。 这种配置方法的一个示例可以在将出口流量路由至通配符目的地中找到。

清理

  • 关闭 curl 服务:

    Zip

    1. $ kubectl delete -f @samples/curl/curl.yaml@
  • 从您的集群中卸载 Istio:

    1. $ istioctl uninstall --purge -y