開發堅固的 Grafana 安全防護網:GCP IAP 與 Gateway API 整合實戰

在管理現代雲端基礎設施時,監控工具的安全性與可靠性至關重要。Grafana 作為熱門的監控視覺化平台,時常成為企業關鍵基礎設施的一部分。然而,近年來 Grafana 相關的 CVE 漏洞頻繁出現,讓安全團隊不得不尋找更強大的保護方案。

玄貓在多家企業的雲端安全架構設計過程中發現,結合 Google Cloud Identity-Aware Proxy (IAP) 與 Gateway API 的方案能提供優異的防護效果。今天就來分享如何透過 Terraform 自動化這套安全架構的建置流程。

安全架構的優勢與挑戰

在深入技術細節前,讓我們先了解這套架構的價值與可能遇到的挑戰。

架構優勢

  • 流量安全閘道:所有進入 Grafana 的流量都會經過 GCP IAP 審查與過濾,大幅降低 Grafana CVE 漏洞被利用的風險
  • 情境感知存取控制:可根據裝置信任狀態、網路位置、使用者群組等條件實施精細的存取策略
  • DDoS 攻擊防護:受益於 Google 雲端的防護機制,有效抵禦分散式阻斷服務攻擊
  • 完整稽核日誌:所有存取行為都會記錄詳細日誌,便於安全分析與合規要求
  • 零信任架構整合:與現代零信任安全模型完美契合,實作身分驗證與授權的分離

潛在挑戰

  • API 使用複雜化:透過程式化方式存取 Grafana API 變得較為複雜,因為請求必須透過 IAP 層
  • 工具相容性:某些原生工具(如 Grafana Terraform Provider)可能無法直接使用,需要採取替代方案:
    • 從內部網路連線到 Grafana 內部端點
    • 使用臨時的 Kubernetes port-forward 將工具連線到本地主機(雖然需要額外步驟,但設定只需幾秒鐘)

前置需求

在開始整合前,請確保您已具備:

  • 運作中的 Kubernetes 叢集
  • 已設定好的 Ingress 或 Gateway 控制器
  • 安裝並設定好的 Terraform 與 gcloud CLI 工具

原始碼參考

所有實作的完整原始碼可於 [此 GitHub 儲存函式庫https://github.com/vidbregar/grafana-gcp-iap) 取得,建議在執行下列步驟時參考。

設定 OAuth 授權畫面

這個步驟必須在 GCP 控制檯手動完成,但每個 GCP 專案只需執行一次:

  1. 開啟 Identity-Aware Proxy 頁面
  2. 點選「CONFIGURE CONSENT SCREEN」(設定同意畫面)
  3. 選擇「Internal」(內部)使用者類別
  4. 填寫必要欄位,並新增授權網域(例如,若 Grafana 網址為 grafana.example.com,則新增 example.com

在實務應用中,玄貓發現這個步驟經常被忽略,導致後續整合遇到困難。請務必確認 OAuth 設定完成並符合組織政策。

Grafana 設定調整

為了讓 Grafana 能與 GCP IAP 正確整合,需要調整其設定檔。以下是關鍵的 grafana.ini 設定:

grafana.ini:
  ...
  database:
    type: postgres
    host: "<資料函式庫>:5432"
    name: grafana
    user: grafana
    # password: "" # 從 GF_DATABASE_PASSWORD 環境變數取得

  users:
    allow_sign_up: false
    allow_org_create: true
    auto_assign_org: true
    auto_assign_org_id: 1
    auto_assign_org_role: Viewer
    verify_email_enabled: false
    default_theme: dark
    viewers_can_edit: false
    editors_can_admin: false

  auth:
    disable_login_form: true

  # GCP IAP 設定
  auth.jwt:
    enabled: true
    auto_sign_up: true
    header_name: "X-Goog-Iap-Jwt-Assertion"
    username_claim: "email"
    email_claim: "email"
    jwk_set_url: "https://www.gstatic.com/iap/verify/public_key-jwk"
    expect_claims: '{"iss": "https://cloud.google.com/iap"}'
    skip_org_role_sync: true

  auth.google:
     enabled: false
  ...

這裡最關鍵的是 auth.jwt 部分,它讓 Grafana 能夠識別並信任來自 GCP IAP 的 JWT 令牌,從而實作無縫的身分驗證。玄貓在實際佈署時發現,若未正確設定 expect_claims,可能導致身分驗證失敗,因此特別留意這個引數。

使用 Helm 佈署 Grafana

接下來,使用 Terraform 的 Helm Provider 來佈署 Grafana:

resource "helm_release" "grafana" {
  name             = "grafana"
  repository       = "https://grafana.github.io/helm-charts"
  chart            = "grafana"
  version          = "8.8.1" # 對應 Grafana 版本 11.4.0
  wait             = false
  atomic           = true
  force_update     = false
  namespace        = "monitoring"
  create_namespace = true

  values = [file("${path.module}/file/values.yaml")]

  # 其他設定...
}

values.yaml 中,可以進一步自訂 Grafana 的設定,例如資源限制、持久化儲存、服務帳號等。玄貓建議在生產環境中至少設定適當的資源請求與限制,以確保 Grafana 的穩定執行。

透過 Gateway 暴露服務

目前 Grafana Helm 圖表 對 Gateway 路由的支援較為有限。為了實作更靈活的設定,我們佈署額外的 Gateway Helm

resource "helm_release" "gateway" {
  name         = "grafana-gateway"
  namespace    = "monitoring"
  chart        = "${path.module}/grafana-gateway"
  wait         = false
  force_update = false
  atomic       = true

  # 其他設定...
}

關鍵在於 GCPBackendPolicy,它為 Gateway 新增 GCP IAP 支援:

spec:
  default:
    iap:
      enabled: true
      oauth2ClientSecret:
        name: "grafana-oauth-secret"

OAuth 客戶端則透過 Terraform 建立,並傳遞給 Gateway Helm

resource "google_iap_client" "grafana" {
  display_name = "Grafana"
  brand        = "projects/XXXXXXXXXXXX/brands/XXXXXXXXXXXX"
}

在實際佈署中,玄貓發現 brand ID 是最容易出錯的部分。確保使用正確的 GCP 專案 ID 和品牌 ID,可以透過 gcloud CLI 查詢:

gcloud iap oauth-brands list --project=YOUR_PROJECT_ID

授權存取設定

當 Kubernetes 資源應用後,GCP 會在背景自動建立一個 Backend Service。要授權存取,需要將適當的主體(使用者或群組)附加到這個 Backend Service。

目前,由於 Backend Service 的名稱是動態生成的,沒有直接的 Terraform 資源可以優雅地處理這個問題。不過,我們可以使用以下變通方法:

resource "google_iap_web_backend_service_iam_member" "member" {
  project = var.project
  web_backend_service = (
    reverse(
      split("/",
        [for x in split(", ",
          data.kubernetes_resource.gateway.object.metadata.annotations["networking.gke.io/backend-services"],
        ) : x if strcontains(x, "monitoring-grafana-80")][0]
      )
    )[0]
  )
  role   = "roles/iap.httpsResourceAccessor"
  member = "group:grafana-accessors@example.com"
}

這段程式碼從 Gateway 資源的註解中提取 Backend Service 的名稱,並為指定的群組授予存取許可權。這是一個技巧性的解決方案,但在 Terraform 中能有效運作。

實際應用情境與最佳實踐

在協助多家企業實施此架構後,玄貓總結了一些實用的最佳實踐:

1. 分層授權策略

使用 IAP 的優勢在於可以實施精細的授權策略。建議將使用者分為不同層級:

  • 管理員:完整的 Grafana 管理許可權,限制特定信任裝置存取
  • 開發團隊:編輯許可權,可以建立和修改儀錶板,但無法變更系統設定
  • 營運團隊:檢視許可權,僅能檢視儀錶板

2. 備用存取機制

即使 IAP 極為可靠,仍建議設定緊急存取機制:

resource "kubernetes_service" "grafana_internal" {
  metadata {
    name      = "grafana-internal"
    namespace = "monitoring"
    annotations = {
      "cloud.google.com/neg" = "{\"ingress\": false}"
    }
  }
  spec {
    selector = {
      "app.kubernetes.io/name" = "grafana"
    }
    port {
      port        = 3000
      target_port = 3000
    }
    type = "ClusterIP"
  }
}

這個內部服務允許在極端情況下,透過 kubectl port-forward 直接存取 Grafana。

3. 監控 IAP 本身

確保 IAP 運作正常同樣重要。建立專門的監控儀錶板追蹤:

  • IAP 授權拒絕率
  • 身分驗證延遲時間
  • 可疑存取模式

常見問題排解

在實施過程中,玄貓經常遇到以下問題:

1. JWT 驗證失敗

症狀:使用者能透過 IAP,但無法登入 Grafana 解決方案:確認 Grafana 的 auth.jwt 設定正確,特別是 expect_claims 欄位

2. Backend Service 未正確建立

症狀:Gateway 佈署成功,但無法透過 IAP 存取 解決方案:檢查 GKE Gateway Controller 日誌,確保 GCP 服務帳號有足夠許可權

3. OAuth 客戶端重定向 URI 錯誤

症狀:身分驗證迴圈或 “redirect_uri_mismatch” 錯誤 解決方案:確保 OAuth 客戶端設定的重定向 URI 與 Gateway 主機名稱比對

隨著 Kubernetes Gateway API 的不斷發展,玄貓預見未來整合會變得更加簡便。特別是以下幾個方向值得關注:

  1. 多叢集 Gateway 支援:跨多個 GKE 叢集統一管理 Grafana 存取
  2. Policy Controller 整合:使用 GKE Policy Controller 自動強制執行 IAP 設定
  3. Workload Identity Federation:進一步增強與 Grafana 之間的身分整合

透過 GCP IAP、Gateway API 和 Terraform 的組合,我們不僅解決了當前的安全需求,也為未來的擴充套件奠定了堅實基礎。這種方法不僅適用於 Grafana,還可以擴充套件到其他需要安全存取的 Kubernetes 服務。

在數位轉型的時代,安全不再是事後考量,而是架構設計的核心要素。希望這篇能幫助您在保障安全的同時,充分發揮 Grafana 的強大監控能力。