云原生技巧 :在本地 K8s 中輕松部署自簽 TLS 證書(shū)
隨著互聯(lián)網(wǎng)的飛速發(fā)展,安全性日益成為我們關(guān)注的焦點(diǎn)。HTTPS 已從一項(xiàng)奢侈的技術(shù)逐漸成為現(xiàn)代網(wǎng)絡(luò)交互的標(biāo)準(zhǔn)。它不僅僅是保護(hù)信息的重要工具,更是實(shí)現(xiàn)信任和品質(zhì)的象征???。當(dāng)你在本地的 K8s 開(kāi)發(fā)環(huán)境中遇到需要使用 HTTPS 來(lái)進(jìn)行訪問(wèn),又該如何為其配置 TLS/SSL 證書(shū)呢?
今天,讓我們一起揭秘如何在 K8s 環(huán)境中輕松自簽證書(shū),為你的本地開(kāi)發(fā)環(huán)境帶來(lái)安全性的提升!
一、Preparation
1. Install Kind
在生成 Kind 的配置文件時(shí),我利用 Kind 的 extraPortMapping 配置選項(xiàng)將端口從主機(jī)轉(zhuǎn)發(fā)到節(jié)點(diǎn)上運(yùn)行的入口控制器。
它的作用是允許本地主機(jī)通過(guò)端口 80/443 向 Ingress 控制器發(fā)出請(qǐng)求。
cat << EOF > cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: local
nodes:
- role: control-plane
image: kindest/node:v1.25.3
extraPortMappings:
- containerPort: 80
hostPort: 80
listenAddress: 127.0.0.1
- containerPort: 443
hostPort: 443
listenAddress: 127.0.0.1
EOF
使用生成的配置,在本地安裝 K8s 集群。
kind create cluster --config cluster.yaml
?? 因?yàn)榕渲昧?extraPortMappings 的原因,如果需要在本地部署多套 K8s 集群,必須調(diào)整端口,又或者是去除 extraPortMappings 這個(gè)配置項(xiàng)。
2. Install Traefik
將 Traefik Labs 的圖表倉(cāng)庫(kù)添加到 Helm。
helm repo add traefik https://traefik.github.io/charts
helm repo update
這里我們給 websecure 設(shè)置主機(jī)端口為 443,這是為了確保傳入的 HTTPS 流量可以被正確地路由到 Traefik。
helm upgrade -i traefik \
--set ports.traefik.expose=true \
--set ports.websecure.hostPort=443 \
--set-string service.type=ClusterIP \
--namespace traefik \
--create-namespace \
traefik/traefik
3. Install Dnsmasq
雖然 /etc/hosts 對(duì)于簡(jiǎn)單的域名到 IP 地址的映射是很有用的,但它是靜態(tài)的,而且不支持通配符或模式匹配,因此你不能為一個(gè)域名的所有子域設(shè)置相同的 IP 地址。
Dnsmasq[1] 它是一個(gè)輕量級(jí)的 DNS 正向和反向緩存服務(wù)器,同時(shí)也可以為 DHCP 功能提供服務(wù)。它被設(shè)計(jì)為易于配置和使用,并且它通常用于小型網(wǎng)絡(luò)環(huán)境,特別是那些需要簡(jiǎn)單的 DHCP 和 DNS 服務(wù)的地方。
可以說(shuō) Dnsmasq 提供了一個(gè)更強(qiáng)大、靈活且集中的解決方案,以下是安裝方法。
brew install dnsmasq
4. Install CFSSL/mkcert
為本地生成自簽名的 SSL/TLS 證書(shū)有很多的工具,我在這里就分享兩種,每種工具都有其特點(diǎn)和最佳用途,大家可以根據(jù)自己的需求和偏好來(lái)選擇。
第一種:CFSSL[2],以下是安裝方法。
brew install cfssl
第二種:mkcert[3],以下是安裝方法。
brew install mkcert
小結(jié)
至此,我們已經(jīng)完成了在本地 K8s 開(kāi)發(fā)環(huán)境中準(zhǔn)備的基礎(chǔ)設(shè)施工作。通過(guò) Kind,我們成功地搭建了一個(gè)本地的 Kubernetes 集群;通過(guò) Helm 和 Traefik,我們?yōu)榧号渲昧藦?qiáng)大的路由和反向代理功能;最后,通過(guò) Dnsmasq,我們提供了一個(gè)靈活的本地 DNS 解決方案,替代了傳統(tǒng)的 /etc/hosts 方法。這些都為我們接下來(lái)進(jìn)行 TLS/SSL 證書(shū)配置打下了堅(jiān)實(shí)的基礎(chǔ)。
現(xiàn)在,我們將進(jìn)入下一個(gè)階段,真正探討如何在 K8s 開(kāi)發(fā)環(huán)境中配置自簽證書(shū),以實(shí)現(xiàn) HTTPS 的安全訪問(wèn)。帶上你的好奇心,和我一起探索這片云原生的奧秘之地!??
二、 創(chuàng)建自簽名證書(shū)
首先,我們得創(chuàng)造一個(gè)自簽證書(shū)。這里,我選擇使用 CFSSL 來(lái)完成這一流程。
初始化配置
輕輕敲入以下命令,生成一個(gè)閃亮的配置文件 config.json?
cfssl print-defaults config > config.json
配置內(nèi)容,我們可以根據(jù)自己的需求稍作調(diào)整
{
"signing": {
"default": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
生成證書(shū)
將以下內(nèi)容寫(xiě)入到 create-selfsign-cert.sh 腳本
#!/usr/bin/env bash
cn=$1
if [[ -z "$cn" ]]; then
read -p "Common name: " cn
fi
extfile=$(mktemp)
cat >"$extfile" <<INNER_EOF
{
"CN": "$cn",
"hosts": [
"$cn"
],
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "CN",
"L": "Shanghai",
"O": "BaseBit",
"OU": "XDP",
"ST": "Shanghai"
}
]
}
INNER_EOF
function finish {
rm "$extfile"
}
trap finish EXIT
cfssl selfsign -config=config.json selfsign "$extfile" | cfssljson -bare "$cn"
mv ${cn}-key.pem ${cn}.key
mv ${cn}.pem ${cn}.crt
接著,執(zhí)行以下命令生成泛域名證書(shū):
$(pwd)/create-selfsign-cert.sh "*.kind.cluster"
?? 小貼士:執(zhí)行后,以下三個(gè)文件將被創(chuàng)建:
? ls |grep kind
*.kind.cluster.crt # 自簽名證書(shū)
*.kind.cluster.csr # 證書(shū)簽名請(qǐng)求(CSR)文件
*.kind.cluster.key # 私鑰文件
關(guān)于 CFSSL 的更多魔法 ??,請(qǐng)前往官網(wǎng)自行探索!
三、創(chuàng)建 Kubernetes TLS Secret
接下來(lái),我們將自簽名證書(shū)和私鑰存儲(chǔ)在 Kubernetes 中作為 TLS Secret:
# 創(chuàng)建一個(gè) TLS Secret
kubectl create secret tls tls-secret \
--key *.kind.cluster.key \
--cert *.kind.cluster.crt
四、 配置 Kubernetes Ingress 使用 TLS Secret
Nice,現(xiàn)在準(zhǔn)備工作都完成啦 ??,接下來(lái),讓我們召喚一個(gè)服務(wù),試試效果吧!
# 創(chuàng)建一個(gè) Nginx Deployment
kubectl create deployment nginx-deployment --image=nginx:1.25.3
# 暴露 Deployment 作為一個(gè) Service
kubectl expose deployment nginx-deployment --port=80
我們引用 TLS Secret 在 K8s Ingress 資源中啟用 HTTPS,對(duì)應(yīng)域名為 nginx.kind.cluster
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
spec:
tls: # 以下 4 行是為了支持 TLS
- hosts: #
- nginx.kind.cluster #
secretName: tls-secret #
rules:
- host: nginx.kind.cluster
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-deployment
port:
number: 80
EOF
五、配置 Dnsmasq
因?yàn)槲覀兊挠蛎亲远x的,所以還需要在本地畫(huà)上一道符咒。
將以下信息添加到 Dnsmasq 配置中,它可以從本地 IP 提供通配符[子]域。
echo 'address=/kind.cluster/127.0.0.1' >> $(brew --prefix)/etc/dnsmasq.conf
正如上面提到的,我將 Kind 集群內(nèi)的端口映射到了主機(jī)上,所以這里只需配置 127.0.0.1 就好,不用再配置集群 host 的實(shí)際 IP。Dnsmasq 也會(huì)嘗試解析子域名記錄,例如 foo.kind.cluster 、 bar.kind.cluster ,這非常的方便。
配置完成,使用 brew 來(lái)重啟 Dnsmasq
sudo brew services restart dnsmasq
我們?cè)贋?nbsp;.kind.cluster 結(jié)尾的域名配置了一個(gè)專用的 DNS 解析器。
cat <<EOF | sudo tee /etc/resolver/kind.cluster
nameserver 127.0.0.1
EOF
最后,使用 dig 命令確認(rèn)域名解析正確指向了 127.0.0.1:
? dig kind.cluster @127.0.0.1
; <<>> DiG 9.10.6 <<>> kind.cluster @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54402
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;kind.cluster. IN A
;; ANSWER SECTION:
kind.cluster. 0 IN A 127.0.0.1
;; Query time: 4 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Oct 29 07:31:11 CST 2023
;; MSG SIZE rcvd: 57
?? 下面我們來(lái)做個(gè)驗(yàn)證吧。
圖片
六、信任自簽名證書(shū)
使用自簽名證書(shū)的缺點(diǎn)是,當(dāng)用戶訪問(wèn)通過(guò)此 Ingress 暴露的服務(wù)時(shí),瀏覽器會(huì)顯示一個(gè)警告,因?yàn)樵撟C書(shū)不是由受信任的證書(shū)頒發(fā)機(jī)構(gòu)頒發(fā)的,怎么解決呢?
其實(shí)很簡(jiǎn)單,在你的計(jì)算機(jī)上,將這個(gè)自簽名證書(shū)添加到受信任的根證書(shū)存儲(chǔ)中,這樣你的瀏覽器就不會(huì)每次警告你連接不安全。
下面我們以 macOS 舉例
步驟 1: 雙擊 .crt 文件,這會(huì)打開(kāi)“鑰匙串訪問(wèn)”應(yīng)用。在“鑰匙串訪問(wèn)”中,你會(huì)看到證書(shū)已經(jīng)被導(dǎo)入。如果沒(méi)看到,你也可以手動(dòng)拖拽.crt 文件到“鑰匙串訪問(wèn)”窗口中。
步驟 2: 然后右鍵點(diǎn)擊你導(dǎo)入的證書(shū),選擇“獲取信息”
圖片
步驟 3: 展開(kāi)“信任”部分,在“使用此證書(shū)時(shí)”選擇“始終信任”
圖片
步驟 4: 最后,讓我們?cè)賮?lái)做下驗(yàn)證 ??
圖片
我們也可以通過(guò) CURL 來(lái)驗(yàn)證,它也不再報(bào)任何的錯(cuò)誤,效果如下所示
? curl -v https://nginx.kind.cluster
* Trying 127.0.0.1:443...
* Connected to nginx.kind.cluster (127.0.0.1) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/cert.pem
* CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
* subject: C=CN; ST=Shanghai; L=Shanghai; O=BaseBit; OU=XDP; CN=*.kind.cluster
* start date: Oct 29 13:54:45 2023 GMT
* expire date: Oct 29 13:59:45 2033 GMT
* subjectAltName: host "nginx.kind.cluster" matched cert's "*.kind.cluster"
* issuer: C=CN; ST=Shanghai; L=Shanghai; O=BaseBit; OU=XDP; CN=*.kind.cluster
* SSL certificate verify ok.
* using HTTP/2
...
< HTTP/2 200
...
通過(guò)以上步驟,我們的本地開(kāi)發(fā)環(huán)境將能夠信任并正確使用自簽名證書(shū)。
但還有一種更簡(jiǎn)單的替代方案 — mkcert,它可以幫助你在本地開(kāi)發(fā)環(huán)境直接創(chuàng)建受信任的證書(shū),不需要繁瑣的配置,大大簡(jiǎn)化了本地環(huán)境配置
mkcert -install
mkcert '*.kind.cluster'
大家不妨試下,這玩意完全可以替代 CFSSL,它對(duì)于本地開(kāi)發(fā)和測(cè)試來(lái)說(shuō)是足夠的。
寫(xiě)在后面
在這篇文章中,我們采用了相對(duì)傳統(tǒng)的方法來(lái)創(chuàng)建自簽證書(shū),可能對(duì)某些場(chǎng)景來(lái)說(shuō)并不算是真正的“云原生”。在接下來(lái)的文章中,我會(huì)為大家?guī)?lái) Cert-Manager[4] 的詳細(xì)介紹,它是一種真正云原生的自簽證書(shū)方法,可以自動(dòng)化證書(shū)的請(qǐng)求和續(xù)訂,旨在更好地融合并適應(yīng)現(xiàn)代的 Kubernetes 生態(tài)系統(tǒng)。??
參考資料
[1]Dnsmasq: https://en.wikipedia.org/wiki/Dnsmasq
[2]CFSSL: https://github.com/cloudflare/cfssl
[3]mkcert: https://github.com/FiloSottile/mkcert
[4]Cert-Manager: https://cert-manager.io/