微微一笑 2020-06-14
etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管。etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件。
《彻底搞懂 etcd 系列文章》将会从 etcd 的基本功能实践、API 接口、实现原理、源码分析,以及实现中的踩坑经验等几方面具体展开介绍 etcd。预计会有 20 篇左右的文章,笔者将会每周持续更新,欢迎关注。
在上一篇文章介绍了 etcd 集群的运维部署之后,本文将会重点讲解 etcd 的安全通信实践。 etcd 支持通过 TLS 协议进行的加密通信。TLS 通道可用于对等体之间的加密内部群集通信以及加密的客户端流量。本文提供了使用对等和客户端 TLS 设置群集的示例。
互联网的通信安全,建立在 SSL/TLS 协议之上。不使用 SSL/TLS 的 HTTP 通信,就是不加密的通信。所有信息明文传播,带来了三大风险:
SSL/TLS 协议是为了解决这三大风险而设计的,希望达到:
下面具体介绍下 SSL 与 TLS 的相关概念:
想要实现数据 HTTPS 加密协议访问,保障数据的安全,就需要 SSL 证书,TLS 是 SSL 与 HTTPS 安全传输层协议名称。
为了进行实践,我们将会安装一些实用的命令行工具,其中包括 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 | 2379 | 2380 |
infra2 | 192.168.202.130| 2379 | 2380 |
$ ls ~/Downloads/cfsslcfssl-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_amd64mv cfssl_1.4.1_linux_amd64 /usr/local/bin/cfsslmv cfssljson_1.4.1_linux_amd64 /usr/local/bin/cfssljsonmv cfssl-certinfo_1.4.1_linux_amd64 /usr/bin/cfssl-certinfo
安装完成之后,查看版本信息的结果:
$ cfssl versionVersion: 1.4.1Runtime: go1.12.12
我们将使用 CloudFlare‘s PKI 工具 cfssl 来配置 PKI Infrastructure,然后使用它去创建 Certificate Authority(CA), 并为 etcd 创建 TLS 证书。
首先创建 ssl 配置目录:
mkdir /opt/etcd/{bin,cfg,ssl} -pcd /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 ca2020/04/30 20:36:58 [INFO] generating a new CA key and certificate from CSR2020/04/30 20:36:58 [INFO] generate received request2020/04/30 20:36:58 [INFO] received CSR2020/04/30 20:36:58 [INFO] generating key: rsa-20482020/04/30 20:36:58 [INFO] encoded CSR2020/04/30 20:36:58 [INFO] signed certificate with serial number 252821789025044258332210471232130931231440888312$ lsca-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 server2020/04/30 20:44:37 [INFO] generate received request2020/04/30 20:44:37 [INFO] received CSR2020/04/30 20:44:37 [INFO] generating key: rsa-20482020/04/30 20:44:37 [INFO] encoded CSR2020/04/30 20:44:37 [INFO] signed certificate with serial number 73061688633166283265484923779818839258466531108lsca-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.492956mshttps://192.168.202.130:2379 is healthy: successfully committed proposal: took = 12.805109mshttps://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, false6b57a3c3b8a54873, started, etcd3, https://192.168.202.130:2380, https://192.168.202.130:2379, falsec1ba2629c5bc62ac, 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 worldOK$ /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 hellohelloworld
写入 hello->wold 的键值对,读取的时候,控制台正常输出了键值。至此,我们成功将 etcd 的通信加密。
如果集群需要加密的通信但不需要经过身份验证的连接,则可以将 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
命令屏蔽对证书链的校验。
本文重点讲解了 etcd 集群的 TLS 安全认证配置,数据通信明文传播存在篡改、窃听、冒充等风险。互联网的通信安全,建立在 SSL/TLS 协议之上。基于 cfssl 工具,验证并且捆绑 TLS 证书,为 etcd 集群成员之间的通信保驾护航。
###host字段指定授权使用该证书的etcd节点IP或子网列表,需要将etcd集群的3个节点都添加其中。cp etcd-v3.3.13-linux-amd64/etcd* /opt/k8s/bin/