天天看點

徹底搞懂 etcd 系列文章(四):etcd 安全

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 系列文章(四):etcd 安全

推薦閱讀

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

參考

etcd docs

繼續閱讀