本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
適用於 Kubernetes 服務的強大、簡單 SSL
嗨,我是 Evan Brown (@evandbrown),我在 Google Cloud Platform 的解決方案架構團隊工作。我最近撰寫了一篇文章和教學,關於在 Kubernetes 上使用 Jenkins 自動化 Docker 和 GCE 映像檔建置流程。今天我將討論如何使用 Kubernetes 服務和密鑰為 Jenkins 網頁 UI 新增 SSL。閱讀本文後,您將能夠為您的公開 HTTP Kubernetes 服務新增 SSL 終止(和 HTTP->HTTPS 重新導向 + 基本驗證)。
起初
秉持最低可行性的精神,我建置的第一個 Jenkins-on-Kubernetes 版本非常基本但功能齊全
- Jenkins 領導者只是一個 Pod 中的單一容器,但由複寫控制器管理,因此如果它失敗,它會自動重新產生。
- Jenkins 領導者公開兩個連接埠 - TCP 8080 用於網頁 UI,TCP 50000 用於建置代理程式註冊 - 這些連接埠以 Kubernetes 服務的形式提供,並具有公用負載平衡器。
以下是第一個版本的視覺呈現
這可以運作,但我對此有一些問題。首先,預設 Jenkins 安裝中未設定驗證。領導者位於公共網際網路上,任何人都可以存取,直到您連線並設定驗證為止。由於沒有加密,因此設定驗證只是一種象徵性的姿態。我們需要 SSL,而且我們現在就需要它!
做您知道的事
在幾毫秒內,我考慮嘗試直接在 Jenkins 上讓 SSL 運作。我以前從未做過,我發現自己在想,它是否會像在 Nginx 上使用 SSL 一樣直接,這是我有經驗的事情。我非常贊成學習新事物,但這似乎是一個不重新發明輪子的好地方:Nginx 上的 SSL 很直接且有完善的文件記錄(其反向 Proxy 功能也是如此),而 Kubernetes 完全是關於透過協調和組合容器來建置功能。讓我們使用 Nginx,並新增一些 Nginx 使其變得簡單的額外功能:HTTP->HTTPS 重新導向和基本存取驗證。
作為 Nginx 服務的 SSL 終止 Proxy
我首先組合了一個 Dockerfile,它繼承自標準 Nginx 映像檔,複製了一些 Nginx 組態檔,並新增了一個自訂進入點 (start.sh)。進入點腳本檢查環境變數 (ENABLE_SSL) 並相應地啟動正確的 Nginx 組態(表示未加密的 HTTP 反向 Proxy 是可能的,但這會破壞目的)。如果啟用了基本存取驗證(ENABLE_BASIC_AUTH 環境變數),腳本也會設定它。
最後,start.sh 評估 SERVICE_HOST_ENV_NAME 和 SERVICE_PORT_ENV_NAME 環境變數。這些變數應設定為您要 Proxy 到的 Kubernetes 服務的環境變數名稱。在此範例中,我們的 Jenkins 領導者的服務巧妙地命名為 jenkins,這表示叢集中的 Pod 將看到名為 JENKINS_SERVICE_HOST 和 JENKINS_SERVICE_PORT_UI 的環境變數(連接埠 8080 對應到 Jenkins 領導者上的連接埠)。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 終止 Proxy 後的概觀。請注意,Jenkins 不再直接公開於公共網際網路
現在,Nginx Pod 如何取得超級機密的 SSL 金鑰/憑證和 htpasswd 檔案(用於基本存取驗證)?
保持機密,保持安全
Kubernetes 具有API 和 Secrets 資源。Secrets「旨在保存敏感資訊,例如密碼、OAuth 權杖和 ssh 金鑰。將此資訊放入 Secret 中比直接將其逐字放入 Pod 定義或 Docker 映像檔中更安全且更靈活。」
您可以在 3 個簡單步驟中在您的叢集中建立 Secret
Base64 編碼您的 Secret 資料(即 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..."
建立 Secrets 資源
$ kubectl create -f secrets.json
若要從容器存取 Secret,請在您的 Pod 規格中將它們指定為磁碟區掛載。以下是我們稍早看到的 Nginx Proxy 範本中的相關摘錄
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 終止 Proxy 範例絕對是後者之一。我沒有浪費時間學習使用 SSL 的新方法。我能夠以可重複使用的方式,快速地使用眾所周知的工具解決我的問題(從想法到運作大約花了 2 個小時)。
查看完整的使用 Jenkins、Packer 和 Kubernetes 自動化映像檔建置儲存庫,以了解 SSL 終止 Proxy 如何在真實叢集中使用,或深入研究 nginx-ssl-proxy 儲存庫中的 Proxy 映像檔詳細資訊(包含 Dockerfile 和 Packer 範本,因此您可以自行建置映像檔)。