0 專輯概述
etcd 是雲原生架構中重要的基礎元件,由 CNCF 孵化托管。etcd 在微服務和 Kubernates 叢集中不僅可以作為服務注冊與發現,還可以作為 key-value 存儲的中間件。
《徹底搞懂 etcd 系列文章》将會從 etcd 的基本功能實踐、API 接口、實作原理、源碼分析,以及實作中的踩坑經驗等幾方面具體展開介紹 etcd。預計會有 20 篇左右的文章,筆者将會每周持續更新,歡迎關注。
1 etcd 安全
在
上一篇文章介紹了 etcd 叢集的運維部署之後,本文将會重點講解 etcd 的安全通信實踐。 etcd 支援通過 TLS 協定進行的加密通信。TLS 通道可用于對等體之間的加密内部群集通信以及加密的用戶端流量。本文提供了使用對等和用戶端 TLS 設定群集的示例。
2 TLS 與 SSL
網際網路的通信安全,建立在 SSL/TLS 協定之上。不使用 SSL/TLS 的 HTTP 通信,就是不加密的通信。所有資訊明文傳播,帶來了三大風險:
- 竊聽風險(eavesdropping):第三方可以獲知通信内容。
- 篡改風險(tampering):第三方可以修改通信内容。
- 冒充風險(pretending):第三方可以冒充他人身份參與通信。
SSL/TLS 協定是為了解決這三大風險而設計的,希望達到:
- 所有資訊都是加密傳播,第三方無法竊聽。
- 具有校驗機制,一旦被篡改,通信雙方會立刻發現。
- 配備身份證書,防止身份被冒充。
下面具體介紹下 SSL 與 TLS 的相關概念:
- SSL (Secure Socket Layer):為Netscape所研發,用以保障在Internet上資料傳輸之安全,利用資料加密(Encryption)技術,可確定資料在網絡上之傳輸過程中不會被截取。目前一般通用之規格為40 bit之安全标準,美國則已推出128 bit之更高安全标準,但限制出境。隻要3.0版本以上之I.E.或Netscape浏覽器即可支援SSL。
- 安全傳輸層協定(TLS)用于在兩個通信應用程式之間提供保密性和資料完整性。該協定由兩層組成: TLS 記錄協定(TLS Record)和 TLS 握手協定(TLS Handshake)。較低的層為 TLS 記錄協定,位于某個可靠的傳輸協定(例如 TCP)上面。
想要實作資料 HTTPS 加密協定通路,保障資料的安全,就需要 SSL 證書,TLS 是 SSL 與 HTTPS 安全傳輸層協定名稱。
3 進行 TLS 加密實踐
為了進行實踐,我們将會安裝一些實用的指令行工具,其中包括 cfssl、cfssljson。
cfssl 是 CloudFlare 的 PKI/TLS 利器。 它既是指令行工具,又可以用于簽名,驗證和捆綁 TLS 證書的 HTTP API 伺服器,環境建構方面需要 Go 1.12+。
cfssljson 程式,從 cfssl 擷取 JSON 輸出,并将證書、密鑰、CSR和 bundle 寫入指定位置。
環境配置
HostName | ip | 用戶端互動端口 | peer 通信端口 |
---|---|---|---|
infra0 | 192.168.202.128 | 2379 | 2380 |
infra1 | 192.168.202.129 | ||
infra2 | 192.168.202.130 |
3.1 安裝 cfssl
$ ls ~/Downloads/cfssl
cfssl-certinfo_1.4.1_linux_amd64 cfssl_1.4.1_linux_amd64 cfssljson_1.4.1_linux_amd64
chmod +x cfssl_1.4.1_linux_amd64 cfssljson_1.4.1_linux_amd64 cfssl-certinfo_1.4.1_linux_amd64
mv cfssl_1.4.1_linux_amd64 /usr/local/bin/cfssl
mv cfssljson_1.4.1_linux_amd64 /usr/local/bin/cfssljson
mv cfssl-certinfo_1.4.1_linux_amd64 /usr/bin/cfssl-certinfo
安裝完成之後,檢視版本資訊的結果:
$ cfssl version
Version: 1.4.1
Runtime: go1.12.12
3.2 配置 CA 并建立 TLS 證書
我們将使用 CloudFlare's PKI 工具 cfssl 來配置 PKI Infrastructure,然後使用它去建立 Certificate Authority(CA), 并為 etcd 建立 TLS 證書。
首先建立 ssl 配置目錄:
mkdir /opt/etcd/{bin,cfg,ssl} -p
cd /opt/etcd/ssl/
etcd ca 配置:
cat << EOF | tee ca-config.json
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"etcd": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
EOF
etcd ca證書:
cat << EOF | tee ca-csr.json
{
"CN": "etcd CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Shanghai",
"ST": "Shanghai"
}
]
}
EOF
生成 CA 憑證和私鑰:
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2020/04/30 20:36:58 [INFO] generating a new CA key and certificate from CSR
2020/04/30 20:36:58 [INFO] generate received request
2020/04/30 20:36:58 [INFO] received CSR
2020/04/30 20:36:58 [INFO] generating key: rsa-2048
2020/04/30 20:36:58 [INFO] encoded CSR
2020/04/30 20:36:58 [INFO] signed certificate with serial number 252821789025044258332210471232130931231440888312
$ ls
ca-config.json ca-csr.json ca-key.pem ca.csr ca.pem
etcd server證書:
cat << EOF | tee server-csr.json
{
"CN": "etcd",
"hosts": [
"192.168.202.128",
"192.168.202.129",
"192.168.202.130"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Beijing",
"ST": "Beijing"
}
]
}
EOF
生成 server 證書:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=etcd server-csr.json | cfssljson -bare server
2020/04/30 20:44:37 [INFO] generate received request
2020/04/30 20:44:37 [INFO] received CSR
2020/04/30 20:44:37 [INFO] generating key: rsa-2048
2020/04/30 20:44:37 [INFO] encoded CSR
2020/04/30 20:44:37 [INFO] signed certificate with serial number 73061688633166283265484923779818839258466531108
ls
ca-config.json ca-csr.json ca-key.pem ca.csr ca.pem server-csr.json server-key.pem server.csr server.pem
啟動 etcd 叢集,配置如下:
#etcd1 啟動
$ /opt/etcd/bin/etcd --name etcd1 --initial-advertise-peer-urls https://192.168.202.128:2380 \
--listen-peer-urls https://192.168.202.128:2380 \
--listen-client-urls https://192.168.202.128:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://192.168.202.128:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster etcd1=https://192.168.202.128:2380, etcd2=https://192.168.202.129:2380, etcd3=https://192.168.202.130:2380 \
--initial-cluster-state new \
--client-cert-auth --trusted-ca-file=/opt/etcd/ssl/ca.pem \
--cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem \
--peer-client-cert-auth --peer-trusted-ca-file=/opt/etcd/ssl/ca.pem \
--peer-cert-file=/opt/etcd/ssl/server.pem --peer-key-file=/opt/etcd/ssl/server-key.pem
#etcd2 啟動
/opt/etcd/bin/etcd --name etcd2 --initial-advertise-peer-urls https://192.168.202.129:2380 \
--listen-peer-urls https://192.168.202.129:2380 \
--listen-client-urls https://192.168.202.129:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://192.168.202.129:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster etcd1=https://192.168.202.128:2380, etcd2=https://192.168.202.129:2380, etcd3=https://192.168.202.130:2380 \
--initial-cluster-state new \
--client-cert-auth --trusted-ca-file=/opt/etcd/ssl/ca.pem \
--cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem \
--peer-client-cert-auth --peer-trusted-ca-file=/opt/etcd/ssl/ca.pem \
--peer-cert-file=/opt/etcd/ssl/server.pem --peer-key-file=/opt/etcd/ssl/server-key.pem
#etcd3 啟動
/opt/etcd/bin/etcd --name etcd3 --initial-advertise-peer-urls https://192.168.202.130:2380 \
--listen-peer-urls https://192.168.202.130:2380 \
--listen-client-urls https://192.168.202.130:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://192.168.202.130:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster etcd1=https://192.168.202.128:2380, etcd2=https://192.168.202.129:2380, etcd3=https://192.168.202.130:2380 \
--initial-cluster-state new \
--client-cert-auth --trusted-ca-file=/opt/etcd/ssl/ca.pem \
--cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem \
--peer-client-cert-auth --peer-trusted-ca-file=/opt/etcd/ssl/ca.pem \
--peer-cert-file=/opt/etcd/ssl/server.pem --peer-key-file=/opt/etcd/ssl/server-key.pem
通過三台伺服器的控制台可以知道,叢集已經成功建立,我們進行驗證:
$ /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.202.128:2379,https://192.168.202.129:2379,https://192.168.202.130:2379" endpoint health
# 輸出如下:
https://192.168.202.129:2379 is healthy: successfully committed proposal: took = 9.492956ms
https://192.168.202.130:2379 is healthy: successfully committed proposal: took = 12.805109ms
https://192.168.202.128:2379 is healthy: successfully committed proposal: took = 13.036091ms
檢視三個節點的健康狀況,
endpoint health
,輸出的結果符合我們的預期。其次,檢視叢集的成員清單:
$ /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.202.128:2379,https://192.168.202.129:2379,https://192.168.202.130:2379" member list
# 輸出如下:
48e15f7612b3de1, started, etcd2, https://192.168.202.129:2380, https://192.168.202.129:2379, false
6b57a3c3b8a54873, started, etcd3, https://192.168.202.130:2380, https://192.168.202.130:2379, false
c1ba2629c5bc62ac, started, etcd1, https://192.168.202.128:2380, https://192.168.202.128:2379, false
輸出三個成員,完全符合我們的預期。經過 TLS 加密的 etcd 叢集,在進行操作時,需要加上認證相關的資訊,我們嘗試先寫再讀的操作:
$ /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.202.128:2379,https://192.168.202.129:2379,https://192.168.202.130:2379" put hello world
OK
$ /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.202.128:2379,https://192.168.202.129:2379,https://192.168.202.130:2379" get hello
hello
world
寫入 hello->wold 的鍵值對,讀取的時候,控制台正常輸出了鍵值。至此,我們成功将 etcd 的通信加密。
3.3 自動證書
如果叢集需要加密的通信但不需要經過身份驗證的連接配接,則可以将 etcd 配置為自動生成其密鑰。 在初始化時,每個成員都基于其通告的 IP 位址和主機建立自己的密鑰集。
在每台機器上,etcd 将使用以下标志啟動:
$ etcd --name etcd1 --initial-advertise-peer-urls https://192.168.202.128:2380 \
--listen-peer-urls https://192.168.202.128:2380 \
--listen-client-urls https://192.168.202.128:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://10.0.1.10:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster infra0=https://192.168.202.128:2380,infra1=https://192.168.202.129:2380,infra2=https://192.168.202.130:2380 \
--initial-cluster-state new \
--auto-tls \
--peer-auto-tls
注意,由于自動簽發證書并不認證身份,是以直接 curl 會傳回錯誤。需要使用 curl 的
-k
指令屏蔽對證書鍊的校驗。
4 小結
本文重點講解了 etcd 叢集的 TLS 安全認證配置,資料通信明文傳播存在篡改、竊聽、冒充等風險。網際網路的通信安全,建立在 SSL/TLS 協定之上。基于 cfssl 工具,驗證并且捆綁 TLS 證書,為 etcd 叢集成員之間的通信保駕護航。
訂閱最新文章,歡迎關注我的公衆号

推薦閱讀
- etcd 與 Zookeeper、Consul 等其它 k-v 元件的對比
- 徹底搞懂 etcd 系列文章(一):初識 etcd
- 徹底搞懂 etcd 系列文章(二):etcd 的多種安裝姿勢
- 徹底搞懂 etcd 系列文章(三):etcd 叢集運維部署