1、概述
应用启动过程中可能需要一些敏感信息,比如访问数据库的用户名密码或者密钥。将这些信息直接保存在容器镜像中显然不妥,Kubernetes 提供的解决方案是 Secret。Secret 会以密文的方式存储数据,避免了直接在配置文件中保存敏感信息。Secret 会以 Volume 的形式被 mount 到 Pod,容器可通过文件的方式使用 Secret 中的敏感数据;此外,容器也可以环境变量的方式使用这些数据。
要使用 Secret,Pod 需要引用 Secret。 Pod 可以用三种方式之一来使用 Secret:
- 作为挂载到一个或多个容器上的卷中的文件。
- 作为容器的环境变量
- 由 kubelet 在为 Pod 拉取镜像时使用
Kubernetes 控制平面也使用 Secret; 例如,引导令牌 Secret 是一种帮助自动化节点注册的机制。
Secret 对象的名称必须是合法的 DNS 子域名。 在为创建 Secret 编写配置文件时,你可以设置 data
与/或 stringData
字段。 data
和 stringData
字段都是可选的。data
字段中所有键值都必须是 base64 编码的字符串。如果不希望执行这种 base64 字符串的转换操作,你可以选择设置 stringData
字段,其中可以使用任何字符串作为其取值。
2、Secret 的类型
创建 Secret 时,你可以使用 Secret 资源的 type
字段, 或者与其等价的 kubectl
命令行参数(如果有的话,例如generic、docker-registry)为其设置类型。 Secret 的 type
有助于对不同类型机密数据的编程处理。
Kubernetes 提供若干种内置的类型,用于一些常见的使用场景。 针对这些类型,Kubernetes 所执行的合法性检查操作以及对其所实施的限制各不相同。
内置类型 | 用法 |
---|---|
Opaque |
用户定义的任意数据,使用base64编码存储信息,可以通过base64 --decode 解码获得原始数据,因此安全性弱。 |
kubernetes.io/service-account-token |
服务账号令牌,用于被 serviceaccount 引用。serviceaccout 创建时 Kubernetes 会默认创建对应的 secret。Pod 如果使用了 serviceaccount,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。 |
kubernetes.io/dockercfg |
~/.dockercfg 文件的序列化形式 |
kubernetes.io/dockerconfigjson |
~/.docker/config.json 文件的序列化形式,用于存储docker registry的认证信息。 |
kubernetes.io/basic-auth |
用于基本身份认证的凭据 |
kubernetes.io/ssh-auth |
用于 SSH 身份认证的凭据 |
kubernetes.io/tls |
用于 TLS 客户端或者服务器端的数据 |
bootstrap.kubernetes.io/token |
启动引导令牌数据 |
通过为 Secret 对象的 type
字段设置一个非空的字符串值,你也可以定义并使用自己 Secret 类型。如果 type
值为空字符串,则被视为 Opaque
类型。 Kubernetes 并不对类型的名称作任何限制。不过,如果你要使用内置类型之一, 则你必须满足为该类型所定义的所有要求。
2.1 Opaque Secret
当 Secret 配置文件中未作显式设定时,默认的 Secret 类型是 Opaque
。 当你使用 kubectl
来创建一个 Secret 时,你会使用 generic
子命令来标明 要创建的是一个 Opaque
类型 Secret。 例如,下面的命令会创建一个空的 Opaque
类型 Secret 对象:
kubectl create secret generic empty-secret kubectl get secret empty-secret
输出类似于
NAME TYPE DATA AGE empty-secret Opaque 0 2m6s
DATA
列显示 Secret 中保存的数据条目个数。 在这个例子种,0
意味着我们刚刚创建了一个空的 Secret。
2.2 服务账号令牌 Secret
类型为 kubernetes.io/service-account-token
的 Secret 用来存放标识某 服务账号的令牌。使用这种 Secret 类型时,你需要确保对象的注解 kubernetes.io/service-account-name
被设置为某个已有的服务账号名称。 某个 Kubernetes 控制器会填写 Secret 的其它字段,例如 kubernetes.io/service-account.uid
注解以及 data
字段中的 token
键值,使之包含实际的令牌内容。
下面的配置实例声明了一个服务账号令牌 Secret:
apiVersion: v1 kind: Secret metadata: name: secret-sa-sample annotations: kubernetes.io/service-account.name: "sa-name" type: kubernetes.io/service-account-token data: # 你可以像 Opaque Secret 一样在这里添加额外的键/值偶对 extra: YmFyCg==
Kubernetes 在创建 Pod 时会自动创建一个服务账号 Secret 并自动修改你的 Pod 以使用该 Secret。该服务账号令牌 Secret 中包含了访问 Kubernetes API 所需要的凭据。
如果需要,可以禁止或者重载这种自动创建并使用 API 凭据的操作。 不过,如果你仅仅是希望能够安全地访问 API 服务器,这是建议的工作方式。
$ kubectl run nginx --image nginx deployment "nginx" created $ kubectl get pods NAME READY STATUS RESTARTS AGE nginx-3137573019-md1u2 1/1 Running 0 13s $ kubectl exec nginx-3137573019-md1u2 ls /run/secrets/kubernetes.io/serviceaccount ca.crt namespace token
2.3 Docker 配置 Secret(kubernetes.io/dockerconfigjson)
kubernetes.io/dockerconfigjson用于存储docker registry的认证信息,可以直接使用kubectl create secret
命令创建:
kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL secret "myregistrykey" created.
查看secret的内容:
kubectl get secret myregistrykey -o yaml apiVersion: v1 data: .dockerconfigjson: eyJjY3IuY2NzLnRlbmNlbnR5dW4uY29tL3RlbmNlbnR5dW4iOnsidXNlcm5hbWUiOiIzMzIxMzM3OTk0IiwicGFzc3dvcmQiOiIxMjM0NTYuY29tIiwiZW1haWwiOiIzMzIxMzM3OTk0QHFxLmNvbSIsImF1dGgiOiJNek15TVRNek56azVORG94TWpNME5UWXVZMjl0In19 kind: Secret metadata: name: myregistrykey ...... type: kubernetes.io/dockerconfigjson
在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey:
apiVersion: v1 kind: Pod metadata: name: foo spec: containers: - name: foo image: janedoe/awesomeapp:v1 imagePullSecrets: - name: myregistrykey
3、Secret使用
Secret 可通过命令行或 YAML 创建。比如希望 Secret 中包含如下信息:
-
用户名 zmc
-
密码
123456
3.1 创建 Secret方式
有四种方法创建 Secret:
(1)通过 --from-literal
:
kubectl create secret generic mysecret --from-literal=username=zmc --from-literal=password=123456
每个 --from-literal
对应一个信息条目。
(2)通过 --from-file
:
echo -n zmc > ./username echo -n 123456 > ./password kubectl create secret generic mysecret --from-file=./username --from-file=./password
每个文件内容对应一个信息条目。
(3)通过 --from-env-file
:
cat << EOF > env.txt username=zmc password=123456 EOF kubectl create secret generic mysecret --from-env-file=env.txt
文件 env.txt
中每行 Key=Value 对应一个信息条目。
(4)通过 YAML 配置文件:
apiVersion: v1 kind: Secret metadata: name: mysecret namespace: default type: Opaque data: username: em1j password: MTIzNDU2
文件中的敏感数据必须是通过 base64 编码后的结果。
[root@master1 ~]# echo -n zmc|base64 em1j [root@master1 ~]# echo -n 123456|base64 MTIzNDU2
执行 kubectl apply
创建 Secret:
[root@master1 ~]# kubectl apply -f mysecret.yaml secret/mysecret created
3.2 查看secret
(1)通过 kubectl get secret
查看存在的 secret
显示有两个数据条目
(2)通过kubectl describe secret
查看条目的 Key
[root@master1 ~]# kubectl describe secret mysecret Name: mysecret Namespace: default Labels: <none> Annotations: Type: Opaque Data ==== password: 6 bytes username: 3 bytes
(3)通过kubectl edit secret mysecret 查看vlaue
apiVersion: v1 data: password: MTIzNDU2 username: em1j kind: Secret metadata: annotations: ...... name: mysecret namespace: default resourceVersion: "307042063" selfLink: /api/v1/namespaces/default/secrets/mysecret uid: 679f748d-cff7-41fa-9bb1-bcee1f00a0fd type: Opaque
(4)通过base64将value反解码
[root@master1 ~]# echo -n MTIzNDU2|base64 --decode 123456[root@master1 ~]# echo -n em1j|base64 --decode zmc
3.3 vloume方式secret的使用
Pod 可以通过 Volume 或者环境变量的方式使用 Secret,先学习 Volume 方式。
Pod 的配置文件如下所示(username: admin password:123456):
① 定义 volume foo
,来源为 secret mysecret
。
② 将 foo
mount 到容器路径 /etc/foo
,可指定读写权限为 readOnly
。
创建 Pod 并在容器中读取 Secret:
可以看到,Kubernetes 会在指定的路径 /etc/foo
下为每条敏感数据创建一个文件,文件名就是数据条目的 Key,这里是 /etc/foo/username
和 /etc/foo/password
,Value 则以明文存放在文件中。
我们也可以自定义存放数据的文件名,比如将配置文件改为:
这时数据将分别存放在 /etc/foo/my-group/my-username
和 /etc/foo/my-group/my-password
中。
以 Volume 方式使用的 Secret 支持动态更新:Secret 更新后,容器中的数据也会更新。
将 password 更新为 abcdef
,base64 编码为 YWJjZGVm
3.4 环境变量中使用secret
通过 Volume 使用 Secret,容器必须从文件读取数据,会稍显麻烦,Kubernetes 还支持通过环境变量使用 Secret。
创建 Pod 并读取 Secret。
通过环境变量 SECRET_USERNAME
和 SECRET_PASSWORD
成功读取到 Secret 的数据。
需要注意的是,环境变量读取 Secret 很方便,但无法支撑 Secret 动态更新。
4、并不安全的Secret
其实目前Secret的实现,就是ConfigMap把value用base64 encode了一下,所以,其实不存在任何安全性,只要decode一下就能出现原来结果,相当于明文存储。base64这玩意儿都不能叫做加密,只能叫做编码,所以我们都不说encrypt,而是encode和decode。
5、总结
- Secret 可以为 Pod 提供密码、Token、私钥等敏感数据;对于一些非敏感数据,比如应用的配置信息,则可以用 ConfigMap。
- Kubernetes 并不对Secret类型的名称作任何限制。不过,如果你要使用内置类型之一, 则你必须满足为该类型所定义的所有要求。
- 以 Volume 方式使用的 Secret 支持动态更新:Secret 更新后,容器中的数据也会更新。
- 环境变量读取 Secret 很方便,但无法支撑 Secret 动态更新。
- Secret的值在Pod里面都是以明文形式显示。
- Secret其实不存在任何安全性,只要decode一下就能出现原来结果,相当于明文存储。
参考:https://kubernetes.io/zh/docs/concepts/configuration/secret/