本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

為 Kubernetes 服務提供強大、簡單的 SSL

大家好,我是 Evan Brown (@evandbrown),我在 Google Cloud Platform 的解決方案架構團隊工作。我最近寫了一篇文章和一個教程,關於如何在 Kubernetes 上使用 Jenkins 自動化 Docker 和 GCE 映象構建過程。今天我將討論如何使用 Kubernetes 服務和秘密來為 Jenkins Web UI 新增 SSL。閱讀本文後,您將能夠為您的公共 HTTP Kubernetes 服務新增 SSL 終止(以及 HTTP->HTTPS 重定向 + 基本身份驗證)。

最初

本著最小可行性的精神,我構建的第一個 Jenkins-on-Kubernetes 版本非常基礎但功能齊全。

  • Jenkins Leader 只是一個 Pod 中的單個容器,但它由複製控制器管理,因此如果它失敗,它會自動重新生成。
  • Jenkins Leader 暴露兩個埠 - TCP 8080 用於 Web UI,TCP 50000 用於構建代理註冊 - 這些埠作為具有公共負載均衡器的 Kubernetes 服務提供。

這是第一個版本的視覺效果

這行得通,但我對此有幾個問題。首先,預設的 Jenkins 安裝中未配置身份驗證。在您連線並配置身份驗證之前,Jenkins Leader 暴露在公共網際網路上,任何人都可以訪問。而且由於沒有加密,配置身份驗證只是一種象徵性的姿態。我們需要 SSL,而且現在就需要!

做你所熟悉的

有幾毫秒我考慮直接在 Jenkins 上配置 SSL。我以前從未做過,我開始懷疑它是否會像在Nginx上配置 SSL 那樣簡單,而 Nginx 是我有所經驗的。我完全支援學習新事物,但這似乎是一個不重複造輪子的絕佳場合:Nginx 上的 SSL 既簡單又文件齊全(其反向代理功能也是如此),而 Kubernetes 的核心理念就是透過編排和組合容器來構建功能。讓我們使用 Nginx,並新增 Nginx 簡化的一些額外功能:HTTP->HTTPS 重定向和基本訪問身份驗證。

作為 Nginx 服務的 SSL 終止代理

我首先編寫了一個Dockerfile,它繼承自標準的 Nginx 映象,複製了一些 Nginx 配置檔案,並添加了一個自定義入口點(start.sh)。入口點指令碼檢查一個環境變數(ENABLE_SSL)並相應地啟用正確的 Nginx 配置(這意味著未加密的 HTTP 反向代理是可能的,但這違背了目的)。如果啟用了基本訪問身份驗證(ENABLE_BASIC_AUTH 環境變數),指令碼還會配置它。

最後,start.sh 評估 SERVICE_HOST_ENV_NAME 和 SERVICE_PORT_ENV_NAME 環境變數。這些變數應設定為您要代理到的 Kubernetes 服務的環境變數名稱。在此示例中,我們的 Jenkins leader 的服務被巧妙地命名為 jenkins,這意味著叢集中的 Pod 將看到名為 JENKINS_SERVICE_HOST 和 JENKINS_SERVICE_PORT_UI 的環境變數(即 8080 埠對映到 Jenkins leader 上的埠)。SERVICE_HOST_ENV_NAME 和 SERVICE_PORT_ENV_NAME 只是引用特定場景中要使用的正確服務,從而允許映象在部署中通用使用。

定義控制器和服務

與此示例中的所有其他 Pod 一樣,我們將使用複製控制器部署 Nginx,允許我們進行伸縮,並自動從容器故障中恢復。以下摘錄自示例應用中的完整描述符顯示了 Pod 規範的一些相關部分

  spec:

    containers:

      -

        name: "nginx-ssl-proxy"

        image: "gcr.io/cloud-solutions-images/nginx-ssl-proxy:latest"

        env:

          -

            name: "SERVICE\_HOST\_ENV\_NAME"

            value: "JENKINS\_SERVICE\_HOST"

          -

            name: "SERVICE\_PORT\_ENV\_NAME"

            value: "JENKINS\_SERVICE\_PORT\_UI"

          -

            name: "ENABLE\_SSL"

            value: "true"

          -

            name: "ENABLE\_BASIC\_AUTH"

            value: "true"

        ports:

          -

            name: "nginx-ssl-proxy-http"

            containerPort: 80

          -

            name: "nginx-ssl-proxy-https"

            containerPort: 443

Pod 將有一個服務,將 TCP 80 和 443 暴露給公共負載均衡器。以下是服務描述符(也可在示例應用中找到)

  kind: "Service"

  apiVersion: "v1"

  metadata:

    name: "nginx-ssl-proxy"

    labels:

      name: "nginx"

      role: "ssl-proxy"

  spec:

    ports:

      -

        name: "https"

        port: 443

        targetPort: "nginx-ssl-proxy-https"

        protocol: "TCP"

      -

        name: "http"

        port: 80

        targetPort: "nginx-ssl-proxy-http"

        protocol: "TCP"

    selector:

      name: "nginx"

      role: "ssl-proxy"

    type: "LoadBalancer"

這是部署了 SSL 終止代理後的概述。請注意,Jenkins 不再直接暴露在公共網際網路上。

現在,Nginx Pod 如何獲取這些絕密的 SSL 金鑰/證書和 htpasswd 檔案(用於基本訪問身份驗證)?

保持秘密,保持安全

Kubernetes 有一個API 和 Secret 資源。Secret“旨在儲存敏感資訊,例如密碼、OAuth 令牌和 SSH 金鑰。將這些資訊放入 Secret 中比直接放入 Pod 定義或 Docker 映象中更安全、更靈活。”

您可以透過 3 個簡單步驟在叢集中建立 Secret:

對您的秘密資料進行 Base64 編碼(即 SSL 金鑰對或 htpasswd 檔案)

$ cat ssl.key | base64  
   LS0tLS1CRUdJTiBDRVJUS...

建立一個描述您的 Secret 的 JSON 文件,並新增 Base64 編碼的值

  apiVersion: "v1"

  kind: "Secret"

  metadata:

    name: "ssl-proxy-secret"

    namespace: "default"

  data:

    proxycert: "LS0tLS1CRUd..."

    proxykey: "LS0tLS1CR..."

    htpasswd: "ZXZhb..."

建立秘密資源

$ kubectl create -f secrets.json

要從容器訪問 Secret,請在 Pod 規範中將其指定為卷掛載。以下是我們之前看到的Nginx 代理模板中的相關摘錄:

  spec:

    containers:

      -

        name: "nginx-ssl-proxy"

        image: "gcr.io/cloud-solutions-images/nginx-ssl-proxy:latest"

        env: [...]

        ports: ...[]

        volumeMounts:

          -

            name: "secrets"

            mountPath: "/etc/secrets"

            readOnly: true

    volumes:

      -

        name: "secrets"

        secret:

          secretName: "ssl-proxy-secret"

定義了一個指向 ssl-proxy-secret secret 資源的 secret 型別的卷,然後將其掛載到容器的 /etc/secrets 中。前面示例中的 secrets 規範定義了 data.proxycert、data.proxykey 和 data.htpasswd,因此我們會看到這些檔案(經過 Base64 解碼)出現在 /etc/secrets/proxycert、/etc/secrets/proxykey 和 /etc/secrets/htpasswd 中,供 Nginx 程序訪問。

現在,齊心協力

我每天都會有“容器和 Kubernetes 有趣又酷!”的時刻,可能每天都有。我開始更頻繁地有“容器和 Kubernetes 非常有用且強大,它們透過幫助我輕鬆完成重要事情來為我的工作增值”的時刻。這個使用 Nginx 的 SSL 終止代理示例絕對屬於後者。我無需浪費時間學習使用 SSL 的新方法。我能夠以可重用的方式,快速地(從構想到工作大約花了 2 小時)使用眾所周知的工具解決我的問題。

檢視完整的使用 Jenkins、Packer 和 Kubernetes 自動化映象構建儲存庫,瞭解 SSL 終止代理如何在實際叢集中使用,或深入瞭解nginx-ssl-proxy 儲存庫中代理映象的詳細資訊(附帶 Dockerfile 和 Packer 模板,以便您可以自行構建映象)。