• 彻底搞懂 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 | 2379 | 2380 |
    infra2 | 192.168.202.130| 2379 | 2380 |

    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 集群成员之间的通信保驾护航。

    订阅最新文章,欢迎关注我的公众号

    推荐阅读

    1. etcd 与 Zookeeper、Consul 等其它 k-v 组件的对比
    2. 彻底搞懂 etcd 系列文章(一):初识 etcd
    3. 彻底搞懂 etcd 系列文章(二):etcd 的多种安装姿势
    4. 彻底搞懂 etcd 系列文章(三):etcd 集群运维部署

    参考

    etcd docs

  • 相关阅读:
    【11.3】
    WPF中内嵌网页的两种方式
    .net的winform中DialogResult属性的使用
    MVC过滤器使用方法
    C#调用C++的DLL错误解决方法
    WPF中UserControl的属性和事件
    React架构之路
    读完这一篇,字符串格式化界的“白富美”(f-strings)抱回家!
    关于使用format()方法格式化字符串,读这一篇就够了!
    String,StringBuffer与StringBuilder
  • 原文地址:https://www.cnblogs.com/aoho/p/13123744.html
Copyright © 2020-2023  润新知