TLS

Envoy 支援監聽器中的 TLS 終止,以及在連線到上游叢集時的 TLS 發起。Envoy 的支援足以為現代網路服務執行標準邊緣代理任務,以及發起與具有進階 TLS 需求(TLS1.2、SNI 等)的外部服務的連線。Envoy 支援下列 TLS 功能

  • 可設定的密碼:每個 TLS 監聽器和用戶端都可以指定它支援的密碼。

  • 用戶端憑證:除了伺服器憑證驗證外,上游/用戶端連線可以呈現用戶端憑證。

  • 憑證驗證和釘選:憑證驗證選項包括基本鏈驗證、主體名稱驗證和雜湊釘選。

  • 憑證撤銷:如果提供憑證撤銷清單 (CRL),Envoy 可以根據憑證撤銷清單 (CRL) 檢查對等憑證。提供

  • ALPN:TLS 監聽器支援 ALPN。HTTP 連線管理員會使用此資訊(以及協定推斷)來判斷用戶端是否正在使用 HTTP/1.1 或 HTTP/2。

  • SNI:伺服器(監聽器)和用戶端(上游)連線都支援 SNI。

  • 會話恢復:伺服器連線支援透過 TLS 會話票證恢復先前的會話(請參閱 RFC 5077)。恢復可以在熱重新啟動之間以及平行 Envoy 執行個體之間執行(通常在前端代理設定中很有用)。

  • BoringSSL 私密金鑰方法:TLS 私密金鑰操作(簽署和解密)可以從 擴充功能 非同步執行。這允許擴展 Envoy 以支援各種金鑰管理方案(例如 TPM)和 TLS 加速。此機制使用 BoringSSL 私密金鑰方法介面

  • OCSP Stapling:線上憑證釘選協定回應可能會釘選到憑證。

底層實作

目前 Envoy 是使用 BoringSSL 作為 TLS 提供者來撰寫的。

FIPS 140-2

BoringSSL 可以依照 FIPS 相容模式 建置,遵循 BoringCrypto 模組的安全政策 中的建置指示,使用 --define boringssl=fips Bazel 選項。目前,此選項僅在 Linux-x86_64 上可用。

FIPS 建置的正確性可以透過檢查 --version 輸出的 BoringSSL-FIPS 是否存在來驗證。

請務必注意,雖然使用 FIPS 相容模組對於 FIPS 相容性是必要的,但本身並不足夠,而且根據上下文,可能需要其他步驟。額外的需求可能包括僅使用核准的演算法和/或僅使用以 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
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            virtual_hosts:
            - name: default
              domains: ["*"]
              routes:
              - match: {prefix: "/"}
                route:
                  cluster: some_service
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            tls_certificates:
            - certificate_chain: {filename: "certs/servercert.pem"}
              private_key: {filename: "certs/serverkey.pem"}
            validation_context:
              trusted_ca:
                filename: certs/cacert.pem
  clusters:
  - name: some_service
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: some_service
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                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": "certs/servercert.pem"}
            private_key: {"filename": "certs/serverkey.pem"}
            ocsp_staple: {"filename": "certs/server_ocsp_resp.der"}
          validation_context:
            match_typed_subject_alt_names:
            - san_type: DNS
              matcher:
                exact: "foo"
            trusted_ca:
              filename: /etc/ssl/certs/ca-certificates.crt

/etc/ssl/certs/ca-certificates.crt 是 Debian 系統上系統 CA 組合的預設路徑。trusted_camatch_typed_subject_alt_names 可讓 Envoy 驗證 127.0.0.1:1234 的伺服器身分是否為「foo」,方式與標準 Debian 安裝上的 cURL 相同。Linux 和 BSD 上系統 CA 組合的常見路徑如下

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

  • /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 的參考資料。

注意

如果僅指定 trusted_ca,Envoy 將驗證所呈現憑證的憑證鏈,但不驗證其主體名稱、雜湊等。通常需要其他驗證內容設定,具體取決於部署。

自訂憑證驗證器

上述說明設定由「預設」憑證驗證器使用。Envoy 也支援 envoy.tls.cert_validator 擴充功能類別中的自訂驗證器,該驗證器可以在 CertificateValidationContext 上設定。

例如,可以將 Envoy 設定為根據 SPIFFE 規格驗證對等憑證,並在單一監聽器或叢集中有多個信任組合。如需更多詳細資訊,請參閱 custom_validator_config 欄位的說明文件

憑證選取

DownstreamTlsContexts 支援多個 TLS 憑證。這些可以是多個伺服器名稱模式的 RSA 和 ECDSA 憑證的組合。

憑證設定/載入規則

  • DNS SAN 或主體通用名稱會擷取為伺服器名稱模式,以在交握期間比對 SNI。如果憑證中存在 DNS SAN,則不會使用主體通用名稱。

  • FQDN(例如「test.example.com」)和萬用字元(例如「*.example.com」)同時有效,這將載入為兩個不同的伺服器名稱模式。

  • 如果為相同的名稱或名稱模式指定多個特定類型(RSA 或 ECDSA)的憑證,則會使用載入的第一個憑證。

  • 非 P-256、P-384 或 P-521 伺服器 ECDSA 憑證會被拒絕。

  • 靜態憑證和 SDS 憑證可能不會在給定的 DownstreamTlsContext 中混合。

憑證選取規則

  • 如果用戶端支援 SNI,例如 SNI 是「test.example.com」,它會尋找與 SNI 完全相符的憑證。如果憑證符合 OCSP 原則並與金鑰類型相符,則會選取該憑證進行交握。如果憑證符合 OCSP 原則,但金鑰類型為 RSA,而用戶端具有 ECDSA 功能,則會將其標記為候選憑證,並繼續搜尋,直到選取完全相符的憑證或憑證耗盡。如果沒有完全相符的憑證,則會選取候選憑證進行交握。

  • 如果用戶端支援 SNI,但從與 SNI 完全相符的憑證中未選取任何憑證,則它會比對萬用字元伺服器名稱。例如,如果 SNI 是「test.example.com」,則具有「test.example.com」的憑證會優先於「*.example.com」。而且萬用字元比對僅適用於 1 個深度的層級,因此「*.com」不會與「test.example.com」比對。之後,它會對每個憑證執行 OCSP 和金鑰類型檢查,這與在精確 SNI 比對之後發生的情況相同。

  • 如果沒有從符合萬用字元名稱的憑證中選取憑證,則會選取候選憑證進行交握(如果存在)。如果沒有候選憑證,請檢查full_scan_certs_on_sni_mismatch,如果已啟用,則掃描所有憑證,否則選擇第一個憑證進行交握。

  • 如果客戶端完全沒有提供 SNI,無論full_scan_certs_on_sni_mismatch是 false 還是 true,都會進行完整掃描。

  • 完整掃描會對每個憑證執行 OCSP 和金鑰類型檢查,這與上面精確 SNI 匹配中的描述相同。如果沒有選取任何憑證,則會回退到整個清單中的第一個憑證。

  • 目前僅支援兩種金鑰類型:RSA 或 ECDSA。如果客戶端支援 P-256、P-384 或 P-521 ECDSA,則會優先選擇 P-256、P-384 或 P-521 ECDSA 憑證,而不是 RSA。回退的憑證可能會導致交握失敗。例如,客戶端僅支援 RSA 憑證,而憑證僅支援 ECDSA。

  • 最終選取的憑證必須符合 OCSP 策略。如果找不到此類憑證,則會拒絕連線。

注意

透過支援基於 SNI 的憑證選取,可以為多個主機名稱設定大量憑證。引入full_scan_certs_on_sni_mismatch來決定當客戶端提供 SNI 時,是否在 SNI 不匹配時繼續完整掃描。SNI 不匹配在此上下文中包含兩種情況:一種是沒有符合 SNI 的憑證,另一種是有符合 SNI 的憑證,但這些憑證的 OCSP 策略失敗。full_scan_certs_on_sni_mismatch預設為 false,因此預設會停用完整掃描。如果啟用完整掃描,它將在 SNI 不匹配時從整個憑證清單中尋找憑證,這可能會因為 O(n) 的複雜度而導致潛在的 DoS 攻擊問題。

目前對於UpstreamTlsContexts僅支援單一 TLS 憑證。

機密探索服務 (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 回應。實際上,必須釘選的憑證會導致 cEnvoy 的行為如同 OCSP 釘選策略為MUST_STAPLE一樣。Envoy 在其 OCSP 回應過期後,將不會使用必須釘選的憑證來建立新連線。

OCSP 回應永遠不會釘選到未透過 status_request 擴充功能表明支援 OCSP 釘選的 TLS 要求。

提供以下執行階段旗標來調整 OCSP 回應的要求並覆寫 OCSP 策略。這些旗標預設為 true

  • envoy.reloadable_features.require_ocsp_response_for_must_staple_certs:停用此選項允許操作員在組態中省略必須釘選憑證的 OCSP 回應。

  • envoy.reloadable_features.check_ocsp_policy:停用此選項將會停用 OCSP 策略檢查。如果用戶端支援,即使回應已過期,OCSP 回應也會在可用時進行釘選。如果沒有回應,則會跳過釘選。

對於 UpstreamTlsContexts,OCSP 回應會被忽略。

驗證篩選器

Envoy 提供一個網路篩選器,透過從 REST VPN 服務擷取的主體來執行 TLS 用戶端驗證。此篩選器會將呈現的客戶端憑證雜湊與主體清單進行比對,以確定是否應允許連線。也可以設定選用的 IP 允許清單。此功能可用於為 Web 基礎結構建置邊緣 Proxy VPN 支援。

用戶端 TLS 驗證篩選器組態參考

自訂交握器擴充功能

CommonTlsContext 具有一個 custom_handshaker 擴充功能,可用於完全覆寫 SSL 交握行為。這對於實作任何難以使用回呼表示的 TLS 行為非常有用。不需要撰寫自訂交握器即可使用私密金鑰方法,請參閱上面描述的私密金鑰方法介面

為了避免重新實作所有Ssl::ConnectionInfo 介面,自訂實作可能會選擇擴充 Envoy::Extensions::TransportSockets::Tls::SslHandshakerImpl

自訂交握器需要透過 HandshakerCapabilities 明確宣告它們負責的 TLS 功能。預設的 Envoy 交握器將管理其餘部分。

一個有用的範例交握器,名為 SslHandshakerImplForTest,位於 此測試中,並示範特殊情況的 SSL_ERROR 處理和回呼。

疑難排解

當 Envoy 在與上游叢集建立連線時發起 TLS 時,任何錯誤都會記錄到 UPSTREAM_TRANSPORT_FAILURE_REASON 欄位或 AccessLogCommon.upstream_transport_failure_reason 欄位中。

當 Envoy 監聽器取得連線並與下游執行 TLS 時,任何錯誤都會記錄到 DOWNSTREAM_TRANSPORT_FAILURE_REASON 欄位或 AccessLogCommon.downstream_transport_failure_reason 欄位中。

常見錯誤有

  • Secret is not supplied by SDS:Envoy 仍在等待 SDS 提供金鑰/憑證或根 CA。

  • SSLV3_ALERT_CERTIFICATE_EXPIRED:對等憑證已過期,並且不允許在組態中使用。

  • SSLV3_ALERT_CERTIFICATE_UNKNOWN:對等憑證不在組態中指定的 SPKI 中。

  • SSLV3_ALERT_HANDSHAKE_FAILURE:交握失敗,通常是因為上游需要用戶端憑證但未提供。

  • TLSV1_ALERT_PROTOCOL_VERSION:TLS 協定版本不符。

  • TLSV1_ALERT_UNKNOWN_CA:對等憑證 CA 不在信任的 CA 中。

可以在此處找到 BoringSSL 可以引發的更詳細錯誤列表