# HTTP Egress 流量的监控和访问策略管理

原文：[Monitoring and Access Policies for HTTP Egress Traffic](https://istio.io/blog/2018/egress-monitoring-access-control/)

作者：VADIM EISENBERG 和 RONEN SCHAFFER

Istio 的主要功能就是在服务网格内部管理微服务之间的通信，除此之外，Istio 还能对 Ingress（从外部进入网格） 和 Egress（从网格发出到外部） 流量进行管理。不管是网格内部流量，还是 Ingress 或者 Egress 流量，Istio 都能够在其中进行访问策略的控制，并完成遥测数据的聚合工作。

本文中我们会展示如何使用 Istio 在 HTTP Egress 流量中实施监控和访问策略控制。文中谈到的内容针对 [Istio 0.8.0](https://github.com/istio/istio/releases/tag/0.8.0) 及以上是有效的。

# 用例

假设一个组织正在运行的应用需要处理来自于 `cnn.com` 的内容。这些应用已经被解构为部署在 Istio 服务网格中的微服务。这些应用要获取 `cnn.com` 多个频道的内容：[edition.cnn.com/politics](https://edition.cnn.com/politics)、[edition.cnn.com/sport](https://edition.cnn.com/sport) 以及 [edition.cnn.com/health](https://edition.cnn.com/health)。目前已经[配置 Istio 使其允许访问 `edition.cnn.com`](https://istio.io/docs/tasks/traffic-management/egress-tls-origination/)，一切运行良好。然而在某一天，他们决定限制政治方面的内容，技术上讲，就是要阻止对 [edition.cnn.com/politics](https://edition.cnn.com/politics) 的访问，继续允许对 [edition.cnn.com/sport](https://edition.cnn.com/sport) 以及 [edition.cnn.com/health](https://edition.cnn.com/health) 的访问。对 [edition.cnn.com/politics](https://edition.cnn.com/politics) 的访问需要在应用程序、命名空间以及用户的不同粒度上进行访问控制。

要实现这个目标，运维人员需要监控对外部服务的访问，并分析 Istio 日志来确保对 [edition.cnn.com/politics](https://edition.cnn.com/politics) 的访问都是经过授权的。另外他们还要配置 Istio，让 Istio 自动阻止对 [edition.cnn.com/politics](https://edition.cnn.com/politics) 的（未授权）访问。

该组织决定防止对新策略发生篡改，通过技术手段执行策略，防止恶意应用访问受限内容。

## 相关任务

* [Egress 流量控制](https://istio.io/docs/tasks/traffic-management/egress/)：网格内应用对（Kubernetes 集群）外部的 HTTP 和 HTTPS 服务的访问方式。
    
* [TLS 方式访问 Egress 流量](https://istio.io/docs/tasks/traffic-management/egress-tls-origination/)：内部应用使用 HTTP 协议访问需要 HTTPS 的外部服务的能力。
    
* [配置 Egress Gateway](https://istio.io/docs/tasks/traffic-management/egress-gateway/) ：如何配置 Istio 令其使用独立的 `egress gateway` 网关服务来发送 Egress 流量。
    
* [收集指标和日志](https://istio.io/docs/tasks/telemetry/metrics-logs/)：为网格中的服务配置指标和日志。
    
* [使用 Grafana 进行指标可视化](https://istio.io/docs/tasks/telemetry/using-istio-dashboard/)：Istio Dashboard 在网格流量监控方面的作用。
    
* [基础访问控制](https://istio.io/docs/tasks/security/basic-access-control/)：网格内服务的访问控制问题。
    
* [安全访问控制](https://istio.io/docs/tasks/security/secure-access-control/)：如何使用黑名单和白名单配置访问策略。
    

和上面列出的任务不同，本文讲述的是 Istio 对 Egress 流量的监控和访问策略。

## 开始之前

依照 [配置 Egress Gateway，使用 Egress Gateway 执行 TLS 访问](https://istio.io/docs/tasks/traffic-management/egress-gateway/#perform-tls-origination-with-the-egress-gateway) 任务中的步骤，不要执行[清理](https://istio.io/docs/tasks/traffic-management/egress-gateway/#cleanup)操作。完成之后，就可在网格之内使用安装了 `curl` 的容器来访问 [edition.cnn.com/politics](https://edition.cnn.com/politics) 了。下面的内容中，我们假设名为 `SOURCE_POD` 的环境变量中包含了 Pod 名称。

## 配置监控和访问策略

既然要用安全方式来完成任务，就需要通过 `egress gateway` 来进行 egress 传输，这部分内容在 [配置 Egress Gateway](https://istio.io/docs/tasks/traffic-management/egress-gateway/) 中有详细描述。这里所谓的安全方式指的是防止恶意应用绕过 Istio 监控和策略管理进行未经授权的访问。

我们的场景中，该组织执行了上一节“开始之前”的步骤。这个步骤完成后，开放了对 `edition.cnn.com` 的访问，并且配置了响应的 Egress Gateway。现在可以对 `edition.cnn.com` 进行监控和策略进行配置了。

### 日志

首先配置一下对 `*.cnn.com` 的记录。创建一个 `logentry` 和两个 [stdio](https://istio.io/docs/reference/config/policy-and-telemetry/adapters/stdio/) 类型的 `handler`，其中一个用 `error` 级别的日志来记录受限的访问，另外一个用 `info` 级别来记录所有到 `*.cnn.com` 的访问。接下来创建 `rules` 把 `logentry` 定向到 `handler` 上。对 `*.cnn.com/politics` 的访问会被指派给受限访问的规则，剩余的访问则会被另一条规则接收。要理解 Istio 的 `logentries`、`rules` 以及 `handlers`，可以阅读 [Istio Adaper Model](https://istio.io/blog/2017/adapter-model/) 一文。下面的示意图中包含了刚才讲到的这些实体和依赖关系：

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745893119417/b83e9203-f56a-46c6-9192-464874dee6c8.png align="center")

* 创建 `logentries`、`rules` 以及 `handlers`：
    

```yaml
# egress 访问的日志条目
apiVersion: "config.istio.io/v1alpha2"
kind: logentry
metadata:
  name: egress-access
  namespace: istio-system
spec:
  severity: '"info"'
  timestamp: request.time
  variables:
    destination: request.host | "unknown"
    path: request.path | "unknown"
    source: source.labels["app"] | source.service | "unknown"
    sourceNamespace: source.namespace | "unknown"
    user: source.user | "unknown"
    responseCode: response.code | 0
    responseSize: response.size | 0
  monitored_resource_type: '"UNSPECIFIED"'
---
# Handler for error egress access entries
apiVersion: "config.istio.io/v1alpha2"
kind: stdio
metadata:
  name: egress-error-logger
  namespace: istio-system
spec:
  severity_levels:
    info: 2 # 输出级别为 error
  outputAsJson: true
---
# 访问 *.cnn.com/politics 的规则
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
  name: handle-politics
  namespace: istio-system
spec:
  match: request.host.endsWith("cnn.com") && request.path.startsWith("/politics")
  actions:
  - handler: egress-error-logger.stdio
    instances:
    - egress-access.logentry
---
# Info 级别的 Egress 日志
apiVersion: "config.istio.io/v1alpha2"
kind: stdio
metadata:
  name: egress-access-logger
  namespace: istio-system
spec:
  severity_levels:
    info: 0 # 输出为 Info 级别
  outputAsJson: true
---
# 访问 *.cnn.com 的规则
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
  name: handle-cnn-access
  namespace: istio-system
spec:
  match: request.host.endsWith(".cnn.com")
  actions:
  - handler: egress-access-logger.stdio
    instances:
    - egress-access.logentry
```

* 分别向 [edition.cnn.com/politics](https://edition.cnn.com/politics)、[edition.cnn.com/sport](https://edition.cnn.com/sport) 发送请求，所有请求都应该返回 `200 OK`。
    

```plaintext
$ kubectl exec -it $SOURCE_POD -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
200
200
200
```

* 查询 Mixer 的日志，查看日志中出现的请求信息：
    

```plaintext
kubectl -n istio-system logs $(kubectl -n istio-system get pods -l istio-mixer-type=telemetry -o jsonpath='{.items[0].metadata.name}') mixer | grep egress-access | grep cnn | tail -4
```

返回如下信息：

```json
{"level":"info","time":"2018-06-18T13:22:58.317448Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/politics","responseCode":200,"responseSize":150448,"source":"sleep","user":"unknown"}
{"level":"error","time":"2018-06-18T13:22:58.317448Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/politics","responseCode":200,"responseSize":150448,"source":"sleep","user":"unknown"}
{"level":"error","time":"2018-06-18T13:22:58.317448Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/politics","responseCode":200,"responseSize":150448,"source":"sleep","user":"unknown"}
{"level":"info","time":"2018-06-18T13:22:59.354943Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/health","responseCode":200,"responseSize":332218,"source":"sleep","user":"unknown"}
```

会看到关于三个请求的四条日志。三个 `info` 级别的条目是关于 `edition.cnn.com` 的，一条 `error` 级别的条目就是关于 `edition.cnn.com/politics` 的访问。服务网格的运维人员能看到所有的访问情况，并且也能通过对 `error` 日志的搜索来查找受限访问。这是在禁止访问之前的第一个监控措施，把所有受限访问都作为错误记录下来，在某些情况下，这就足够安全了。

### 利用路由进行访问控制

在启动对 `edition.cnn.com` 的访问日志之后，自动启动了一个访问策略，只允许访问 `/health` 和 `/sport` URL。这样简单的策略控制可以用 Istio 路由来实现。

* 重新定义 `edition.cnn.com` 的 `VirtualService`：
    

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: direct-through-egress-gateway
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - istio-egressgateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        port:
          number: 443
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway
      port: 443
      uri:
        regex: "^.health|^.sport"
    route:
    - destination:
        host: edition.cnn.com
        port:
          number: 443
      weight: 100
```

注意这里加入了一个针对 `uri` 的 `match` 条件，会检查 URL 路径是不是 `/health` 或者 `/sport`。另外还要注意的是，这个条件是加入到 `VirtualService` 的 `istio-egressgateway` 部分，egress gateway 是一个需要注意安全的组件（参见 [Egress Gateway 的安全考量](https://istio.io/docs/tasks/traffic-management/egress-gateway/#additional-security-considerations)），应该慎重对待其安全性，防止影响后续的策略实施过程。

* 再次发送之前的三个 HTTP 请求：
    

```plaintext
$ kubectl exec -it $SOURCE_POD -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
404
200
200
```

会看到 [edition.cnn.com/politics](https://edition.cnn.com/politics) 返回了 `404 Not Found`，[edition.cnn.com/sport](https://edition.cnn.com/sport) 和 [edition.cnn.com/health](https://edition.cnn.com/health) 都返回了 200。

> `VirtualService` 的传播和生效可能需要几秒钟的等待。

* 查询 Mixer 日志，看看刚才发生的请求在日志中的体现：
    

```plaintext
kubectl -n istio-system logs $(kubectl -n istio-system get pods -l istio-mixer-type=telemetry -o jsonpath='{.items[0].metadata.name}') mixer | grep egress-access | grep cnn | tail -4
```

得到结果如下：

```json
{"level":"info","time":"2018-06-19T12:39:48.050666Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/politics","responseCode":404,"responseSize":0,"source":"sleep","sourceNamespace":"default","user":"unknown"}
{"level":"error","time":"2018-06-19T12:39:48.050666Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/politics","responseCode":404,"responseSize":0,"source":"sleep","sourceNamespace":"default","user":"unknown"}
{"level":"info","time":"2018-06-19T12:39:48.091268Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/health","responseCode":200,"responseSize":334027,"source":"sleep","sourceNamespace":"default","user":"unknown"}
{"level":"info","time":"2018-06-19T12:39:48.063812Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/sport","responseCode":200,"responseSize":355267,"source":"sleep","sourceNamespace":"default","user":"unknown"}
```

这里还能看到 [edition.cnn.com/politics](https://edition.cnn.com/politics) 的访问日志，只不过这次的 `responseCode` 是 `404`。

使用 Istio 路由之后，我们成功的实现了初步的访问控制，但是如果是更复杂的需要，这种程度还是不够的。例如希望允许特定条件下对 [edition.cnn.com/politics](https://edition.cnn.com/politics) 的访问，这需要一些更复杂的策略，只判断 URL 是不够的。这就需要 [Istio Mixer Adapter](https://istio.io/blog/2017/adapter-model/)，（例如[白名单](https://istio.io/docs/tasks/security/basic-access-control/#access-control-using-whitelists)和[黑名单](https://istio.io/docs/tasks/security/basic-access-control/#access-control-using-denials)）的协助，来协助控制对 URL 路径的允许和禁止行为。借 \[Policy Rules\] 的帮助，这样就可以使用 [Istio expression language](https://istio.io/docs/reference/config/policy-and-telemetry/expression-language/) 来实现复杂条件的定义，完成包含逻辑控制在内的复杂限制了。这些规则可以在日志和策略检查之间进行复用。另外还可以使用 [Istio RBAC](https://istio.io/docs/concepts/security/rbac/)进行更复杂的控制。

还有一个额外的需要就是和远程访问策略系统进行集成。如果用例中设计的组织已经有使用一些 [认证和访问管理系统](https://en.wikipedia.org/wiki/Identity_management)，可能会需要配置 Istio 从这些系统中获取访问策略方面的信息。可以通过实现 [Istio Mixer Adapter](https://istio.io/blog/2017/adapter-model/) 的方式来进行集成。

取消掉前面我们使用路由规则实现的访问控制，接下来使用 Mixer 策略来实现。

* 用之前的 [Configure an Egress Gateway](https://istio.io/docs/tasks/traffic-management/egress-gateway/#perform-tls-origination-with-the-egress-gateway) 中的版本替换 `edition.cnn.com` 的 `VertualService`：
    

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: direct-through-egress-gateway
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - istio-egressgateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        port:
          number: 443
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway
      port: 443
    route:
    - destination:
        host: edition.cnn.com
        port:
          number: 443
      weight: 100
```

* 再次发送之前的三个到 `cnn.com` 的请求，这里会看到三个 200 的成功返回：
    

```plaintext
$ kubectl exec -it $SOURCE_POD -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
200
200
200
```

> `VirtualService` 的传播和生效可能需要几秒钟的等待。

### 使用 Mixer 策略进行访问控制

这一步中，我们使用 [Listchecker adapter](https://istio.io/docs/reference/config/policy-and-telemetry/adapters/list/)，这是白名单的一个变体。用一个静态 URL 列表定义一个 `listentry`，然后在 `listchecker` 中用 `overrides` 字段进行检查。如果有外部的 [认证和访问管理系统](https://en.wikipedia.org/wiki/Identity_management)，可以使用 `providerurl` 字段取而代之。下面图示显示了更新之后的 对象关系。注意这里复用了同样的策略，`handle-cnn-access` 对日志和访问策略同样生效

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745893424703/0efa794b-920c-4076-a9ce-99c0cfc11ae2.png align="center")

。

* 定义 `path-checker` 以及 `request-path`：
    

```yaml
apiVersion: "config.istio.io/v1alpha2"
kind: listchecker
metadata:
  name: path-checker
  namespace: istio-system
spec:
  overrides: ["/health", "/sport"]  # 提供一个静态列表
  blacklist: false
---
apiVersion: "config.istio.io/v1alpha2"
kind: listentry
metadata:
  name: request-path
  namespace: istio-system
spec:
  value: request.path
```

* 修改 `handle-cnn-access` 规则，要求将 `request-path` 发送给 `path-checker`：
    

```yaml
# 访问 cnn.com egress 的规则
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
  name: handle-cnn-access
  namespace: istio-system
spec:
  match: request.host.endsWith(".cnn.com")
  actions:
  - handler: egress-access-logger.stdio
    instances:
      - egress-access.logentry
  - handler: path-checker.listchecker
    instances:
      - request-path.listentry
```

* 再次执行 curl 指令，发现 [edition.cnn.com/politics](http://edition.cnn.com/politics) 返回了 404：
    

```plaintext
$ kubectl exec -it $SOURCE_POD -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
404
200
200
```

### 使用 Mixer 策略进行访问控制（续）

在前面我们配置了日志和访问控制之后，新增一个需要就是允许在 `policics` 命名空间内的应用能够访问 `cnn.com` 的所有内容，并且不受监控。接下来我们在 Istio 中进行配置，完成这一要求。

* 创建 `polictics` 命名空间
    

```plaintext
$ kubectl create namespace politics
namespace "politics" created
```

* 在 `polictics` 命名空间中启动 [sleep](https://github.com/istio/istio/tree/release-0.8/samples/sleep)。
    

如果使用了 [自动注入 Sidecar](https://istio.io/docs/setup/kubernetes/sidecar-injection/#automatic-sidecar-injection)，执行：`$ kubectl apply -n politics -f samples/sleep/sleep.yaml`，否则，就需要进行注入了：`kubectl apply -n politics -f <(istioctl kube-inject -f samples/sleep/sleep.yaml)`。

* 预备使用 `policics` 命名空间中的 `sleep` pod 来发送请求，这里定义一个环境变量来保存 Pod 名称。
    

```plaintext
export SOURCE_POD_IN_POLITICS=$(kubectl get pod -n politics -l app=sleep -o jsonpath={.items..metadata.name})
```

* 这次从新的 Pod（`$SOURCE_POD_IN_POLITICS`）中发送刚才的请求。因为我们还没有给新的命名空间中的应用设置例外，[edition.cnn.com/politics](http://edition.cnn.com/politics) 还是返回了 404。
    

```plaintext
$ kubectl exec -it $SOURCE_POD_IN_POLITICS -n politics -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
404
200
200
```

* 查询 Mixer 日志，会看到来自 `politics` 命名空间的访问记录：
    

```plaintext
kubectl -n istio-system logs $(kubectl -n istio-system get pods -l istio-mixer-type=telemetry -o jsonpath='{.items[0].metadata.name}') mixer | grep egress-access | grep cnn | tail -4
```

查询结果：

```json
{"level":"info","time":"2018-06-19T17:37:14.639102Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/politics","responseCode":404,"responseSize":76,"source":"sleep","sourceNamespace":"politics","user":"unknown"}
{"level":"error","time":"2018-06-19T17:37:14.639102Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/politics","responseCode":404,"responseSize":76,"source":"sleep","sourceNamespace":"politics","user":"unknown"}
{"level":"info","time":"2018-06-19T17:37:14.653225Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/sport","responseCode":200,"responseSize":356349,"source":"sleep","sourceNamespace":"politics","user":"unknown"}
{"level":"info","time":"2018-06-19T17:37:14.767923Z","instance":"egress-access.logentry.istio-system","destination":"edition.cnn.com","path":"/health","responseCode":200,"responseSize":334027,"source":"sleep","sourceNamespace":"politics","user":"unknown"}
```

上面的输出中可以看到 `sourceNamespace` 的值为 `politics`。

* 重新定义 `handle-cnn-access` 以及 `handle-politics` 策略，为新的命名空间定义例外的日志和访问策略。
    

```yaml
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
  name: handle-politics
  namespace: istio-system
spec:
  match: request.host.endsWith("cnn.com") && request.path.startsWith("/politics") && source.namespace != "politics"
  actions:
  - handler: egress-error-logger.stdio
    instances:
    - egress-access.logentry
---
# 访问 egress cnn.com 的规则
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
  name: handle-cnn-access
  namespace: istio-system
spec:
  match: request.host.endsWith(".cnn.com") && source.namespace != "politics"
  actions:
  - handler: egress-access-logger.stdio
    instances:
      - egress-access.logentry
  - handler: path-checker.listchecker
    instances:
      - request-path.listentry
```

* 在 `$SOURCE_POD` 中重复执行测试：
    

```plaintext
$ kubectl exec -it $SOURCE_POD -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
404
200
```

`$SOURCE_POD` 是在 `default` 命名空间的，所以对 [edition.cnn.com/politics](http://edition.cnn.com/politics) 的访问会被拒绝。

* 在 `$SOURCE_POD_IN_POLITICS` 中重复执行测试：
    

```plaintext
$ kubectl exec -it $SOURCE_POD_IN_POLITICS -n politics -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
200
200
200
```

现在所有访问都可以通过了。

* 查看 Mixer 日志，会发现看不到 `sourceNamespace` 为 `politics` 的条目了：
    

```plaintext
kubectl -n istio-system logs $(kubectl -n istio-system get pods -l istio-mixer-type=telemetry -o jsonpath='{.items[0].metadata.name}') mixer | grep egress-access | grep cnn
```

## Dashboard

让运维人员能够可视化的进行 egress 流量监控，也能增强安全性。

* 浏览[使用 Grafana 进行指标可视化](https://istio.io/docs/tasks/telemetry/using-istio-dashboard/#viewing-the-istio-dashboard)任务，完成其中的步骤 1-3。
    
* 从 `$SOURCE_POD` 发送到 `cnn.com` 的请求：
    

```plaintext
$ kubectl exec -it $SOURCE_POD -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
404
200
200
```

因为 `$SOURCE_POD` 是存在于 `default` 命名空间中的，所以对 [edition.cnn.com/politics](http://edition.cnn.com/politics) 的访问会被拒绝。

* 从 `$SOURCE_POD_IN_POLITICS` 发送到 `cnn.com` 的请求：
    

```plaintext
$ kubectl exec -it $SOURCE_POD_IN_POLITICS -n politics -c sleep -- bash -c 'curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/politics; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/sport; curl -sL -o /dev/null -w "%{http_code}\n" http://edition.cnn.com/health'
200
200
200
```

* 滚动 Dashboard 到 HTTP 服务部分的 `istio-egressgateway.istio-system.svc.cluster.local` 一节。会看到大致如下的显示：
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745893277237/f59c2a08-d4b4-4e82-8e74-d5f8736f3345.png align="center")

在左侧 `Requests by Source, Version and Response Code` 中，会看到 default 命名空间中的 `unknown` 版本的 sleep 应用收到的 404 返回码。运维人员可以据此发现试图访问受控目标的应用。还可以看到在 `polictics` 命名空间中 `sleep` 应用收到的 200 返回码，这样也就知道了对受控外部资源的有效访问情况。

## 和 HTTPS egress 控制的比较

这个用例中，应用使用的是 HTTP 和 Istio Egress Gateway 结合提供 TLS 的。换个方式，应用可以自行发送 TLS 请求给 `edition.cnn.com`，本节中我们会对两种方式的优劣进行一些比较。

HTTP 方式中，请求在本地是明文传输，经由 Sidecar 转发给 Egress Gateway 的。如果 Istio 使用双向 TLS 部署，Sidecar 代理和 Egress Gateway 之间的通信就是加密的。Egress Gateway 解密信息，查看 URL 路径，HTTP 方法和 Header，上报监控数据、执行前置检查。如果请求没有被拒绝，Egress Gateway 就会为外部目标执行 TLS 封装，这样请求就会被再次加密，以密文形式发送给外部目标。下图演示了这种方式中的网络流向。图中 Gateway 方块中的 HTTP 标志，代表报文在 Gateway 解密之后变成明文的阶段。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745893311818/1554c0a1-2556-4a2b-9525-e3c8c214828e.png align="center")

这种方式的缺陷在于，请求在本机是明文传输的，可能会违反某些组织的安全需要。有些 SDK 的外部服务 URL 包含协议部分都是硬编码的，因此发送 HTTP 请求是不可能的。这种办法的好处是可以获取 HTTP 头、方法以及 URL 路径，并可以据此制定规则。

在 HTTPS 形式下，从应用到外部目标的请求是端到端加密的。下图演示了这种方式的数据流。在 Gateway 中见到的报文，同样还是 HTTPS。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745893352730/6ca98966-3917-4b86-9ab0-c9837a10d361.png align="center")

端到端的 HTTPS 可能是更好的一种加密方式。然而因为流量是加密通过 Istio 代理和 Egress Gateway 的，因此只能看到源和目的的 IP 以及 [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)。在 Istio 开通双向 TLS 的情况下，[源身份](https://istio.io/docs/concepts/security/mutual-tls/#identity)也是可知的。Gateway 无法获知 HTTP 头、方法以及 URL 路径，因此基于 HTTP 信息的策略就无法实现了。我们的用例中要求可以访问 `edition.cnn.com`。如果 Istio 中启用了双向 TLS，组织可以设置部分应用允许访问 `edition.cnn.com`。然而却无法允许或禁止访问特定的 URL。对 `/politics` 的允许或者禁止，在这种上下文中都是无法实现的。

我们认为，这样讲解之后，用户就可以对这两种方法进行优劣势的评估，进而做出合适的选择。

## 结论

本文中我们展示了 Istio 用 HTTP 访问 Egress 时的监控和策略。其中的监控过程，可以配置日志适配器结合 Istio Dashboard 来完成。而访问策略可以通过配置 `VirtualService` 或者配置多种策略适配器来完成。我们演示了一个简单的策略，只允许某些 URL 的访问。我们另外还展示了稍微复杂一些的策略，通过放行指定命名空间中的指定应用，来做出例外。最后，我们比较了两种 HTTPS 过程的优劣，同时也就有不同的控制能力。

## 清理

1. 执行 [配置 Egress Gateway](https://istio.io/docs/tasks/traffic-management/egress-gateway/) 任务中的 [清理](https://istio.io/docs/tasks/traffic-management/egress-gateway/#cleanup)任务。
    
2. 删除日志和策略配置：
    
    ```shell
    kubectl delete logentry egress-access -n istio-system
    kubectl delete stdio egress-error-logger -n istio-system
    kubectl delete stdio egress-access-logger -n istio-system
    kubectl delete rule handle-politics -n istio-system
    kubectl delete rule handle-cnn-access -n istio-system
    kubectl delete -n istio-system listchecker path-checker
    kubectl delete -n istio-system listentry request-path
    ```
    
3. 删除 `politics` 命名空间
    
4. 执行[使用 Grafana 进行指标可视化](https://istio.io/docs/tasks/telemetry/using-istio-dashboard/)任务中的[清理](https://istio.io/docs/tasks/telemetry/using-istio-dashboard/#cleanup)环节。
