TLS

当和上游集群连接的时候,Envoy 在监听器中同时支持 TLS 终结TLS 发起 。这种有效的支持确保了 Envoy 能够在现代 web 服务中发挥标准边缘代理的职责,以及和那些有高级 TLS 需求(TLS1.2、SNI 等等)的外部服务发起连接。Envoy 支持以下的 TLS 特性:

  • 可配置的加密 :每一个 TLS 监听器和客户端可以指定那些他们支持的加密算法。

  • 客户端证书 :除了服务器证书验证外,上游/客户端连接还能够提供客户端证书。

  • 证书验证和固定 :证书验证选项包括基本的证书链验证、持有者名称验证以及哈希固定。

  • 证书撤销 :如果 提供 了证书撤销列表(CRL), Envoy 可以根据它来检查对端证书。

  • ALPN :TLS 监听器支持 ALPN。HTTP 连接管理器使用这种信息(除了协议接口外)来决定一个客户端在用 HTTP/1.1 还是 HTTP/1.2。

  • SNI :SNI 同时支持服务器连接(监听器)和客户端连接(上游)。

  • 会话(session)恢复 :服务器连接支持通过 TLS 会话凭证来恢复之前的会话(查看 RFC 5077 )。可以在热重启时和并行 Envoy 的实例之间执行会话恢复(在前段代理配置中是非常有用的)。

  • BoringSSL 私钥方法 :TLS 私钥操作(签名和解密)可以通过 扩展 来异步执行。这允许通过扩展 Envoy 来支持多个 key 的管理方案(比如 TPM)和 TLS 加速。这种机制使用了 BoringSSL 私钥方法接口

  • OCSP Stapling : 在线证书绑定协议(Online Certificate Stapling Protocol)响应可能要被绑定到证书中。

底层实现

目前 Envoy 使用 BoringSSL 作为 TLS 提供者。

FIPS 140-2

BoringSSL 可以在 FIPS 合规模式 创建,使用 --define boringssl=fips Bazel 选项,根据 BoringCrypto 模块安全政策 的指导来创建。目前,这个选项只在 Linux-x86_64 上有效。

FIPS 创建的正确性可以通过检查 –version 选项的输出中是否存在 BoringSSL-FIPS 字样来验证。

需要特别注意,对于 TIPS 合规来讲使用 FIPS-compliant 模式是非常必要的,但仅仅靠这个是远远不不够的,还需要依赖于上下文,或者还需要一些其他的步骤。其他的需求可能包含只使用经过批准的算法和/或者仅仅使用在 FIPS 合规模式下操作的模块所产生的私钥。关于更多内容,可以参考 BoringCrypto 模块安全政策 和/或者 认证的 CMVP 实验室

请注意 FIPS 合规构建是基于 BoringSSL 的老版本,而不是非 FIPS 构建,而且它并不支持最新的 QUIC API。

开启证书验证

上下游连接的证书验证是没开启的,除非验证信息指定了一个或者多个受信任的证书签发机构。

配置示例

static_resources:
  listeners:
  - name: listener_0
    address: { socket_address: { address: 127.0.0.1, port_value: 10000 } }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        # ...
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            validation_context:
              trusted_ca:
                filename: /usr/local/my-client-ca.crt
  clusters:
  - name: some_service
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: some_service
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.2
                port_value: 1234
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        common_tls_context:
          tls_certificates:
            certificate_chain: { "filename": "/cert.crt" }
            private_key: { "filename": "/cert.key" }
            ocsp_response: { "filename": "/ocsp_response.der" }
          validation_context:
            match_subject_alt_names:
              exact: "foo"
            trusted_ca:
              filename: /etc/ssl/certs/ca-certificates.crt

在 Debian 系统上 /etc/ssl/certs/ca-certificates.crt 是系统 CA 包文件的的默认路径。trusted_camatch_subject_alt_names 能够使 Envoy 在验证 127.0.0.2:1234 的服务器标识为 “foo” 时,使用与标准安装在 Debian 系统上的 cURL 用相同的方式。在 Linux 和 BSD 系统上的系统 CA 包的常见路径如下:

  • /etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu/Gentoo etc.)

  • /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (CentOS/RHEL 7)

  • /etc/pki/tls/certs/ca-bundle.crt (Fedora/RHEL 6)

  • /etc/ssl/ca-bundle.pem (OpenSUSE)

  • /usr/local/etc/ssl/cert.pem (FreeBSD)

  • /etc/ssl/cert.pem (OpenBSD)

其他 TLS 选项可参考 UpstreamTlsContextsDownstreamTlsContexts

Attention

如果只指定了 trusted_ca, 则 Envoy 将会验证所提供证书的证书链, 而不验证其主题名称、哈希等。根据部署,通常需要其他验证上下文配置。

证书选择

DownstreamTlsContexts 支持多个 TLS 证书。这些可能是 RSA 和 P-256 ECDSA 证书的一种混合。以下规则适用:

  • 只有特定类型(RSA 或者ECDSA)中的一个证书被指定。

  • Non-P-256 服务器 ECDSA 证书被拒绝。

  • 如果客户端支持 P-256 ECDSA,且在 DownstreamTlsContext 存在 P-256 ECDSA 证书,那么这个证书就会被选择,这和 OCSP 策略是一致的。

  • 如果客户端只支持 RSA 证书,且在 DownstreamTlsContext 存在 RSA 证书,那么这个证书就会被选择。

  • 否则,列取的第一个证书将会被使用。如果客户端只支持 RAS 证书而服务器端只支持 ECDSA 证书的话,这将会导致握手失败。

  • 静态和 SDS 证书可能不会被混何在 DownstreamTlsContext 中。

  • 被选择的证书必须符合 OCSP 策略。如果没找到符合条件的证书,连接会被拒绝。

目前只提供单个 TLS 证书 UpstreamTlsContexts

安全发现服务(SDS)

TLS 证书可以在静态资源中指定或者从远端获取。静态资源的证书轮换是支持的,可以通过 SDS 文件系统配置 或者从 SDS 服务器端获取更新信息来实现。详细信息请看 SDS

OCSP Stapling

DownstreamTlsContexts 支持在握手阶段把在线证书状态协议(OCSP)响应绑定到 TLS 证书上。 ocsp_staple 字段允许操作者在上下文中为每一个证书提供一个预先估算的 OCSP 响应。单个响应可能与多个证书无关。如果提供了响应,则 OCSP 响应必须是有效且需要检验证书没有被撤销。过期的 OCSP 响应也可被接受,但是可能会引起下游连接错误,这取决于 OCSP 的绑定策略。

DownstreamTlsContexts 支持使用 ocsp_staple_policy 字段来控制,当与证书相关联的 OCSP 响应丢失或者过期时,Envoy 是否应该停止使用证书还是继续无绑定运行。标记为 must-staple 的证书需要一个有效的 OCSP 响应,而这与 OCSP 的绑定策略无关。是实践中,一个 must-staple 证书让 Envoy 具有和 OCSP 绑定策略是 MUST_STAPLE 时一样的行为。在 OCSP 响应过期以后, Envoy 将不会在新连接中使用一个 must-staple 证书。

OCSP 响应将从来不会被绑定到那些通过 status_request 扩展来实现 OCSP 绑定的 TLS 请求上。

通过以下提供的运行时标志,可以调整 OCSP 的响应需求和复写 OCSP 策略。这些标志的默认值都是 true

  • envoy.reloadable_features.require_ocsp_response_for_must_staple_certs :禁用此标志可允许操作者在配置中忽略 must-staple 证书的 OCSP 响应。

  • envoy.reloadable_features.check_ocsp_policy :禁用此标志可以禁用 OCSP 的策略检查功能。如果客户端支持 OCSP 响应,可在 OCSP 响应可用时进行绑定,即使 OCSP 响应已过期。如果没有响应存在,绑定会被跳过。

OCSP 响应会被 UpstreamTlsContexts 所忽略。

认证过滤器

Envoy 提供了一个网络过滤器,这个过滤器通过从 REST VPN 服务获取的信息中来匹配当前客户端证书的哈希值,以此来决定连接是否被允许。可选项 IP 允许列表也是可以被配置的。在 web 基础设施中此功能可被用来构建边缘代理的 VPN 支持。

客户端 TLS 认证过滤器 configuration reference

定制的握手器(handshaker)扩展

CommonTlsContext 有一个 custom_handshaker 扩展,可以被用来完全重写 SSL 握手行为。这对那些很难通过回调来表达 TLS 行为的实现是非常有用的。写一个使用私钥方法的定制化握手器其实是不必要的,可以查看上面描述的 私钥方法接口

为了避免重复实现所有的 Ssl::ConnectionInfo 接口,可以选择一个定制化的实现来扩展 Envoy::Extensions::TransportSockets::Tls::SslHandshakerImpl

定制化的握手器需要通过 握手器能力 来显式声明。默认的 Envoy 握手器将会管理剩余的事情。

一个名为 SslHandshakerImplForTest 的握手器示例,可在 这个测试 中查看,且演示了特殊情况下 SSL_ERROR 的处理和回调。

故障排除

当和上游集群发生连接的时候,Envoy 会发起 TLS,任何错误都会被记录在 UPSTREAM_TRANSPORT_FAILURE_REASON 字段或者 AccessLogCommon.upstream_transport_failure_reason<envoy_v3_api_field_data.accesslog.v3.AccessLogCommon.upstream_transport_failure_reason> 字段。

  • SDS 没有提供加密信息(Secret is not supplied by SDS) :Envoy 一直在等待 SDS 能够传递 key/cert 或者 root 证书。

  • SSLV3_ALERT_CERTIFICATE_EXPIRED : 对端证书过期且在配置中不被允许。

  • SSLV3_ALERT_CERTIFICATE_UNKNOWN : 对端证书不在指定的 SPKI 配置中。

  • SSLV3_ALERT_HANDSHAKE_FAILURE :握手失败,通常是由于上游需要客户端证书,但是却没有提供。

  • TLSV1_ALERT_PROTOCOL_VERSION :TLS 协议版本不匹配。

  • TLSV1_ALERT_UNKNOWN_CA :对端证书的签发机构不在受信任的签发机构名单中。

更多由 BoringSSL 引发的错误的详细列表信息可以在 这儿 找到。