我们前边的文章一直在讨论如何将配置信息通过ConfigMap对象传递个运行在容器中的应用程序,比如注入环境变量或者以configMap类型的数据卷挂载到容器的文件系统中,如果有客户问你配置文件中的用户名和密码呢?你可能会想也可以吧!从原理上讲你当然可以这么做,但是对于敏感信息,很明显这是一种有严重安全风险的做法,因为有访问权限的用户都可以挂载这个configMap,并获取你的用户名和密码。
为了让类似用户名和密码这样的数据更加安全,Kubernetes提供Secrets类型的对象,这是本篇文章要介绍的内容。
熟悉Secrets对象的同学,会发现它和configMap惊人的相似,两类对象都包含的是键值对数据,并且都可以被注入到容器运行的环境中,或者通过文件挂载到容器中,大家可能会问题,那为啥要设计两类对象呢?
具体来说,secrets对象要比cofigmap对象在Kubernetes平台上出现的早,因为secrets对开发和运维同学来说,使用起来并不是太友好,因此Kubernetes社区对secretes进行了优化,设计出了configmap对象。起初两个类型支持的数据类型基本相同,并且功能也类似,因此Kubernetes体系有这两个对象类型,是历史原因造成的。如果放现在,分分钟钟就被设计成一个类型了,但是考虑到向后兼容性,这两个几乎同质的对象,也都走出了自己不一样的人生,目前最新版本的Kubernetes上,两个对象类型或多或少有些差异,这是笔者为什么要写这篇文章的原因。
首先我们来对比一下,看看这两个同质的对象类型到底是相似在哪里?差异在哪里?如下图所示:
如上表所示,data字段和binaryData字段类似,都包含进行过base64编码的二进制数据;stringData字段和configmap的data字段类似,用来存储引用程序的配置信息(文本格式),唯一的区别是,在secret类型中,stringData字段为“write-only”,我们可以将数据写到这个字段(无需手动进行base64编码处理),当应用程序从这个对象读取的时候,数据就会出现在data字段中,并且返回的是base64编码过的数据。
这也是secret和configmap对象的最大的区别,因为我们在configmap的任何一个字段上的操作,状态变化只会体现在对应字段上。另外和configmap类似,我们也可以将secrete设置为只读。从对象的结构角度看,secret有了类型字段是configmap没有的,这个字段用来声明secret对象的类型,如下所示为目前提供的类型选项:
- Opaque类型,默认类型,如果创建secret对象的时候没有指定类型,用来保存用户和系统的敏感数据,没有特殊的要求
- bootstrap.kubernetes.io/token类型,用来保存集群新增节点是的token令牌数据
- kubernetes.io/basic-auth类型,用来保存basic认证需要的用户名和密码数据
- kubernetes.io/dockercfg类型,用来保存访问Docker镜像仓库需要的认证和授权信息。必须包含叫.dockercfg的键,值为机器上的!/.dockercfg配置文件数据
- kubernetes.io/dockerconfigjson类型,和上边的类型相似,用来保存Docker新版本访问镜像仓库的认证和授权信息,不累述
- kubernetes.io/service-accounttoken类型,用来保存Kubernetes服务账号的token信息。这个类型大家会经常碰到,比如我们在集群中创建POD的时候,如果不特殊执行,会挂载一个默认的service account,用来访问API Server,这个service account的token信息,就保存在此类型中
- kubernetes.io/ssh-auth类型,用来保存SSH认证需要的私钥信息,私钥信息必须保存在名为ssh-privatekey的键上
- kubernetes.io/tls类型,用来保存TLS证书和私钥信息。这两个信息必须分别保存在key tls.crt和tls.key的键上
除了笔者上边介绍的这些命名和字段级别的差异之外,Kubernetes在实际处理这两种类型的对象时,也是有区别的。具体来说,Kubernetes确保只会将secret数据分发给真正需要敏感数据的POD,另外secret在被分发给工作节点后,数据只会被保存在内存中,为了安全考虑,secret类型的对象在工作节点上不会被写到磁盘。正是因为Kubernetes对secret做了特殊的安全保障,因此我们在进行系统开发的时候,安全相关的敏感数据应该被配置到secret对象中,而不是configMap,虽然两种从技术的角度都支持。
笔者在前边的系列文章使用过边车代理容器envoy来给k8ssample(大家可以从docker pull qigaopan/k8ssample:v1.2拉取)应用增加HTTPS的处理能力。具体来说,envoy代理容器需要配置TLS证书和私钥,由于这些都是非常敏感的数据,特别是私钥,因此这些数据应该使用secret类型的对象来保存。接下来,我们来看看如何通过使用secret对象来提升隐私数据的安全性。
我们从创建secret对象开始,在创建这个对象之前,读者需要准备好证书(.crt文件)和私钥文件(.key文件),由于创建私钥属于会“百度”都能搞定的范畴,因此咱就不浪费时间了。有了证书文件和私钥文件之后,我们可以在自己的集群上运行: kubectl create secret tls yunpan-tls --cert example-com.crt --key example-com.key命令来创建对应类型的secret对象。这句命令不难理解,我们让Kubernetes创建一个名为yunpan-tls,类型为tls(具体来说是kubernetes.io/tls类型)的secret对象,证书和私钥的数据分别从对应的输入文件读取。
注:同其confimap类型的对象一致,当前Kubernetes版本下,secret的大小不能超过1M。
注:由于secret对象类型的特殊性,笔者并不建议大家使用YAML文件来创建对象,因为YAML文件会被保存到配置管理仓库中,因此数据存在被窃取的风险。
好了,废话不多说,我们赶紧在自己的集群上把对应的secret对象创建出来,并确定对象已经被成功穿件出来,如下是在笔者的本地集群上的输出:
➜ version-2 kubectl create secret tls yunpan-tls --cert example-com.crt --key example-com.key
secret/yunpan-tls created
➜ version-2 kubectl get secrets
NAME TYPE DATA AGE
default-token-4l6j9 kubernetes.io/service-account-token 3 289d
(删除部分输出,方便显示)
yunpan-tls kubernetes.io/tls 2 94s
接下来我们将yunpan-tls这个secret中的敏感数据投影到应用程序的POD中,这里需要使用secret类型的数据卷,secret类型的数据卷和configMap类型的数据卷类似,我们需要在对应POD的YAML文件中首先定义数据卷,并且在需要使用这些敏感数据的容器实例上将数挂载到容器的文件系统,如下图所示的POD定义:
如上图所示,我们定义了secret类型的数据卷,并挂载到envoy容器的对应目录中。读者需要特别注意的是,由于私钥文件的机密性和私密性特征,我们在容器的挂载配置部分,设置了0600权限,这个文件只对文件所有者可读写,为了让大家对上边这个POD的定义有更加清晰的认识,请看下图:
如上图所示,我们通过将secret对象投影到POD,并挂载到对应容器的文件系统上,就将敏感数据传递给容器了。接着我们在自己的环境中部署这个叫yunpan-ssl的POD。如下图所示:
如上图所示,应用程序如预期一样启动,并且我们可以看到,挂载到容器文件系统上证书文件可见。好了,今天的内容这么多了,下篇我们来聊聊downward API的概念,解决如何把动态数据注入到POD中,敬请期待!