Kubernetes基于Secret存储敏感数据
1 简介Secret
1.1 用途
- 用途:Kubernetes的Secret对象与ConfigMap对象类似,只不过ConfigMap一般用于存储普通配置信息,而Secret一般用于存储敏感配置信息(例如密码、OAuth Token、SSH Key等)。通过把敏感配置信息单独存储在Secret对象中,而不是直接存储在Pod中或Docker镜像中,能够更加的安全和灵活。
1.2 类型
-
类型:Kubernetes中的Secret对象一共有三种类型。
类型名称 说明 Opaque 用于存储Base64编码后的配置信息,这种类型的配置信息可以通过环境变量或挂载的形式传给Pod使用。该类型的信息可以经Base64解码而获得原始数据,因此安全性较低,因此不建议将密码等敏感信息放入到这种类型的Secret中。 kubernetes.io/dockerconfigjson 用于存储Docker Registry的认证信息,K8S集群到Docker镜像仓库拉取镜像时,需要使用这种类型的Secret进行Docker身份认证,只有认证通过后,才能成功拉取镜像。 kubernetes.io/service-account-token 用于被ServiceAccount引用。ServiceAccount创建时,Kubernetes会默认创建对应的Secret,Pod如果使用了ServiceAccount,对应的Secret会自动挂载到Pod的/run/secrets/kubernetes.io/serviceAccount目录中
2 Opaque类型的Secret
2.1 创建Secret
2.1.1 使用命令的方式创建
- 说明:通过使用命令的方式创建Opaque类型的Secret,需要指定Secret的类型为 generic。
2.1.1.1 通过文字值创建
- 说明:通过文字值创建Secret时,需要传入密码等敏感配置信息的原始数据,Kubernetes会自动地将这些原始数据通过Base64进行编码,并使用编码后的值来创建Secret。
- 创建:在Kubelet客户端中通过执行以下命令即可创建一个名称为secret-1的Secret。
kubectl create secret generic secret-1 --from-literal=username=admin --from-literal=password=123456
- 查看:上述命令执行成功之后,即可执行以下命令以查看创建成功的Secret,结果截图如下。
kubectl get secret secret-1 -o yaml
2.1.1.2 通过目录创建
-
说明:通过目录创建Secret时,目录下的文件名(包括后缀名)即为Secret中的Key,目录下的文件的内容经Base64编码后的值即为Secret中对应Key的Value。
-
文件:例如在Kubelet客户端中的当前目录下的secret目录中有如下4个文件,其文件名和内容分别如下所示:
# secret/username admin # secret/username.txt admin # secret/password 123456 # secret/password.txt 123456
-
创建:在Kubelet客户端中通过执行以下命令即可创建一个名称为secret-2的Secret。
kubectl create secret generic secret-2 --from-file=./secret
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建成功的Secret,结果截图如下。
kubectl get secret secret-2 -o yaml
2.1.1.3 通过文件创建
-
说明:通过文件创建Secret时,如果不指定生成的Key,则文件名(包括后缀名)即为Secret中的key,文件的内容经Base64编码后的值即为Secret中对应Key的Value。
-
文件:例如在Kubelet客户端中的当前目录下的secret目录中下有如下2个文件,其文件名和内容分别如下所示:
# secret/username.txt admin # secret/password.txt 123456
-
创建:在Kubelet客户端中通过执行以下命令即可创建一个名称为secret-3的Secret和创建一个名称为secret-4的Secret。
# 不指定生成的Key kubectl create secret generic secret-3 --from-file=./secret/username.txt --from-file=./secret/password.txt # 指定生成的Key kubectl create secret generic secret-4 --from-file=username=./secret/username.txt --from-file=password=./secret/password.txt
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建成功的Secret,结果截图如下。
kubectl get secret secret-3 -o yaml kubectl get secret secret-4 -o yaml
2.1.1.4 通过配置文件创建
-
说明:通过Porperties配置文件创建Secret时,Properties文件中的Key即为Secret中的Key,Properties文件中的Value经Base64编码后的值即为Secret中对应Key的Value。
-
文件:例如在Kubelet客户端中的当前目录下有如下1个Properties配置文件,其内容如下所示
# secret.properties username=admin password=123456
-
创建:在Kubelet客户端中通过执行以下命令即可创建一个名称为secret-5的Secret。
kubectl create secret generic secret-5 --from-env-file=./secret.properties
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建成功的Secret,结果截图如下。
kubectl get secret secret-5 -o yaml
2.1.2 使用声明的方式创建
-
说明:通过使用声明的方式创建Opaque类型的Secret,需要指定Secret的类型为 Opaque,并且需要传入密码等敏感信息的原始数据经由Base64编码后的值,而不是密码等敏感信息的原始数据。
-
编码:在Shell命令行中执行以下命令,即可生成username和password的值经由Base64编码后的值。
echo -n 'admin' | base64 echo -n '123456' | base64
-
yaml:在Kubelet客户端中的当前目录下创建一个名称为secret.yaml的文件,其内容如下所示。
apiVersion: v1 kind: Secret metadata: name: secret-6 type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm
-
创建:在Kubelet客户端中通过执行以下命令即可使用上述yaml文件创建一个名称为secret-6的Opaque类型的Secret。
kubectl apply -f ./secret.yaml
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建成功的Secret,结果截图如下。
kubectl get secret secret-6 -o yaml
2.2 使用Secret
2.2.1 通过环境变量的方式使用
-
说明:Kubernetes中的Secret对象中的配置项,可以通过环境变量的方式传给Pod使用,然后再在Pod中使用这些环境变量,这种方式要求指定Secret中的各个Key传给Pod中的哪个环境变量,而Secret中各个Key对应的Value经Base64解码后的值即为传给Pod的环境变量的值。
-
yaml:在Kubelet客户端中的当前目录下创建一个名称为secret-env-pod.yml文件,其内容如下所示。
apiVersion: v1 kind: Pod metadata: name: secret-env-pod spec: containers: - name: busybox image: busybox:latest command: [ "/bin/sh", "-c", "echo $SECRET_USERNAME; echo $SECRET_PASSWORD" ] env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: secret-1 key: username - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: secret-1 key: password restartPolicy: Never
-
创建:在Kubelet客户端中通过执行以下命令即可使用上述yaml文件创建一个名称为secret-env-pod的Pod,在该Pod中会使用在2.1.1.1小节中创建的名称为secret-1的Secret,在该Pod中会打印出名称为SECRET_USERNAME和SECRET_PASSWORD这两个环境变量的值。
kubectl apply -f secret-env-pod.yml
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建的Pod的运行日志,从中可以看出成功打印出了Secret中的username和password的值,结果截图如下
kubectl logs -f secret-env-pod
2.2.2 通过挂载的方式使用
- 说明:Kubernetes中的Secret对象中的配置项,可以通过挂载的方式挂载到Pod中的指定路径下的文件中,然后再在Pod使用这些文件,挂载的时候可以指定Secret中每个Key挂载的文件名,也可以不指定Secret中每个Key挂载的文件名。
2.2.2.1 不指定Key挂载的文件名
-
说明:如果在挂载的时候没有指定Secret中每个Key要挂载的文件名,则Secret中的Key即为挂载后的文件名,而Secret中对应Key的Value经Base64解码后的值即为挂载后的文件的内容。这种情况下,Secret中所有的Key都会挂载到指定目录中。
-
yaml:在Kubelet客户端中的当前目录下创建一个名称为secret-mount-pod.yml文件,其内容如下所示。
apiVersion: v1 kind: Pod metadata: name: secret-mount-pod spec: containers: - name: busybox image: busybox:latest command: [ "/bin/sh", "-c", "ls /etc/secret; cat /etc/secret/username; cat /etc/secret/password" ] volumeMounts: - name: secret mountPath: "/etc/secret" readOnly: true volumes: - name: secret secret: secretName: secret-2 restartPolicy: Never
-
创建:在Kubelet客户端中通过执行以下命令即可使用上述yaml文件创建一个名称为secret-mount-pod的Pod,在该Pod中会使用在2.1.1.2小节中创建的名称为secret-2的Secret,在该Pod中会展示出/etc/secret目录下的所有文件名称,并打印出该目录下的名称为username和password的文件中的内容。
kubectl apply -f secret-mount-pod.yml
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建的Pod的运行日志,从中可以看出/etc/secret目录下存在4个文件,这些文件的名称(包括后缀名)即为Secret中的Key,该目录下的名称为username的文件中的内容即为Secret中名称为username的Key的Value经由Base64解码后的值,结果截图如下。
kubectl logs -f secret-mount-pod
2.2.2.2 指定Key挂载的文件名
-
说明:在挂载的时候还可以指定Secret中每个Key要挂载的文件名,则Secret中对应Key的Value经Base64解码后的值即为挂载后的文件的内容。这种情况下,只有指定的Secret中的Key才会挂载到指定目录中,而没有指定的Secret中的Key则不会挂载到指定目录中。
-
yaml:在Kubelet客户端中的当前目录下创建一个名称为secret-mount-pod-spec.yml文件,其内容如下所示。
apiVersion: v1 kind: Pod metadata: name: secret-mount-pod-spec spec: containers: - name: busybox image: busybox:latest command: [ "/bin/sh", "-c", "ls /etc/secret; ls /etc/secret/auth; cat /etc/secret/auth/login_username; cat /etc/secret/auth/login_password" ] volumeMounts: - name: secret mountPath: "/etc/secret" readOnly: true volumes: - name: secret secret: secretName: secret-2 items: - key: username.txt path: auth/login_username - key: password.txt path: auth/login_password restartPolicy: Never
-
创建:在Kubelet客户端中通过执行以下命令即可使用上述yaml文件创建一个名称为secret-mount-pod-spec的Pod,在该Pod中会使用在2.1.1.2小节中创建的名称为secret-2的Secret,在该Pod中会展示出/etc/secret目录及/etc/secret/auth目录下的所有文件名称,并打印出该/etc/secret/auth目录下的名称为login_username和login_password的文件中的内容。
kubectl apply -f secret-mount-pod-spec.yml
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建的Pod的运行日志,从中可以看出/etc/secret目录只有auth子目录,而/etc/secret/auth目录下也只有2个文件,而/etc/secret/auth目录下的名称为login_username的文件中的内容即为Secret中名称为username.txt的Key的Value经由Base64解码后的值,结果截图如下。
kubectl logs -f secret-mount-pod-spec
3 DockerConfigJson类型的Secret
3.1 创建Secret
3.1.1 使用命令的方式创建
-
说明:通过使用命令的方式创建DockerConfigJson类型的Secret,需要指定Secret的类型为 docker-registry。需要指定Docker仓库的域名地址、登录名、登录密码等敏感配置信息的原始数据,Kubernetes会自动地将这些原始数据通过Base64进行编码,并使用编码后的值来创建Secret。
-
创建:在Kubelet客户端中通过执行以下命令即可创建一个名称为docker-secret-1的DockerConfigJson类型的Secret。
kubectl create secret docker-registry docker-secret-1 --docker-server=registry.k8s.com --docker-username=admin --docker-password=Harbor12345
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建成功的Secret,结果截图如下。
kubectl get secret docker-secret-1 -o yaml
-
解码:从上面的输出结果可知,Docker认证信息经过Base64编码生成了一个字符串,并放在了.dockerconfigjson字段中,为了弄清楚是如何使用docker认证信息构造Base64编码的字符串,把生成的Base64编码的字符串复制出来,并通过在Shell命令行中执行以下命令进行解码,其结果如下所示。
echo eyJhdXRocyI6eyJyZWdpc3RyeS5rOHMuY29tIjp7InVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6IkhhcmJvcjEyMzQ1IiwiYXV0aCI6IllXUnRhVzQ2U0dGeVltOXlNVEl6TkRVPSJ9fX0= | base64 -d
-
进一步解码:从上图中可知,解码出来的结果是一个JSON对象,且JSON对象中的auth字段的值仍然是一个Base64编码的字符串,把auth字段的值复制出来,并通过在Shell命令行中执行以下命令进一步解码,其结果如下所示:
echo YWRtaW46SGFyYm9yMTIzNDU= | base64 -d
-
结果:通过上述的两次解码后,可以发现,在使用命令的方式生成DockerConfigJson类型的Secret时,是通过以下的JSON格式组织Docker认证信息,并把JSON对象作为字符串进行Base64解码。
Base64({"auths":{"域名地址":{"username":"登录名","password":"登录密码","auth":"Base64(登录名:登录密码)"}}})
3.1.2 使用声明的方式创建
-
说明:通过使用声明的方式创建DockerConfigJson类型的Secret,需要指定Secret的类型为 kubernetes.io/dockerconfigjson。通过3.1.1小节可知,Docker认证信息需要经过两次Base64编码才能被DockerConfigJson类型的Secret使用,并且还需要使用以下的JSON格式组织Docker认证信息。
Base64({"auths":{"域名地址":{"username":"登录名","password":"登录密码","auth":"Base64(登录名:登录密码)"}}})
-
编码:在Shell命令行中通过执行以下命令对Docker的认证信息进行第一次编码。
echo 'admin:Harbor12345' | base64
-
进一步编码:上一步编码完成之后,使用上一步编码生成的结果以及Docker认证的信息再一次进行编码,在Shell命令行中通过执行以下命令对Docker的认证信息进行第二次编码,第二次编码生成的结果即为可用于生成DockerConfigJson类型的Secret的值。注意:第二次编码的结果可能会有多行,需要拼接在一起进行使用。
echo '{"auths":{"registry.k8s.com":{"username":"admin","password":"Harbor12345","auth":"YWRtaW46SGFyYm9yMTIzNDUK"}}}' | base64
-
yaml:在Kubelet客户端中的当前目录下创建一个名称为docker-secret.yml文件,并使用上一步中生成的Docker认证信息的Base64编码格式的值作为.dockerconfigjson字段的值,其内容如下所示。
apiVersion: v1 kind: Secret metadata: name: docker-secret-2 type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5rOHMuY29tIjp7InVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6IkhhcmJvcjEyMzQ1IiwiYXV0aCI6IllXUnRhVzQ2U0dGeVltOXlNVEl6TkRVSyJ9fX0K
-
创建:在Kubelet客户端中通过执行以下命令即可使用上述yaml文件创建一个名称为docker-secret-2的DockerConfigJson类型的Secret。
kubectl apply -f docker-secret.yml
-
查看:上述命令执行成功之后,即可执行以下命令以查看创建成功的Secret,结果截图如下。
kubectl get secret docker-secret-2 -o yaml
3.2 使用Secret
- 说明:DockerConfigJson类型的Secret用于K8S集群从Docker镜像仓库拉取镜像时进行认证使用,因此,需要在创建Pod的yaml中指定用于K8S集群从Docker镜像仓库中拉取镜像的Docker认证信息,即要指定DockerConfigJson类型的Secret。
- yaml:在Kubelet客户端中的当前目录下创建一个名称为docker-secret-pod.yml文件,并指定K8S集群从Docker镜像仓库中拉取registry.k8s.com/k8s/busybox:latest镜像时,使用3.1.1小节中生成的名称为docker-secret-1的DockerConfigJson类型的Secret进行Docker认证,其内容如下所示。
apiVersion: v1 kind: Pod metadata: name: docker-secret-pod spec: containers: - name: busybox image: registry.k8s.com/k8s/busybox:latest command: [ "/bin/sh", "-c", "echo hello world" ] imagePullSecrets: - name: docker-secret-1 restartPolicy: Never
- 创建:在Kubelet客户端中通过执行以下命令即可使用上述yaml文件创建一个名称为docker-secret-pod的Pod,该Pod中会打印一句 “hello world”。
kubectl apply -f docker-secret-pod.yml
- 查看:上述命令执行成功之后,即可执行以下命令以查看Pod是否创建成功,以及查看Pod的运行日志,从中可以看出Pod创建成功,且Pod成功的向日志中打印出了"hello world"这句话,结果截图如下。
kubectl get pods kubectl logs -f docker-secret-pod
- 补充:如果在上述docker-secret-pod.yml中没有指定imagePullSecrets字段,则使用该yml文件创建Pod时,由于K8S从Docker镜像仓库拉取镜像时没有Docker认证信息,因此会拉取失败,从而看到的结果如下图所示。