秘密发现服务 (SDS)

TLS 证书(秘密)可以在引导程序的 static_resource secrets 中指定。但它们也可以通过秘密发现服务 (SDS) 远程获取。

SDS 最重要的优点是简化证书管理。没有此功能,在 k8s 部署中,证书必须作为秘密创建并挂载到代理容器中。如果证书已过期,则需要更新秘密并重新部署代理容器。使用 SDS,中央 SDS 服务器会将证书推送到所有 Envoy 实例。如果证书已过期,服务器只需将新证书推送到 Envoy 实例,Envoy 将立即使用新证书,无需重新部署。

如果监听器服务器证书需要通过 SDS 远程获取,则它不会被标记为活动状态,其端口在获取证书之前不会打开。如果 Envoy 由于连接故障或错误响应数据而无法获取证书,则监听器将被标记为活动状态,端口将打开,但对该端口的连接将被重置。

上游集群以类似的方式处理,如果集群客户端证书需要通过 SDS 远程获取,则它不会被标记为活动状态,并且在获取证书之前不会使用。如果 Envoy 由于连接故障或错误响应数据而无法获取证书,则该集群将被标记为活动状态,它可以用于处理请求,但路由到该集群的请求将被拒绝。

如果静态集群使用 SDS,并且它需要定义 SDS 集群(除非使用 Google gRPC,它不需要集群),则 SDS 集群必须在使用它的静态集群之前定义。

Envoy 代理与 SDS 服务器之间的连接必须是安全的。一种选择是在同一主机上运行 SDS 服务器并使用 Unix 域套接字进行连接。否则,连接需要代理和 SDS 服务器之间的 TLS 身份验证。当前用于身份验证的凭据类型包括

  • mTLS – 在这种情况下,SDS 连接的客户端证书必须静态配置。

  • AWS IAM SigV4

SDS 服务器

SDS 服务器需要实现 gRPC 服务 SecretDiscoveryService。它遵循与其他 xDS 相同的协议。

注意

SPIRE,生产身份 SPIFFE 规范 的开源参考实现,可以作为 Envoy 的 SDS 服务器。有关如何使用 SPIFFE 和 SPIRE 配置 Envoy,请参阅 SPIRE 文档

SDS 配置

SdsSecretConfig 用于指定秘密。它的字段 _name_ 是必填字段。如果它的 _sds_config_ 字段为空,则 _name_ 字段指定引导程序 static_resource secrets 中的秘密。否则,它指定 SDS 服务器作为 ConfigSource。仅 gRPC 支持 SDS 服务,因此它的 _api_config_source_ 必须指定 grpc_service

SdsSecretConfig 用于 CommonTlsContext 中的两个字段。第一个字段是 _tls_certificate_sds_secret_configs_,用于使用 SDS 获取 TlsCertificate。第二个字段是 _validation_context_sds_secret_config_,用于使用 SDS 获取 CertificateValidationContext

密钥轮换

通常更倾向于通过 gRPC SDS 执行密钥轮换,但当无法或不需要这样做时(例如,在 SDS 凭据引导期间),SDS 允许在秘密引用文件系统路径时进行文件系统轮换。目前,这支持以下秘密类型

默认情况下,包含秘密的目录将被监视以进行文件系统移动事件。例如,位于 /foo/bar/baz/cert.pem 的密钥或受信任的 CA 证书将在 /foo/bar/baz 处被监视。可以通过在 TlsCertificateCertificateValidationContext 中指定 _watched_directory_ 路径来明确控制被监视的目录。这允许在路径前身(例如 /foo/bar)处建立监视;当实现常见的密钥轮换方案时,此功能非常有用。

密钥轮换的示例在 下面 提供。

示例一:static_resource

此示例展示了如何在 static_resource 中配置秘密

static_resources:
  secrets:
    - name: server_cert
      tls_certificate:
        certificate_chain:
          filename: certs/servercert.pem
        private_key:
          filename: certs/serverkey.pem
    - name: client_cert
      tls_certificate:
        certificate_chain:
          filename: certs/clientcert.pem
        private_key:
          filename: certs/clientkey.pem
    - name: validation_context
      validation_context:
        trusted_ca:
          filename: certs/cacert.pem
        verify_certificate_hash:
          E0:F3:C8:CE:5E:2E:A3:05:F0:70:1F:F5:12:E3:6E:2E:97:92:82:84:A2:28:BC:F7:73:32:D3:39:30:A1:B6:FD
  clusters:
    - connect_timeout: 0.25s
      load_assignment:
        cluster_name: local_service_tls
        ...
        transport_socket:
          name: envoy.transport_sockets.tls
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
            common_tls_context:
              tls_certificate_sds_secret_configs:
              - name: client_cert
  listeners:
    ....
    filter_chains:
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            tls_certificate_sds_secret_configs:
            - name: server_cert
            validation_context_sds_secret_config:
              name: validation_context

在此示例中,证书在引导程序 static_resource 中指定,它们不会远程获取。在配置中,_secrets_ 静态资源有 3 个秘密:client_certserver_certvalidation_context。在集群配置中,主机之一在其 _tls_certificate_sds_secret_configs_ 中使用 client_cert。在监听器部分,其中之一在其 _tls_certificate_sds_secret_configs_ 中使用 server_cert,并在其 _validation_context_sds_secret_config_ 中使用 validation_context

示例二:SDS 服务器

此示例展示了如何配置从远程 SDS 服务器获取的秘密

 1node:
 2  cluster: envoy_cluster
 3  id: envoy_node
 4
 5static_resources:
 6  listeners:
 7  - name: listener_0
 8    address:
 9      socket_address:
10        address: 0.0.0.0
11        port_value: 8000
12    filter_chains:
13    - transport_socket:
14        name: envoy.transport_sockets.tls
15        typed_config:
16          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
17          common_tls_context:
18            tls_certificate_sds_secret_configs:
19            - name: server_cert
20              sds_config:
21                api_config_source:
22                  api_type: GRPC
23                  grpc_services:
24                  - envoy_grpc:
25                      cluster_name: sds_server_mtls
26            validation_context_sds_secret_config:
27              name: validation_context
28              sds_config:
29                api_config_source:
30                  api_type: GRPC
31                  grpc_services:
32                  - envoy_grpc:
33                      cluster_name: sds_server_uds
34  clusters:
35  - name: sds_server_mtls
36    typed_extension_protocol_options:
37      envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
38        "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
39        explicit_http_config:
40          http2_protocol_options: {}
41    load_assignment:
42      cluster_name: sds_server_mtls
43      endpoints:
44      - lb_endpoints:
45        - endpoint:
46            address:
47              socket_address:
48                address: 127.0.0.1
49                port_value: 8234
50    transport_socket:
51      name: envoy.transport_sockets.tls
52      typed_config:
53        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
54        common_tls_context:
55          tls_certificates:
56          - certificate_chain:
57              filename: certs/servercert.pem
58            private_key:
59              filename: certs/serverkey.pem
60  - name: sds_server_uds
61    typed_extension_protocol_options:
62      envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
63        "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
64        explicit_http_config:
65          http2_protocol_options: {}
66    load_assignment:
67      cluster_name: sds_server_uds
68      endpoints:
69      - lb_endpoints:
70        - endpoint:
71            address:
72              pipe:
73                path: /tmp/uds_path
74  - name: example_cluster
75    load_assignment:
76      cluster_name: local_service_tls
77      endpoints:
78      - lb_endpoints:
79        - endpoint:
80            address:
81              socket_address:
82                address: 127.0.0.1
83                port_value: 8443
84    transport_socket:
85      name: envoy.transport_sockets.tls
86      typed_config:
87        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
88        common_tls_context:
89          tls_certificate_sds_secret_configs:
90          - name: client_cert
91            sds_config:
92              api_config_source:
93                api_type: GRPC
94                grpc_services:
95                - google_grpc:
96                    target_uri: unix:/tmp/uds_path
97                    stat_prefix: sds_uds_server

为了说明,上面的示例使用三种方法访问 SDS 服务器。gRPC SDS 服务器可以通过 Unix 域套接字路径 /tmp/uds_path127.0.0.1:8234 通过 mTLS 访问。它提供三个秘密,client_certserver_certvalidation_context。在配置中,集群 example_cluster 证书 client_cert 被配置为使用 Google gRPC 与 UDS 交谈 SDS 服务器。监听器需要从 SDS 服务器获取 server_certvalidation_contextserver_cert 使用 Envoy gRPC 与集群 sds_server_mtls 配合使用,该集群配置为使用客户端证书通过 mTLS 与 SDS 服务器通信。validate_context 使用 Envoy gRPC 与集群 sds_server_uds 配合使用,该集群配置为使用 UDS 路径与 SDS 服务器通信。

示例三:xDS gRPC 连接的证书轮换

管理 Envoy 与 xDS 服务器之间的 xDS gRPC 连接的证书会引入引导问题:SDS 服务器无法管理连接到服务器所需的证书。

此示例展示了如何通过从文件系统获取 SDS 配置来设置 xDS 连接。证书和密钥文件将使用 inotify 进行监视,并在没有重新启动的情况下自动重新加载。相反,示例二:SDS 服务器 要求在更新后重新启动才能重新加载 xDS 证书和密钥。

clusters:
- name: control_plane
  type: LOGICAL_DNS
  connect_timeout: 1s
  load_assignment:
    cluster_name: control_plane
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: controlplane
              port_value: 8443
  typed_extension_protocol_options:
    envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
      "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
      explicit_http_config:
        http2_protocol_options: {}
  transport_socket:
    name: "envoy.transport_sockets.tls"
    typed_config:
      "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext"
      common_tls_context:
        tls_certificate_sds_secret_configs:
          name: tls_sds
          sds_config:
            path: /etc/envoy/tls_certificate_sds_secret.yaml
        validation_context_sds_secret_config:
          name: validation_context_sds
          sds_config:
            path: /etc/envoy/validation_context_sds_secret.yaml

SDS 配置文件 /etc/envoy/tls_certificate_sds_secret.yaml 中给出了客户端证书的路径,包括客户端的证书链和私钥。

resources:
  - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
    name: tls_sds
    tls_certificate:
      certificate_chain:
        filename: /certs/sds_cert.pem
      private_key:
        filename: /certs/sds_key.pem

SDS 配置文件 /etc/envoy/validation_context_sds_secret.yaml 中给出了用于验证 xDS 服务器证书的 CA 证书捆绑包的路径。

resources:
  - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
    name: validation_context_sds
    validation_context:
      trusted_ca:
        filename: /certs/cacert.pem

在上面的示例中,将建立对 /certs 的监视。此目录中的文件移动将触发更新。提供改进原子性的另一种常见密钥轮换方案是建立一个活动符号链接 /certs/current 并使用原子移动操作来替换符号链接。在这种情况下,监视需要在证书的祖父母目录上。Envoy 通过使用 _watched_directory_ 支持此方案。继续上面的示例

resources:
  - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
    name: tls_sds
    tls_certificate:
      certificate_chain:
        filename: /certs/current/sds_cert.pem
      private_key:
        filename: /certs/current/sds_key.pem
      watched_directory:
        path: /certs
resources:
  - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
    name: validation_context_sds
    validation_context:
      trusted_ca:
        filename: /certs/current/cacert.pem
      watched_directory:
        path: /certs

可以使用以下方法执行秘密轮换

ln -s <path to new secrets> /certs/new && mv -Tf /certs/new /certs/current

统计信息

SSL 套接字工厂输出以下与 SDS 相关的统计信息。它们都是计数器类型。

对于下游监听器,它们位于 _listener.<LISTENER_IP>.server_ssl_socket_factory._ 命名空间中。

名称

描述

ssl_context_update_by_sds

已更新的 ssl 上下文的总数。

downstream_context_secrets_not_ready

由于 ssl 证书为空而导致的下游连接重置的总数。

对于上游集群,它们位于 _cluster.<CLUSTER_NAME>.client_ssl_socket_factory._ 命名空间中。

名称

描述

ssl_context_update_by_sds

已更新的 ssl 上下文的总数。

upstream_context_secrets_not_ready

由于 ssl 证书为空而导致的上游连接重置的总数。

SDS 有一个 统计信息 树,根位于 _sds.<SECRET_NAME>._ 命名空间中。此外,以下统计信息在此命名空间中跟踪

名称

描述

key_rotation_failed

在 SDS 更新之外失败的文件系统密钥轮换的总数。