Kubernetes对象——Secret

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 服务器,这是建议的工作方式。

Pod 如果使用了 serviceaccount,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。
示例:
$ 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 中包含如下信息:

  1. 用户名 zmc

  2. 密码 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/passwordValue 则以明文存放在文件中。

 我们也可以自定义存放数据的文件名,比如将配置文件改为:

 

这时数据将分别存放在 /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/

参考:https://www.cnblogs.com/benjamin77/p/9960624.html

posted @ 2022-01-18 00:21  人艰不拆_zmc  阅读(879)  评论(0编辑  收藏  举报