【技术分享】etcd未授权访问的风险及修复方案详解

  • A+
所属分类:安全文章

【技术分享】etcd未授权访问的风险及修复方案详解




etcd简介



etcd是一个具有强一致性的分布式 key-value 存储组件。采用类似目录结构的方式对数据进行存储,仅在叶子结点上存储数据,叶子结点的父节点为目录,不能存储数据。

“etcd”这个名字源自两个想法:unix “/etc” 目录和 “d” istributed 分布式系统。“/etc” 目录是用于存储单个系统的配置数据的位置,而 etcd 用于存储大规模分布式的配置信息。因此,加了 “d” 的 “/etc” 就是 “etcd”。

etcd使用比较多的场景包括服务注册与发现、键值对存储、消息发布订阅等。

在kubernetes中,etcd存储集群状态和配置信息,以用于服务发现和集群管理。

 



测试环境搭建



测试环境说明:

  • etcdctl在本机运行;

  • etcd集群部署在虚拟机中的docker下;

  • 虚拟机环境:CentOS 7;

  • 虚拟机ip:192.168.126.143

首先拉取etcd镜像

docker pull quay.io/coreos/etcd:v3.3.1# 查看镜像docker images

创建自定义网络

docker network create --driver bridge --subnet=172.16.1.0/16 --gateway=172.16.1.1 mynet# 查看网络docker network ls

创建etcd节点

节点1:

docker run -d -p 23791:2379 -p 23801:2380 --name etcdnode1 --network=mynet --ip 172.16.2.1 quay.io/coreos/etcd:v3.3.1 etcd -name etcdnode1 -advertise-client-urls http://172.16.2.1:2379 -initial-advertise-peer-urls http://172.16.2.1:2380 -listen-client-urls http://0.0.0.0:2379 -listen-peer-urls http://0.0.0.0:2380 -initial-cluster-token etcd-cluster -initial-cluster "etcdnode1=http://172.16.2.1:2380,etcdnode2=http://172.16.2.2:2380,etcdnode3=http://172.16.2.3:2380" -initial-cluster-state new

节点2

docker run -d -p 23792:2379 -p 23802:2380 --name etcdnode2 --network=mynet --ip 172.16.2.2 quay.io/coreos/etcd:v3.3.1 etcd -name etcdnode2 -advertise-client-urls http://172.16.2.2:2379 -initial-advertise-peer-urls http://172.16.2.2:2380 -listen-client-urls http://0.0.0.0:2379 -listen-peer-urls http://0.0.0.0:2380 -initial-cluster-token etcd-cluster -initial-cluster "etcdnode1=http://172.16.2.1:2380,etcdnode2=http://172.16.2.2:2380,etcdnode3=http://172.16.2.3:2380" -initial-cluster-state new

节点3:

docker run -d -p 23793:2379 -p 23803:2380 --name etcdnode3 --network=mynet --ip 172.16.2.3 quay.io/coreos/etcd:v3.3.1 etcd -name etcdnode3 -advertise-client-urls http://172.16.2.3:2379 -initial-advertise-peer-urls http://172.16.2.3:2380 -listen-client-urls http://0.0.0.0:2379 -listen-peer-urls http://0.0.0.0:2380 -initial-cluster-token etcd-cluster -initial-cluster "etcdnode1=http://172.16.2.1:2380,etcdnode2=http://172.16.2.2:2380,etcdnode3=http://172.16.2.3:2380" -initial-cluster-state new

参数说明:

【技术分享】etcd未授权访问的风险及修复方案详解
【技术分享】etcd未授权访问的风险及修复方案详解
# 查看docker进程docker ps

更多的安装和部署方式可参考:
http://blueskykong.com/2020/05/27/etcd-2/
http://blueskykong.com/2020/06/06/etcd-3/

 



未授权访问利用



刚刚我们搭建好的etcd环境,没有经过特殊配置,默认是未经授权即可访问的。

使用官方提供的etcdctl直接用命令行即可访问etcd,无需去了解每个http api。

下载etcd:https://github.com/etcd-io/etcd/releases

解压后在命令行中进入etcd目录下。

etcdctl api版本切换:

export ETCDCTL_API=2export ETCDCTL_API=3

切换版本后可以执行etcdctl -h命令查看帮助。

目前网上的公开文章大部分都是在讲v2版本api的利用,比如:

直接访问http://ip:2379/v2/keys/?recursive=true ,可以看到所有的key-value值。

或者使用etcdctl:

etcdctl --endpoints="http://ip:2379" ls

etcd v3版本的api和v2版本完全不同,所以访问上面的url不会看到任何数据。这里主要简单介绍一下v3版本api的使用。

搭建好上面的测试环境后,可以执行以下命令,向etcd中插入几条测试数据:

etcdctl --endpoints=192.168.126.143:23791 put /testdir/testkey1 "Hello world1"etcdctl --endpoints=192.168.126.143:23791 put /testdir/testkey2 "Hello world2"etcdctl --endpoints=192.168.126.143:23791 put /testdir/testkey3 "Hello world3"

查看指定key的值:

etcdctl --endpoints=192.168.126.143:23791  get /testdir/testkey1

执行下面命令即可读取etcd中存储的所有数据:

etcdctl --endpoints=192.168.126.143:23791 get / --prefix

--prefix用来指定前缀,上述命令的意思就是获取所有“/”作为前缀的key value值

如果结果过多,还可以通过--limit选项限制数量:

etcdctl --endpoints=192.168.126.143:23791 get / --prefix --limit=2

下面命令可用于列出当前目标所属同一集群的所有节点:

etcdctl --endpoints=192.168.126.143:23791  member list

更多etcdctl使用示例可以在压缩包中的:README-etcdctl.md、READMEv2-etcdctl.md文档里查看,分别对应v3、v2版本api。

 



未授权访问可能产生的风险



kubernetes的master会安装etcd v3用来存储数据,如果管理员进行了错误的配置,导致etcd未授权访问的情况,那么攻击者就可以从etcd中拿到kubernetes的认证鉴权token,从而接管集群。

在真实的场景中,还有一些应用使用etcd来存储各种服务的账号密码、公私钥等敏感数据。而很多etcd服务的使用者完全没有考虑过其安全风险,这种情况和redis的使用情况差不多,在企业内网比较普遍,甚至也有少部分人会将其开放到公网。

更多关于etcd未授权访问风险的描述可参考:https://gcollazo.com/the-security-footgun-in-etcd/

 



如何安全的使用etcd



etcd目前支持两种安全方案,分别解决了不同问题。

1、basic认证(基于角色的访问控制)

这种安全方案解决了用户认证和权限管理的问题。

etcd在2.1版本之前,是一个完全开放的系统,任何人都可以通过rest api对etcd数据进行增删改查。2.1版本之后,引入了用户认证功能,并且支持权限管理。但为了向前兼容,默认并未开启,需要手动启用。

etcd 2.x版本开启basic认证的相关命令和etcd 3.x版本有所区别,可以参考:https://blog.csdn.net/ucmir183/article/details/84454506

此处主要讲解etcd 3.x版本开启basic认证的过程。首先创建root用户:

etcdctl --endpoints=192.168.126.143:23791 user add root

如图,输入密码,重复输入并确认密码后创建成功:

【技术分享】etcd未授权访问的风险及修复方案详解

接下来执行下面命令启用认证:

etcdctl --endpoints=192.168.126.143:23791 auth enable

启用认证后会自动为root账号创建一个root角色,该角色拥有全部etcd数据的读写权限。接下来访问etcd就必须要带着账号密码了。

例如:

查看所有角色:

etcdctl --endpoints=192.168.126.143:23791 --user root:password role list

查看所有用户:

etcdctl --endpoints=192.168.126.143:23791 --user root:password user list

创建一个新的角色:

etcdctl --endpoints=192.168.126.143:23791 --user root:password role add staff

授予staff角色/testdir/testkey1只读权限:

etcdctl --endpoints=192.168.126.143:23791 --user root:password role grant-permission staff read /testdir/testkey1

授予staff角色/pub/作为key前缀的所有数据读写权限:

etcdctl --endpoints=192.168.126.143:23791 --user root:password role grant-permission staff --prefix=true readwrite /pub/
查看staff角色权限:
etcdctl --endpoints=192.168.126.143:23791 --user root:password role get staff
结果如图:

【技术分享】etcd未授权访问的风险及修复方案详解

创建一个新用户:

etcdctl --endpoints=192.168.126.143:23791 --user root:password user add staffuser1

同样需要输入要创建用户的密码。

授予staffuser1用户staff角色权限:

etcdctl --endpoints=192.168.126.143:23791 --user root:password user grant-role staffuser1 staff

创建后的staffuser1用户将拥有我们之前配置的staff角色的数据访问权限。

更多访问控制相关命令可参考官方文档:https://etcd.io/docs/v3.4/op-guide/authentication/

2、基于TLS的身份验证和数据传输

互联网中所有明文传输数据的方式,都面临三个风险:窃听、篡改和冒充。SSL/TLS协议的出现解决了这三个问题。

基于TLS的身份验证方式既解决了传输安全的问题,也可以用来解决未授权访问的问题。

TLS协议的原理不在这里赘述,如果不了解可以自行查阅相关资料。接下来主要讲etcd如何使用TLS进行身份验证和数据传输的实践。

首先我们需要下载cfssl:https://github.com/cloudflare/cfssl/releases
cfssl 是 CloudFlare 的 PKI证书管理工具。

下载cfssl-certinfo_1.5.0_linux_amd64、cfssljson_1.5.0_linux_amd64、cfssl_1.5.0_linux_amd64这三个文件,下载后全部移动到/usr/local/bin/目录下。

[[email protected] Downloads]# mv cfssl_1.5.0_linux_amd64 /usr/local/bin/cfssl[[email protected] Downloads]# mv cfssljson_1.5.0_linux_amd64 /usr/local/bin/cfssljson[[email protected] Downloads]# mv cfssl-certinfo_1.5.0_linux_amd64 /usr/local/bin/cfssl-certinfo[[email protected] Downloads]# ls /usr/local/bin/cfssl*/usr/local/bin/cfssl  /usr/local/bin/cfssl-certinfo  /usr/local/bin/cfssljson

创建将要存放PKI配置和证书的目录,并进入目录下:

[[email protected] /]# mkdir /etc/etcd/pki -p[[email protected] /]# cd /etc/etcd/pki/

2.1 创建CA根证书

[[email protected] pki]# vi ca-csr.jso

填入下面内容:

{    "CN": "ETCD Root CA",    "key": {        "algo": "rsa",        "size": 2048    },    "names": [        {            "C": "CN",            "L": "Shanghai",            "ST": "Shanghai"        }    ]}

生成根证书和key

[[email protected] pki]# cfssl gencert -initca  ca-csr.json | cfssljson -bare ca[[email protected] pki]# lsca.csr  ca-csr.json  ca-key.pem  ca.pem

2.2 签发证书配置文件

我们需要签发三种证书,创建ca-config.json文件,定义三个profile:

{    "signing": {        "default": {            "expiry": "168h"        },        "profiles": {            "server": {                "expiry": "8760h",                "usages": [                    "signing",                    "key encipherment",                    "server auth"                ]            },            "client": {                "expiry": "8760h",                "usages": [                    "signing",                    "key encipherment",                    "client auth"                ]            },            "peer": {                "expiry": "8760h",                "usages": [                    "signing",                    "key encipherment",                    "server auth",                    "client auth"                ]            }        }    }}

其中,server作为服务端与客户端通信时的服务端证书,client作为服务端与客户端通信时的客户端证书,peer作为集群节点之间的通信证书。

2.3 生成服务端证书

创建etcd-server.json文件

{    "CN": "etcd server",    "hosts": [        "172.16.2.1",        "172.16.2.2",        "172.16.2.3",        "192.168.126.143"    ],    "key": {        "algo": "rsa",        "size": 2048    },    "names": [        {            "C": "CN",            "L": "Shanghai",            "ST": "Shanghai"        }    ]}

生成服务端证书:

[[email protected] pki]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server etcd-server.json | cfssljson -bare server2021/03/31 07:24:58 [INFO] generate received request2021/03/31 07:24:58 [INFO] received CSR2021/03/31 07:24:58 [INFO] generating key: rsa-20482021/03/31 07:24:58 [INFO] encoded CSR2021/03/31 07:24:58 [INFO] signed certificate with serial number 545742070794152469099370572346380711975550497369[[email protected] pki]# lsca-config.json  ca-csr.json  ca.pem            server.csr      server.pemca.csr          ca-key.pem   etcd-server.json  server-key.pem
上面这种方式,把所有节点的ip都写到了hosts中,集群成员使用统一的服务端证书。生产环境一般把hosts写成统一的对外域名。也可以分开创建三个配置文件,每个配置文件里面填写一个ip,不公用,这样方便后面扩容。

2.4 生成客户端证书

创建etcd-client.json文件,因为客户端证书仅用于签发验证客户端身份,因此不需要hosts字段。

{    "CN": "etcd client",    "key": {        "algo": "rsa",        "size": 2048    },    "names": [        {            "C": "CN",            "L": "Shanghai",            "ST": "Shanghai"        }    ]}

生成客户端证书:

[[email protected] pki]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client etcd-client.json | cfssljson -bare client

由于没有填写hosts字段,因此会有“[WARNING] This certificate lacks a “hosts” field. ”的警告,忽略就好。

2.5 生成peer节点通信证书

创建etcd-peer.json文件:

{    "CN": "etcd peer",    "hosts": [        "172.16.2.1",        "172.16.2.2",        "172.16.2.3"    ],    "key": {        "algo": "rsa",        "size": 2048    },    "names": [        {            "C": "CN",            "L": "Shanghai",            "ST": "Shanghai"        }    ]}

生成peer节点通信证书:

[[email protected] pki]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer etcd-peer.json | cfssljson -bare peer

2.6 重新创建带TLS传输认证的etcd集群

首先停止运行之前创建的etcd集群,并将其删除。

[[email protected] w4ter0]# docker stop $(docker ps -a -q) 9cc6afc100ad56148a0ebae243ef4286ca34[[email protected] w4ter0]# docker rm $(docker ps -a -q)9cc6afc100ad56148a0ebae243ef4286ca34

执行下面命令创建新的etcd集群。

节点1:

docker run -d -p 23791:2379 -p 23801:2380 -v /etc/etcd/pki:/pki --name etcdnode1 --network=mynet --ip 172.16.2.1 quay.io/coreos/etcd:v3.3.1 etcd -name etcdnode1 -advertise-client-urls https://172.16.2.1:2379 -initial-advertise-peer-urls https://172.16.2.1:2380 -listen-client-urls https://0.0.0.0:2379 -listen-peer-urls https://0.0.0.0:2380 -initial-cluster-token etcd-cluster -initial-cluster "etcdnode1=https://172.16.2.1:2380,etcdnode2=https://172.16.2.2:2380,etcdnode3=https://172.16.2.3:2380" -initial-cluster-state new -cert-file=/pki/server.pem -key-file=/pki/server-key.pem -client-cert-auth -trusted-ca-file=/pki/ca.pem -peer-client-cert-auth -peer-cert-file=/pki/peer.pem -peer-key-file=/pki/peer-key.pem -peer-trusted-ca-file=/pki/ca.pem

节点2:

docker run -d -p 23792:2379 -p 23802:2380 -v /etc/etcd/pki:/pki --name etcdnode2 --network=mynet --ip 172.16.2.2 quay.io/coreos/etcd:v3.3.1 etcd -name etcdnode2 -advertise-client-urls https://172.16.2.2:2379 -initial-advertise-peer-urls https://172.16.2.2:2380 -listen-client-urls https://0.0.0.0:2379 -listen-peer-urls https://0.0.0.0:2380 -initial-cluster-token etcd-cluster -initial-cluster "etcdnode1=https://172.16.2.1:2380,etcdnode2=https://172.16.2.2:2380,etcdnode3=https://172.16.2.3:2380" -initial-cluster-state new -cert-file=/pki/server.pem -key-file=/pki/server-key.pem -client-cert-auth -trusted-ca-file=/pki/ca.pem -peer-client-cert-auth -peer-cert-file=/pki/peer.pem -peer-key-file=/pki/peer-key.pem -peer-trusted-ca-file=/pki/ca.pem

节点3:

docker run -d -p 23793:2379 -p 23803:2380 -v /etc/etcd/pki:/pki --name etcdnode3 --network=mynet --ip 172.16.2.3 quay.io/coreos/etcd:v3.3.1 etcd -name etcdnode3 -advertise-client-urls https://172.16.2.3:2379 -initial-advertise-peer-urls https://172.16.2.3:2380 -listen-client-urls https://0.0.0.0:2379 -listen-peer-urls https://0.0.0.0:2380 -initial-cluster-token etcd-cluster -initial-cluster "etcdnode1=https://172.16.2.1:2380,etcdnode2=https://172.16.2.2:2380,etcdnode3=https://172.16.2.3:2380" -initial-cluster-state new -cert-file=/pki/server.pem -key-file=/pki/server-key.pem -client-cert-auth -trusted-ca-file=/pki/ca.pem -peer-client-cert-auth -peer-cert-file=/pki/peer.pem -peer-key-file=/pki/peer-key.pem -peer-trusted-ca-file=/pki/ca.pem

再次访问新创建的etcd集群,直接访问

etcdctl --endpoints="https://192.168.126.143:23791" member list

会报如下错误:

【技术分享】etcd未授权访问的风险及修复方案详解

浏览器中访问报错如下:

【技术分享】etcd未授权访问的风险及修复方案详解

需要携带客户端的证书和密钥访问。将签发的客户端证书、密钥和ca证书copy到本机etcdctl同级目录下,指定对应参数即可正常访问:

etcdctl --endpoints="https://192.168.126.143:23791" --cacert=ca.pem --cert=client.pem --key=client-key.pem member list
【技术分享】etcd未授权访问的风险及修复方案详解至此,我们完成了基于TLS的身份验证和数据传输配置。

虽然上面两个方案都能解决etcd未授权访问的问题,但是为保证安全,实际使用时强烈建议两种方案同时上,既实现了权限管控,又保障了传输安全。

参考资料:
https://etcd.io/docs/v3.4/op-guide/
https://blog.csdn.net/u011508407/article/details/108549703
https://blog.csdn.net/weixin_30788731/article/details/97545042
https://www.jianshu.com/p/7bbef1ca9733
https://blog.csdn.net/ucmir183/article/details/84454506
https://juejin.cn/post/6844903678269210632
https://www.cnblogs.com/effortsing/p/10332492.html
http://blueskykong.com/categories/etcd/
https://gcollazo.com/the-security-footgun-in-etcd/

(点击“阅读原文”查看链接)

【技术分享】etcd未授权访问的风险及修复方案详解

- End -
精彩推荐
5亿+用户数据遭泄,Facebook归因网络爬虫可还行?
【技术分享】六问MySQL?你敢来挑战吗?
【技术分享】Win10下一个有意思的驱动引起可能性的拒绝服务攻击
【技术分享】一次被MSRC坑的经历

【技术分享】etcd未授权访问的风险及修复方案详解

戳“阅读原文”查看更多内容

本文始发于微信公众号(安全客):【技术分享】etcd未授权访问的风险及修复方案详解

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: