Ingress Nginx 接連披露高危安全漏洞,是否有更好的選擇?

作者: 澄潭

今年 K8s Ingress Nginx 項目接連披露了三個高危安全漏洞(CVE-2021-25745 [ 1] , CVE-2021-25746 [ 2] , CVE-2021-25748 [ 3] ),該項目也在近期宣佈將停止接收新功能 PR,專注修復並提升穩定性。Ingress Nginx 作爲 K8s 項目自帶的網關組件,被大量用戶的 K8s 集羣默認安裝。作爲處於 Internet 網絡邊界的基礎軟件,又被大規模使用,勢必會成爲一些攻擊者的理想目標。一旦防線攻破,其代價是慘痛的,可以參考同樣是網絡邊界的基礎組件,OpenSSL 的 Heartbleed 心血漏洞殷鑑不遠。

2014 年 Heartbleed 漏洞釋出不久,OpenBSD 開始自行維護 LibreSSL,Google 也推出了 BoringSSL,基於和 OpenSSL 遵循同一套 SSL/TLS 協議標準,提供更安全的替代品。類似的,基於同一套 K8s Ingress API 標準,是否有更安全的 K8s 網關可以替代 Ingress Nginx?

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: bar.foo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80

Ingress API 標準示例

《K8s 網關選型初判:Nginx 還是 Envoy》一文中,我們已經給出了這個新的選項:MSE 雲原生網關。本文繼續展開分析,爲何 MSE 雲原生網關有更好的安全性保障。

Ingress Nginx 架構設計缺陷

1.png

Ingress Nginx 容器架構(圖片來自 kubernetes.io)

Ingress Nginx 安全漏洞頻發的背後,是由其不安全的架構設計導致的:將控制面 Ingress Controller 組件(Go 程序),和數據面 Nginx 組件放在一個容器內。控制面在這裏是一個 Admin 的角色,可想而知其會管理一些敏感的信息,例如和 K8s API Server 通信的認證憑證。數據面和控制面共用容器,就給攻擊者通過數據面獲取這些敏感信息提供了可乘之機。舉例來說:

K8s 使用了 RBAC 機制實現 API Server 接口的認證鑑權,而用於 RBAC 認證的憑證信息,會通過 volume 掛載到容器的/var/ru n/secrets/kubernetes.io/serviceaccount 目錄。CVE-2021-25745 就是利用了控制面拼接 nginx.conf 配置文件的漏洞,通過 Ingress Path 實現了配置注入,讓 Nginx 提供一個靜態文件代理的路由,從而獲取到這個憑證。可以看下有了這個憑證能幹什麼事情:

2.png

Ingress Nginx 憑證權限(圖片來自 blog.lightspin.io)

上圖是 ingress-nginx 這個 ServiceAccount 角色的 ClusterRole 權限描述,因爲網關需要加載 TLS 證書,所以這個角色是具有查看集羣內所有 Secret 的權限的。攻擊者不但能通過這個憑證拿到所有 TLS 證書的私鑰信息,還能拿到集羣裏所有密鑰類配置!

3.png

導致漏洞的架構根因 (圖片來自 blog.lightspin.io)

實際上,CVE-2021-25746 和 CVE-2021-25748,包括更早的 CVE-2021-25742 [ 4] ,根因都是這個問題。CVE-2021-25742 基於 custom snippet 通過 Nginx 配置片段實現憑證獲取;CVE-2021-25746 可以基於多種 Ingress Annotation 實現 Nginx 配置片段注入獲取憑證;CVE-2021-25748 繞過了針對 CVE-2021-25745 修復的正則檢測。真是防不勝防......

Ingress Nginx 社區認識到了這個架構問題的嚴重性,已經開始計劃做控制面和數據面的分離。若繼續保持現有架構,未來可能會爆出更嚴重的安全漏洞。

值得注意的是,這種架構除了會導致上述安全問題,還會導致容器 CPU 負載較高時,控制面和數據面進程互相搶佔調度,出現一系列穩定性問題,例如:

  1. 由控制面負責的存活健康檢查(livenessProbe)超時失敗,導致容器不斷重啓

  2. 在開啓了 prometheus 採集監控指標的情況下,控制面因爲高負載搶佔不到足夠的 CPU,會出現 OOM,導致容器被 kill,詳細原因見文末相關 issue [ 5]

更安全的替代品——MSE 雲原生網關

4.png

MSE 雲原生網關的控制面和數據面架構

從上圖可以看到,MSE 雲原生網關使用了數據面(Envoy)和控制面(Istio)隔離的架構,從根本上避免了上述問題。MSE 雲原生網關採用了託管部署的模式,不是部署在用戶自己的 K8s 集羣中,即使出現了安全漏洞,用戶也可以通過一鍵平滑升級輕鬆修復漏洞。並且有專業安全團隊收集漏洞情報,相比開源能提供更迅速、更可靠的修復方案。

基於前面幾個 CVE 漏洞原理的說明,不難發現 Ingress Nginx 通過控制面拼接 nginx.conf 配置實現數據面控制的方式也存在很大的安全隱患,例如定義一個特殊的 Ingress Path:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-example
spec:
  rules:
  - http:
      paths:
      - pathType: Prefix
        # 下文中{...}省略號隱去了可能引發漏洞的配置
        path: "/inject{...}location /abc"
        backend:
          service:
            name: service
            port:
              number: 80

就會在生成的 nginx.conf 中出現如下配置片段:

location /inject{...}location /abc {
  set $ingress_name "ingress-example";
  ...
  ...
}

熟悉 Nginx 配置的同學會瞭解,這裏有兩個 location 路徑匹配規則,其中 location /abc 是對應上述 Ingress 路由配置的,而 location /inject 則可以實現一個額外的配置注入,在{...}中可以寫任意的 Nginx  Location 級別配置,甚至使用靈活度很高的 Lua 腳本,達到配置注入者的各種目的。

不同於 Ingress Nginx 通過控制面拼接 nginx.conf 配置實現數據面控制,雲原生網關使用了更安全可靠的 xDS 協議,通過 xDS API 配置解析替代字符串拼接,從根本上避免了拼接配置導致配置注入的問題,確保配置動作是明確的,行爲是可預期的。下面是向 Envoy 下發路由匹配規則用到的 proto 協議,不同於 Ingress Nginx 的 location 指令拼接,這種方式顯然約束了路由匹配配置的作用範圍。

message RouteMatch {
  option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RouteMatch";
  message GrpcRouteMatchOptions {
    option (udpa.annotations.versioning).previous_message_type =
        "envoy.api.v2.route.RouteMatch.GrpcRouteMatchOptions";
  }
  message TlsContextMatchOptions {
    option (udpa.annotations.versioning).previous_message_type =
        "envoy.api.v2.route.RouteMatch.TlsContextMatchOptions";
    google.protobuf.BoolValue presented = 1;
    google.protobuf.BoolValue validated = 2;
  }
  // An extensible message for matching CONNECT requests.
  message ConnectMatcher {
  }
  reserved 5, 3;
  reserved "regex";
  oneof path_specifier {
    option (validate.required) = true;
    string prefix = 1;
    string path = 2;
    type.matcher.v3.RegexMatcher safe_regex = 10 [(validate.rules).message = {required: true}];
    ConnectMatcher connect_matcher = 12;
    string path_separated_prefix = 14 [(validate.rules).string = {pattern: "^[^?#]+[^?#/]$"}];
    string path_template = 15
        [(validate.rules).string = {min_len: 1 max_len: 256 ignore_empty: true}];
  }
  google.protobuf.BoolValue case_sensitive = 4;
  core.v3.RuntimeFractionalPercent runtime_fraction = 9;
  repeated HeaderMatcher headers = 6;
  repeated QueryParameterMatcher query_parameters = 7;
  GrpcRouteMatchOptions grpc = 8;
  TlsContextMatchOptions tls_context = 11;
  repeated type.matcher.v3.MetadataMatcher dynamic_metadata = 13;
}

值得一提的是,目前 Ingress Nginx 的大量路由策略功能都需要通過更新 nginx.conf,然後重啓 Nginx 生效,重啓過程中客戶端連接會斷開,在 websocket 等長連接場景下,會造成業務影響;而通過 Envoy 的 xDS 配置下發,路由策略生效基於 RDS/ECDS,對長連接完全無影響。

爲了方便用戶從 Ingress Nginx 平滑遷移到 MSE 雲原生網關,我們除了完全兼容了 K8s Ingress API 的標準,也兼容了常用的 Ingress Nginx Annotation,詳見文末文檔 [ 6]

此外,雲原生網關的插件市場提供了多種認證鑑權和安全防護插件,可以增強網絡安全防護能力:

5.png

雲原生網關插件市場

用戶還可以基於 Wasm 技術,用多種語言(Go、JS、Rust 等)實現網關功能動態擴展(無需重啓網關),基於 Wasm 的沙箱機制,即使你的代碼邏輯訪問了空指針,也不會導致網關 crash。這種安全且簡單的擴展機制也是 Ingress Nginx 不具備的。

參考鏈接:

[1] CVE-2021-25745:

https://github.com/kubernetes/ingress-nginx/issues/8502

[2] CVE-2021-25746:

https://github.com/kubernetes/ingress-nginx/issues/8503

[3] CVE-2021-25748:

https://github.com/kubernetes/ingress-nginx/issues/8686

[4] CVE-2021-25742:

https://github.com/kubernetes/ingress-nginx/issues/7837

[5] 相關 issue:

https://github.com/kubernetes/ingress-nginx/pull/8397

[6] 文檔:

https://help.aliyun.com/document_detail/424813.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章