实验 11:匹配传输和应用协议

在这个实验中,我们将学习如何使用 TLS 检查器过滤器来选择一个特定的过滤器链。单个过滤器链将根据 transport_protocolapplication_protocol将流量分配到不同的上游集群。

我们将为我们的上游主机使用 mendhak/http-https-echo Docker 镜像。这些容器可以被配置为监听 HTTP/HTTPS 并将响应回传。

我们将运行镜像的三个实例来代表非 TLS HTTP、TLS HTTP/1.1 和 TLS HTTP/2 协议。

# non-TLS HTTP
docker run -dit -p 8080:8080 -t mendhak/http-https-echo:18

# TLS HTTP1.1
docker run -dit -e HTTPS_PORT=443 -p 443:443 -t mendhak/http-https-echo:18

# TLS HTTP2
docker run -dit -e HTTPS_PORT=8443 -p 8443:8443 -t mendhak/http-https-echo:18

为了确保这三个容器都在运行,我们可以用 curl 发送几个请求,看看是否得到了回应。从输出中,我们还可以检查主机名是否与实际的 Docker 容器 ID 相符。

# non-TLS HTTP
$ curl http://localhost:8080
{
  "path": "/",
  "headers": {
    "host": "localhost:8080",
    "user-agent": "curl/7.64.0",
    "accept": "*/*"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "localhost",
  "ip": "::ffff:172.18.0.1",
  "ips": [],
  "protocol": "http",
  "query": {},
  "subdomains": [],
  "xhr": false,
  "os": {
    "hostname": "100db0dce742"
  },
  "connection": {}
}

注意在向其他两个容器发送请求时,我们将使用 -k 标志来告诉 curl 跳过对服务器 TLS 证书的验证。此外,我们可以使用 --http1.1--http2 标志来发送 HTTP1.1 或 HTTP2 请求。

# HTTP1.1
$ curl -k --http1.1 https://localhost:443
{
  "path": "/",
  "headers": {
    "host": "localhost",
    "user-agent": "curl/7.64.0",
    "accept": "*/*"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "localhost",
  "ip": "::ffff:172.18.0.1",
  "ips": [],
  "protocol": "https",
  "query": {},
  "subdomains": [],
  "xhr": false,
  "os": {
    "hostname": "51afc40f7506"
  },
  "connection": {
    "servername": "localhost"
  }
}

$ curl -k --http2 https://localhost:8443
{
  "path": "/",
  "headers": {
    "host": "localhost:8443",
    "user-agent": "curl/7.64.0",
    "accept": "*/*"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "localhost",
  "ip": "::ffff:172.18.0.1",
  "ips": [],
  "protocol": "https",
  "query": {},
  "subdomains": [],
  "xhr": false,
  "os": {
    "hostname": "40e7143e6a55"
  },
  "connection": {
    "servername": "localhost"
  }
}

一旦我们验证了容器的正确运行,我们就可以创建 Envoy 配置。我们将使用 tls_inspectorfilter_chain_match 字段来检查传输协议是否是 TLS,以及应用协议是否是 HTTP1.1(http/1.1)或 HTTP2(h2)。基于这些信息,我们会有不同的集群,将流量转发到上游主机(Docker 容器)。记住 HTTP 运行在端口 8080,TLS HTTP/1.1 运行在端口 443,TLS HTTP2 运行在端口 8443

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 10000
    listener_filters:
    - name: "envoy.filters.listener.tls_inspector"
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
    filter_chains:
    - filter_chain_match:
        # Match TLS and HTTP2
        transport_protocol: tls
        application_protocols: [h2]
      filters:
      - name: envoy.filters.network.tcp_proxy
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
          cluster: service-tls-http2
          stat_prefix: https_passthrough
    - filter_chain_match:
        # Match TLS and HTTP1.1
        transport_protocol: tls
        application_protocols: [http/1.1]
      filters:
      - name: envoy.filters.network.tcp_proxy
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
          cluster: service-tls-http1.1
          stat_prefix: https_passthrough
    - filter_chain_match:
      # No matches here, go to HTTP upstream
      filters:
      - name: envoy.filters.network.tcp_proxy
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
          cluster: service-http
          stat_prefix: ingress_http
  clusters:
  - name: service-tls-http2
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service-tls-http2
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 8443
  - name: service-tls-http1.1
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service-tls-http1.1
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 443
  - name: service-http
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service-http
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 8080
admin:
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

将上述 YAML 保存为 tls.yaml,并使用 func-e run -c tls.yaml 运行它。

为了测试这一点,我们可以像以前一样发出类似的 curl 请求,并检查主机名是否与正在运行的 Docker 容器相符。

$ curl http://localhost:10000 | jq '.os.hostname'
"100db0dce742"

$ curl -k --http1.1 https://localhost:10000 | jq '.os.hostname'
"51afc40f7506"

$ curl -k --http2 https://localhost:10000 | jq '.os.hostname'
"40e7143e6a55"

另外,我们可以检查各个容器的日志,看看请求是否被正确发送。

results matching ""

    No results matching ""