使用加密插件加密secrets中的数据

众所周知,kubernetes中提供了一种名为secrets的对象,用于存放集群内部使用的各类敏感数据,比如数据库用户名、密码、各种token、证书等等,从而使得敏感信息和普通配置文件有效解耦。但是默认情况下secrets信息在etcd中是以base64编码形式保存的明文,本篇文章说明如何通过插件加密存储机密数据。

加密插件配置

总体来说配置比较简单,跟着官网的说明做就ok。官方连接:https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/。这里面值得注意的是,kube-apisever的加密插件配置参数为--encryption-provider-config,在1.13版本之前是--experimental-encryption-provider-config,该参数在1.14版本之后已经被正式废弃。

配置文件示例:

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - identity: {}
    - aesgcm:
        keys:
        - name: key1
          secret: c2VjcmV0IGlzIHNlY3VyZQ==
        - name: key2
          secret: dGhpcyBpcyBwYXNzd29yZA==
    - aescbc:
        keys:
        - name: key1
          secret: c2VjcmV0IGlzIHNlY3VyZQ==
        - name: key2
          secret: dGhpcyBpcyBwYXNzd29yZA==
    - secretbox:
        keys:
        - name: key1
          secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=

其中resources可以是多组独立的配置,每组配置下定义了该组资源的加解密的策略,比如这个配置文件定义了secrets资源的加解密策略。providers定义了加解密的实际提供者,目前k8支持的provider如下所示:

名称 加密类型 强度 速度 密钥长度 其它事项
identity N/A N/A N/A 不加密写入的资源。当设置为第一个 provider 时,资源将在新值写入时被解密。
aescbc 填充 PKCS#7 的 AES-CBC 最强 32字节 建议使用的加密项,但可能比 secretbox 稍微慢一些。
secretbox XSalsa20 和 Poly1305 更快 32字节 较新的标准,在需要高度评审的环境中可能不被接受。
aesgcm 带有随机数的 AES-GCM 必须每 200k 写入一次 最快 16, 24, 或者 32字节 建议不要使用,除非实施了自动密钥循环方案。
kms 使用信封加密方案:数据使用带有 PKCS#7 填充的 AES-CBC 通过 data encryption keys(DEK)加密,DEK 根据 Key Management Service(KMS)中的配置通过 key encryption keys(KEK)加密 最强 32字节 建议使用第三方工具进行密钥管理。为每个加密生成新的 DEK,并由用户控制 KEK 轮换来简化密钥轮换。配置 KMS 提供程序

其中identity就是明文,不加密。其余就是各类加解密算法,建议使用aescbc,足够用了,其实就是使用CBC模式、PKCS#7填充的aes256加密。这里要注意的是,providers中可以设置多个加密provider,每个provider可以设置多个加密的密钥。

加解密规则

  • 加密:kube-apiserver默认会使用第一个provider的第一个key进行加密(上面这个例子里面就是明文不加密了)
  • 解密:会依次尝试所有的解密算法,每个算法中会依次尝试所有的key,如果全部尝试失败,则会返回一个错误,以阻止客户端访问该资源。这么设置的原因,当然也是因为一旦你更换了加密密钥(或者加密算法),还能保证你原来用老的密钥(算法)加密的数据还可以正常的访问(加密插件会按照有序列表中的定义挨个尝试)。

kube-apiserver配置

到这里按照常规流程你一定想kubectl create -f来创建这个资源了,如果你这么做了,不出意外的话会看到如下的报错:

error: unable to recognize "encrypt.conf": no matches for kind "EncryptionConfiguration" in version "apiserver.config.k8s.io/v1"

这是因为,kube-apiserver的相关资源,是不能通过kubectl命令来创建的,官方文档并没有明确说明,其实也很好理解,自己怎么创建自己嘛!这个资源,只能是通过配置启动参数在kube-apisever启动的时候来加载。这边我使用kubeadm安装的集群,配置文件位置在/etc/kubernetes/manifests,找到kube-apiserver.yaml,这个就是kube-apiserver启动用的配置文件(用其他方式安装的也类似,只要找到这个配置文件就可以)。所以说这个加密插件的启动,目前来说貌似只能在私有集群中实现,如果你用的是gke、ake、tke这样的云服务商提供的集群就不行了。

tips: kube-apiserver这个pod和普通pod不同,是一个静态pod(static pod),也就是直接启动在特定的node上,由该宿主机的kubelet直接控制。pod不会漂移,配置文件也在宿主机上面,kubelet在启动时,通过读取/etc/kubernetes/manifests里面的配置信息,直接拉起pod。事实上如果你使用kubeadm,则会安装4个静态pod,分别是etcd、kube-apiserver、kube-scheduler、kube-controller-manager。不难看出这些就是保障k8集群正常运作的核心组件,其他插件诸如core-dns、kube-proxy、calico等都是以daemonset或者deployment形式运行的普通pod。在修改了/etc/kubernetes/manifests里的配置信息后,kubelet会自动重启该静态pod。

我们在这个配置文件中加入如下信息:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    component: kube-apiserver
    tier: control-plane
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
    - --encryption-provider-config=/etc/kubernetes/pki/encrypt.conf
    - --advertise-address=192.168.31.241
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC
.....

其中encryption-provider-config就是配置插件插件启动时读取的配置文件所在位置,这边我们把前面写的配置文件命名为encrypt.conf,放在/etc/kubernetes/pki目录下,通过阅读配置文件,我们可以看到kube-apiserver在启动的时候会挂载三个宿主机目录,其中就有/etc/kubernetes/pki。所以你把配置文件放在这里,kube-apiserver启动的时候就能正确找到这个配置文件了。

加密功能验证

kube-apisever重启后,我们就可以尝试一下,看看加密功能是否生效。这里我们用到的配置文件如下:

kind: EncryptionConfiguration
apiVersion: apiserver.config.k8s.io/v1
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key1
          secret: SSM5rRRrQ9+8MsA2cHeRfb7KG9rvF/wsqHOgoQAv5bM=
    - identity: {}

这个配置保证了我们新建的secrets资源都会默认使用aescbc加密算法,并且使用key1中定义的这个密钥来加密数据。注意identity这个参数必须要设置,否则所有我们之前建立的secrets都会无法访问。具体原因其实上面已经讲过,大家可以想一下为什么。

现在我们来新建一个serctes资源来验证一下

kubectl create secret generic secret1 -n default --from-literal=mykey=mydata

之后登陆入容器etcd中

kubectl exec -it etcd-miwifi-r1cm-srv -n kube-system /bin/sh

执行etcdctl命令查看刚才建立的secret1密钥的内容,这里要注意的是etcd默认的api版本是v2,k8默认使用的版本是v3,两者互不兼容,所以在执行的时候需要在命令前显式的加上ETCDCTL_API=3来告诉etcdctl我要调用的是v3 api,或者使用环境变量export指定也可以。由于v3默认开启了ssl认证,所以在调用的时候还需要加上连接认证信息,这部分内容可以在etcd.yamllivenessProbe这个配置中查看到,把这条命令复制出来,后面加上secret1的路径,就可以查看到secret内容了

/ # ETCDCTL_API=3 etcdctl --endpoints=https://[127.0.0.1]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key get /registry/secrets/default/secret1  | hexdump -C
00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
00000010  73 2f 64 65 66 61 75 6c  74 2f 73 65 63 72 65 74  |s/default/secret|
00000020  31 0a 6b 38 73 3a 65 6e  63 3a 61 65 73 63 62 63  |1.k8s:enc:aescbc|
00000030  3a 76 31 3a 6b 65 79 31  3a d5 62 9c a5 45 d3 76  |:v1:key1:.b..E.v|
00000040  50 b5 4f 44 66 22 a6 37  2d 95 87 e9 93 65 72 a4  |P.ODf".7-....er.|
00000050  2d 97 b1 b6 44 b0 8e 7c  27 ba 99 61 86 56 a7 97  |-...D..|'..a.V..|
00000060  21 03 eb 46 93 a9 ba f7  c1 63 fe 5c 34 12 9d 54  |!..F.....c.\4..T|
00000070  ba 3e 73 d5 71 b4 b9 28  ac 0e 66 6e a2 09 44 48  |.>s.q..(..fn..DH|
00000080  cf c6 da 4a 24 6d 49 06  dd f4 e6 85 ff ab e0 e3  |...J$mI.........|
00000090  ed 59 07 98 c2 3e 33 9e  91 f7 9a 9e d1 7f db 65  |.Y...>3........e|
000000a0  f8 60 40 2d 7c 86 1a f2  8b 37 67 c8 83 d3 5e 7b  |.`@-|....7g...^{|
000000b0  fa 51 35 f1 ee d7 51 28  81 a3 9b bd 6d 80 bb e7  |.Q5...Q(....m...|
000000c0  b8 0e 4b 85 0e 90 f3 50  41 0a                    |..K....PA.|

注意在数据头部出现k8s:enc:aescbc:v1:,说明数据已经被正确加密,使用的是aescbc算法,使用的密钥为key1

接下来我们看下kube-apiserver在读取的时候是否正确解密了,执行下面的命令

[root@MiWiFi-R1CM-srv manifests]# kubectl get secrets secret1 -o yaml
apiVersion: v1
data:
  mykey: bXlkYXRh
kind: Secret
metadata:
  creationTimestamp: "2019-08-16T15:12:14Z"
  name: secret1
  namespace: default
  resourceVersion: "549592"
  selfLink: /api/v1/namespaces/default/secrets/secret1
  uid: 3a74e0fd-c038-11e9-95ca-0800279f163b
type: Opaque

得到mykey的base64编码数据bXlkYXRh,将其decode一下

[root@MiWiFi-R1CM-srv manifests]# echo -n "bXlkYXRh" | base64 --decode
mydata

没错,正是我们设置的sercets机密数据,试验成功!

总结

通过kubernetes提供的加密插件,使得etcd中存放的secrets数据都以密文的形式存放,这无异大大提高了数据安全性。但是要明确一点,加密插件只是加密了etcd中保存的数据,这意味着你执行kubectl get secrets mysecret -o yaml这样的命令看到的仍然是明文,在容器内部注入的secrets文件或者环境变量看到的也是明文,原因当然是kube-apiserver在从etcd中取出数据的时候已经帮你自动解密了。如果你有全程加密的需求(比如说想在容器内看到的也是密文),这显然是kubernetes这种平台层的工具做不到的,因为这已经涉及到了应用的改造。

其实就目前的实际使用场景看,如果你有将etcd直接暴露给集群内第三方服务使用或者直接暴露给外部服务使用的需求(一般非常少),那么你最好使用加密插件,否则会面临机密数据泄漏的风险。而如果etcd仅供k8s的系统组件来使用的话,由于kubernetes本身已经有比较完善的rbac机制,那么你只要做好kube-apiserver的权限管理即可,例如:

  • etcd启用安全连接机制,严格禁止非系统组件的直接访问
  • kubectl客户端工具只能在master节点上由系统管理员操作
  • 各服务内的serviceaccount做好权限管理,禁止直接访问secrets资源

那么其实也未必需要加密,k8s默认提供的secrets策略已经完全能够满足要求(毕竟你即使在etcd中加密了,有kubectl权限的和有访问secrets权限的账号还是可以看到明文)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容