本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
為 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 模板,以便您可以自行構建映象)。