Preface
之前於 11個保護你 Kubernetes 集群的技巧與觀念(上) 中有不斷的強調加密連線 SSL
的重要性。最簡單的範例就是如果今天有一個需求需要於 kubernetes
叢集中部屬一個網頁服務供外部存取時,預設情況下我們都會走 HTTP(80)
, 然而在安全意識崛起的時代,基本上一個網頁服務沒有 HTTPS(443)
的存取方式時,大家對於是否要在該網站輸入一些機密資料都會變得小心翼翼甚至拒絕使用。
為了讓該網頁伺服器提供 HTTPS
的連線方式,我們必須要提供一組憑證,且該憑證的簽署方是要被信任的。
在這個情況下,我們可以像第三方廠商購買憑證的方式來取得一個合法的憑證,並且將該憑證部屬在我們的網頁伺服器上,這樣就可以順利的提供 HTTPS
安全連線方式。
但是更多時候,很多人都只有買域名卻沒有買憑證,解決方法都是透過免費的 Let's Encrypt
去簽署憑證,然而 Let's Encrypt
本身期限很短,需要定期更新來確保憑證的有效性。
這個過程其實不會太難,基本上就是申請 Let's Encrypt
服務,然後想辦法定期更新該憑證即可。
然而這個步驟有沒有辦法變得很簡單? 可以讓叢集部屬人員可以用非常簡單的方式來達成這件事情?
本文會跟大家介紹 Cert-Manager
這套用來管理憑證相關的服務,如何透過這個服務可以輕鬆的在 kubernetes
內把上述的事情自動處理完畢,讓你只需要專心處理自己的應用程式即可。
Component
如同前言所述,接下來的文章都會針對 Cert-Manager
這個套件作介紹,大致上會講述如果要完成前述所需要的自動更新憑證等相關事情,於 Cert-Manager
中實際上會是怎麼完成的。同時身為系統管理員,需要準備哪些相關的資訊來處理。
Issuers
Cert-Manager
裡面總共有兩種類型的 Issuers
, 分別是 Issuer
以及 ClusterIssuers
. 這兩個的差異只有在於其影響的 kubernetes namespace
的範圍而以,其餘的功能都一樣. 所以接下來的文章將會使用 Issuers
來當做範例介紹.
Issuers
代表的是一個能夠從會簽署 x509
憑證的授權單位所取得憑證的一個資源,最
目前 Issuers
支援的 DNS Challenge
有兩種,分別是 HTTP-01
以及 DNS-01
.
常見的後端代表就是想辦法從 Let's Encrypt
取得憑證.
- Issuers 是透過
Kubernetes CustomResourceDefinition(CRD)
所自定義的新資源- 這意味你可以透過
kubectl get issuers
來檢視相關的設定
- 這意味你可以透過
- 對於每個
kubernetes
叢集來說,系統內至少要部屬一個Issuers
來完成憑證的功能 - Issuers 目前提供了四種後端接口, 這邊會稍微介紹一下彼此的差異,最後說明本文會基於哪種後端接口來展示
ACME
這個類型應該是最常見也是最多人在使用的,透過 Automated Certificate Management Environment(ACME)
這個協定與已知的 CA
溝通來取得憑證。
與 CA
的溝通過程中,最困難也是最核心的部份就是 DNS Challenge
, 使用者必須要向 CA
證明自己擁有想要簽署憑證的域名。
目前 Issuers
支援的 DNS Challenge
有兩種,分別是 HTTP-01
以及 DNS-01
.
HTTP-01
顧名思義,就是透過 HTTP
的方式來驗證是否擁有該網域,要採用這種方式基本上要滿足幾個條件
- 首先針對想要簽署的網域進行設定,譬如將
test.hwchiu.com
指向到一個IP
地址 - 該
CA
會嘗試透過HTTP
的方式去連線test.hwchiu.com
. 其會預期得到一個對應的HTTP
回應,該回應的格式可以參考 HTTP Challenge
透過上述兩個條件,我們可以觀察到幾個現象
HTTP-01
並不支援wildcard
的domain
, 因為要透過DNS
查詢對應的 IP 來取得 HTTP 回應- 為了讓
HTTP
封包可以順利的連線,通常客戶端都會需要有一個可存取的對外 IP 地址- 當然這部分你若熟悉網路,要透過
DNAT
的方式將封包導入到私有 IP 的網頁伺服器上也是沒有問題
- 當然這部分你若熟悉網路,要透過
大部分人的人在使用 Let's Encrypt
時基本上都是透過 HTTP-01
這個方式來驗證,因為使用上其實相對簡單,基本的那些網頁伺服器以及對應的 HTTP 格式都有相關的軟體幫忙處理,大幅度簡化了管理員的設定。
本篇文章並不打算使用這個方式,因為過於簡單沒有什麼好設定的,所以我們來看看另外一種挑戰, DNS-01
.
DNS-01
相對於 HTTP-01
透過 HTTP
連線來驗證是否有該網域的擁有權, DNS-01
則是透過創造一個預期內容的 TXT
紀錄來證明你擁有該網域的擁有權。為了達成上述的事項,Issuers
目前有支援下列的 DNS
供應商來幫忙完成這個自動創建 TXT
紀錄的需求
- Google CloudDNS
- Amazon Route53
- Cloudflare
- Akamai FastDNS
- RFC2136
- ACME-DNS
透過 DNS-01
的機制就可以打破 HTTP-01
的限制,客戶端本身即使位於私有網路內也是沒有問題的。
而因為我自己的網域目前是放在 Cloudflare
上,因此本文就會採取 DNS-01
的方式並且使用 Cloudflare
作為測試的 DNS
提供者。
CA
相對於上述的 ACME
的使用方式, CA
類型的 Issuers
則是會使用一組事先準備好的簽署金鑰來發行憑證。這組簽署憑證必須要以 kubernetes secret
的形式存於 kubernetes cluster
中。
舉例來說,你可以先透過 openssl
的方式產生對應的檔案,並且把該檔案加入到 kubernetes secret
內,最後透過 CA
類型的 Issuers
來使用。
Valut
如果你系統內有安裝 HashCrop Valut 的話,可以透過這個類型的 Issuers
來與 Valut
溝通。
Self-Signed
這個選項目前會用到的機會不多,大致上有兩種情境
- 想要透過
kubernetes
打造一個Public Key Infrastructur (PKI)
架構 - 搭配後續會介紹的
Certificate
作為一個root CA
供CA
型態的Issuers
使用 -. 可以參考 resource-validation-webhook 的使用範例來瞭解如何搭配使用
想要更加深入的瞭解這些選項可以直接到官網閱讀相關的資訊以及範例使用
Certificate
看完了 Issuers
之後,我們來看看第二個也是最後一個透過 kubernetes CRD
所建立的物件,Certificate
.
Certificate
就如同其名稱一樣,代表的是一個憑證,其會嘗試使用 Issuers
來獲得對應的憑證與金鑰,當成功取得對應的資源後,就會將該對憑證與金鑰的資訊放到 kubernetes secret
裡面。
Certificate
以及產生的 kubernetes secret
都跟 namespace
有綁定關係,所以對於不同的 namespace
的應用程式若想要擁有這張簽署的憑證,則都需要創立一個對應的 certificate
來使用。
Summary
基本上整個 cert-manager
的關係都是由 Issuers/Certificate
所組成的,當然也有部屬安裝 cert-manager
時會佈署的 kubernetes deployment
, 該 deployment
作為 Issuers/Certificate
之間的橋樑, 定期確認 Certificate
的合法性並且當憑證快要到期時還會嘗試更新。
透過這些資源的整合,Cert-Manager
就能夠於 kubernetes
叢集內提供可以從 Let's Encrypt
取得憑證並且定期自動更新以確保憑證不會過期的憑證供我們其他的應用程式使用。
Setup
看了上述的基本概念介紹後,我們接下來就要手把手的嘗試透過 Cert-Manager
來取得一個憑證,這中間的過程大概如下
- 部屬
cert-manager
相關的服務到kubernetes
叢集中 - 部署
Issuers
- 部屬
Certificate
- 透過檢查
Kubernetes Secret
, 確認是否有拿到可用的憑證
整個系統的架構圖如下,基本上我們會有 Issuers/Certificate
基本元件,同時為了滿足 DNS-01
, 這邊會需要一個額外的 kubernetes secret
供 Issuers
使用。此外 Certificate
最後則會產生一個 kubernetes secret
的合法憑證。
Cert-Manager
安裝 Cert-Manager
的部分有兩種安裝方式,第一種就是最基本也是最熟悉的,根據自己的環境去部屬事先提供好了 Yaml
檔案。
這部分可以參考官方 Github 上面的檔案,基本上會根據叢集是否有啟用 RBAC (Role-Based Access Control)
來決定要部屬那個檔案。
另外一個安裝方式則是透過 Package
的概念來安裝,這邊採用的是 Helm
的方式來安裝,透過 Helm
的打包及管理,套件管理員將該服務所需要的 Yaml
都收集完畢並且打包,使用者只需要透過 Helm
的指令很簡單輕鬆的直接安裝完畢。
這邊我就直接使用 Helm
的方式來安裝 Cert-Manager
.
hwchiu➜~» helm install \
--name cert-manager \
--namespace kube-system \
stable/cert-manager
想要安裝 Helm
可以參考官網的教學
安裝完畢後,可以透過 kubectl
的指令去確認是否需要的 deployment
有運行起來
hwchiu➜~» kubectl -n kube-system get deployment/cert-manager
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
cert-manager 1 1 1 1 5d
Issuers
當 cert-manager
正確安裝後,我們接下來要開始處理 Issuers
, 再我的範例之中,我採用的是 clusterIssuers
來建立一個供所有 namespace
都可以使用的 Issuers
.
此外,由於我要採用的是 DNS-01
的方式來驗證網域的擁有權,同時我的 DNS
提供商是 CloudFlare
. 因此我必須要先創立一個 kubernetes sercet
讓我的 Issuers
有能力操控我的 CloudFlar
.
CloudFlare
這部分會依據你的 DNS
供應商有不同的做法,詳細的可以參考 Cert-Manager Supported DNS01 Providers
- 以
CloudFlare
爲範例,首先到你網域的控制台中去取得你的Public Key
. 可參考下圖 - 得到該
Public Key
後,接下來要轉成Base64
並且放到kubernetes sercet
.
假設該 Public Key
是 12345678910
, 執行下列函式得到對應的 Base64
編碼結果
echo -n "12345678910" | base64
接下來我們要透過 yaml
的方式創建 kubernetes secret
.
打開文件 sercet.yml
並且貼上下列內容
apiVersion: v1
kind: Secret
metadata:
name: cloudflare
namespace: kube-system
data:
api: MTIzNDU2Nzg5MTA=
創建好上述的 yaml
檔案後,我們就透過 kubectl create -f secret.yml
ClusterIssuers
接下來我們就要來創建我們的 ClusterIssuers
了,
這邊我們直接來看相關的 yaml
內容,有一些東西要注意
- 我們在
Issuer
內目前使用了acme
的方式來認證 - server 的部分,一般來說會先用
staging
的內容來確認所有的資訊都正常且可以運作,正式環境中就會切換成v02
的版本來使用 - 於
acme
裡面,我們使用dns01
的方式來驗證- 我們採用的是
cloudflare
的方式, 這邊就是填寫你擁有網域的信箱 - 接下來要使用剛剛創立的
kubernetes secret
,name/key
就對應到kubernetes secret
內的metadata:name
以及data/api
.
- 我們採用的是
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: cert-demo
namespace: default
spec:
acme:
#server: https://acme-v02.api.letsencrypt.org/directory
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected]
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: cert-demo
# ACME DNS-01 provider configurations
dns01:
# Here we define a list of DNS-01 providers that can solve DNS challenges
providers:
- name: cf-dns
cloudflare:
email: [email protected]
# A secretKeyRef to a cloudflare api key
apiKeySecretRef:
name: cloudflare
key: api
創建上述 ClusterIssuers
的檔案之後,可以透過 kubectl describe clusgerissuer/cert-demo
去確認狀態,譬如
.....
Status:
Acme:
Uri: https://acme-staging-v02.api.letsencrypt.org/acme/acct/7037688
Conditions:
Last Transition Time: 2018-09-30T16:51:03Z
Message: The ACME account was registered with the ACME server
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>
確認這邊是 True/Ready
之後, 對於 Issuers
的事情就告一段落了
Certificates
正式進入 Yaml
之前,有幾個重要的事情要先注意
- 每個
Certificate
可以設定Subject Alternative Names(SANs)
, 亦即可以設定多個domain name
. - 針對設定的所有
domain name
,Issuers
都必須要去處理。實際上在Issuers
的設定中,可以設定多種方法,甚至是同時支援多個DNS
供應商。 - 因此對應
Certificae
中的每個domain name
, 都要指派對應Issuers
的可以用來處理該Certificate
的物件,譬如dns-01
內的某種dns
供應商。 - 每個
Certificate
最後會把得到的憑證資訊都寫入到Kubernetes Secret
的資源內,所以我們必須要設定我們期望的名稱
有了上述的概念後,我們就可以來看一下 Yaml
的內容了
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: demo-certi
spec:
secretName: demo-certi-tls
issuerRef:
name: cert-demo
kind: ClusterIssuer
dnsNames:
- test.hwchiu.com
acme:
config:
- dns01:
provider: cf-dns
domains:
- test.hwchiu.com
這個範例中,我們要先指定該 Certificate
要使用的 Issuers
. 這部分要透過 issuerRef
來指定剛剛創立的 ClusterIssuers
.
接下來透過 secretName
去指定之後創立的 kubernetes secret
的名稱
最後要處理子域名的問題,在這個範例中只有一個,也就是 test.hwchiu.com
.
所以在 acme
的部分就直接使用先前創立的 ClusterIssuers
內所描述的 DNS01
方式去進行認證
當這一切都準備就緒後就透過 kubectl apply
給部屬到叢集內。
接下來我們可以觀察整體運行的狀況,我們透過 kubctl descriBe certificate demo-creti
可以看一下 certificae
當前的狀態。
一個運行的範例如下
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CreateOrder 16s cert-manager Created new ACME order, attempting validation...
Normal IssueCert 15s cert-manager Issuing certificate...
Normal CertObtained 13s cert-manager Obtained certificate from ACME server
Normal CertIssued 13s cert-manager Certificate issued successfully
當一切都完畢後,就可以透過 kubernetes get secret
看到 cert-manager
已經幫忙創立了一個 kubernetes secret
,裡面已經包含了你需要的憑證。
如果有使用 ingress
相關的,可以直接在 Ingress
那邊透過 tls
的設定直接使用該 secret
幫你的 Ingress
套上這個憑證囉
Summary
使用任何的解決方案前,都要先思考一下到底自己遇到的是什麼問題,再從這個問題延伸去尋找方法來處理。接下來為了要更能夠熟悉該方法所帶來的好處以及壞處,就必須要花時間去瞭解其架構以及原理,最後透過動手操作的方式來實際體驗是否真的能夠解決問題。
本文的出發點很簡單,就是想要有一個解決方案能夠幫忙處理 Ingress
的憑證,然後因為沒錢所以希望使用 Let's Encrypt
的服務,但是礙於該服務所簽署的憑證有效期限很短,所以需要一直更新。
基於這個問題的情況下,我開始研究了 Cert-Manager
,並且透過一系列的操作與體驗來理解如何使用 Cert-Manager
解決我的問題。
最後也希望大家都能夠保持類似的態度與想法來解決每一個問題,藉由這些步驟能夠更深刻的體會每個解決方案背後的原理以及設計思緒,將這些概念融入自己的思想之中以不停的成長學習。