theboyaply
theboyaply
发布于 2020-03-30 / 543 阅读
0
0

k8s Secret介绍

参考:https://kubernetes.io/zh/docs/concepts/configuration/secret/

k8s Secret介绍

Secret 是一种包含少量敏感信息例如密码、token 或 key 的对象。这样的信息可能会被放在 Pod spec 中或者镜像中;将其放在一个 secret 对象中可以更好地控制它的用途,并降低意外暴露的风险。

用户可以创建 secret,同时系统也创建了一些 secret。

要使用 secret,pod 需要引用 secret。Pod 可以用三种方式使用 secret:作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里;作为环境变量注入;或者当 kubelet 为 pod 拉取镜像时使用。

Secret 有三种类型:

  • kubernetes.io/service-account-token:这是 Service Account 使用 API 凭证自动创建和附加 secret。Kubernetes 自动创建包含访问 API 凭据的 secret,并自动修改您的 pod 以使用此类型的 secret。

    如果需要,可以禁用或覆盖自动创建和使用API凭据。但是,如果您需要的只是安全地访问 apiserver,我们推荐这样的工作流程。

  • kubernetes.io/dockerconfigjson:用来存储私有 docker registry 的认证信息。使用 imagePullSecrets策略指定 Secret。

  • Opaque:base64 编码格式的 Secret,用来存储密码、密钥等;但数据也可以通过 base64 –decode 解码得到原始数据,所有加密性很弱。

Service Account

这种是 k8s 集群可以自己管理的一种加密数据,如果没有特殊需求,可以不对其进行修改,这里不详细介绍。

Opaque Secret

使用文件创建

假设有些 pod 需要访问数据库。这些 pod 需要使用的用户名和密码在您本地机器的 ./username.txt./password.txt 文件里。

echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt

kubectl create secret 命令将这些文件打包到一个 Secret 中并在 API server 中创建了一个对象。

[root@k8s-master01 secret]# kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
secret/db-user-pass created

这时查看 Secret 你会发现,k8s 会自动将明文的 username 和 password 内容改为 base64 编码格式:

[root@k8s-master01 secret]# kubectl get secret
NAME                  TYPE                                  DATA   AGE
db-user-pass          Opaque                                2      3m38s
default-token-5997c   kubernetes.io/service-account-token   3      12d

[root@k8s-master01 secret]# kubectl get secret db-user-pass -o yaml
apiVersion: v1
data:
  password.txt: MWYyZDFlMmU2N2Rm
  username.txt: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: "2020-03-30T14:35:56Z"
  name: db-user-pass
  namespace: default
  resourceVersion: "267060"
  selfLink: /api/v1/namespaces/default/secrets/db-user-pass
  uid: 08f3315f-e21e-4680-b54b-a980dc9829e7
type: Opaque

使用 yaml 清单创建

Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式。

下面我们对一个账号密码进行加密:

[root@k8s-master01 secret]# echo -n "admin" | base64
YWRtaW4=
[root@k8s-master01 secret]# echo -n "123456" | base64
MTIzNDU2

创建 mysecret.yaml 文件:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MTIzNDU2

使用 kubectl apply 创建:

[root@k8s-master01 secret]# kubectl apply -f mysecret.yaml 
secret/mysecret created

[root@k8s-master01 secret]# kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-5997c   kubernetes.io/service-account-token   3      12d
mysecret              Opaque                                2      6s

查看 Secret 依然是 base64 编码格式保存的内容:

[root@k8s-master01 secret]# kubectl get secret mysecret -o yaml
apiVersion: v1
data:
  password: MTIzNDU2
  username: YWRtaW4=
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"password":"MTIzNDU2","username":"YWRtaW4="},"kind":"Secret","metadata":{"annotations":{},"name":"mysecret","namespace":"default"},"type":"Opaque"}
  creationTimestamp: "2020-03-30T14:28:32Z"
  name: mysecret
  namespace: default
  resourceVersion: "266360"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: cb6a9cd6-173f-4a50-a061-7a4d3c8542ca
type: Opaque

解码 Secret

从 Secret 中获取到的 base64 编码格式的内容,也可以使用 base64 编码格式转化为明文:

[root@k8s-master01 secret]# kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-5997c   kubernetes.io/service-account-token   3      12d
mysecret              Opaque                                2      18m

[root@k8s-master01 secret]# kubectl get secret mysecret -o yaml
apiVersion: v1
data:
  password: MTIzNDU2
  username: YWRtaW4=
......

[root@k8s-master01 secret]# echo -n "MTIzNDU2" | base64 -d
123456
[root@k8s-master01 secret]# echo -n "YWRtaW4=" | base64 -d
admin

编辑 Secret

可以通过下面的命令编辑一个已经存在的 secret 。

kubectl edit secrets mysecret

这将打开默认配置的编辑器,并允许更新 data 字段中的 base64 编码的 secret:

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  password: MTIzNDU2
  username: YWRtaW4=
kind: Secret
......

使用 Secret

作为 volume 使用

Secret 可以作为数据卷被挂载,或作为环境变量暴露出来以供 Pod 中的容器使用。它们也可以被系统的其他部分使用,而不直接暴露在 Pod 内。 例如,它们可以保存凭据,系统的其他部分应该用它来代表您与外部系统进行交互。

在 Pod 中的 volume 里使用 Secret

  • 创建一个 secret 或者使用已有的 secret。多个 pod 可以引用同一个 secret。
  • 修改 pod 的定义在 spec.volumes[] 下增加一个 volume。可以给这个 volume 随意命名,它的 spec.volumes[].secret.secretName 必须等于 secret 对象的名字。
  • spec.containers[].volumeMounts[] 加到需要用到该 secret 的容器中。指定 spec.containers[].volumeMounts[].readOnly = truespec.containers[].volumeMounts[].mountPath 为您想要该 secret 出现的尚未使用的目录。
  • 修改您的镜像并且/或者命令行让程序从该目录下寻找文件。Secret 的 data 映射中的每一个键都成为了 mountPath 下的一个文件名。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  volumes:
  - name: foo
    secret:
      secretName: mysecret
  containers:
  - name: mypod
    image: hub.xixihaha.com/library/mynginx:v1
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true

想要用的每个 secret 都需要在 spec.volumes 中指明。

如果 pod 中有多个容器,每个容器都需要自己的 volumeMounts 配置块,但是每个 secret 只需要一个 spec.volumes

可以打包多个文件到一个 secret 中,或者使用的多个 secret,怎样方便就怎样来。

向特性路径映射 secret 密钥

我们还可以控制 Secret key 映射在 volume 中的路径。您可以使用 spec.volumes[].secret.items 字段修改每个 key 的目标路径:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username
  containers:
  - name: mypod
    image: hub.xixihaha.com/library/mynginx:v1
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true

效果如下:

  • username secret 存储在 /etc/foo/my-group/my-username 文件中而不是 /etc/foo/username 中。
  • password secret 没有被映射。

如果使用了 spec.volumes[].secret.items,只有在 items 中指定的 key 被映射。要使用 secret 中所有的 key,所有这些都必须列在 items 字段中。所有列出的密钥必须存在于相应的 secret 中。否则,不会创建卷。

挂载的 secret 被自动更新

当已经在 volume 中被消费的 secret 被更新时,被映射的 key 也将被更新。 Kubelet 在周期性同步时检查被挂载的 secret 是不是最新的。 但是,它正在使用其本地缓存来获取 Secret 的当前值。

缓存的类型可以使用 (ConfigMapAndSecretChangeDetectionStrategy 中的 KubeletConfiguration 结构). 它可以通过基于 ttl 的 watch(默认)传播,也可以将所有请求直接重定向到直接kube-apiserver。 结果,从更新密钥到将新密钥投射到 Pod 的那一刻的总延迟可能与 kubelet 同步周期 + 缓存传播延迟一样长,其中缓存传播延迟取决于所选的缓存类型。 (它等于观察传播延迟,缓存的ttl或相应为0)。

作为环境变量使用

将 secret 作为 pod 中的环境变量使用:

  • 创建一个 secret 或者使用一个已存在的 secret。多个 pod 可以引用同一个 secret。
  • 修改 Pod 定义,为每个要使用 secret 的容器添加对应 secret key 的环境变量。消费 secret key 的环境变量应填充 secret 的名称,并键入 env[x].valueFrom.secretKeyRef
  • 修改镜像并/或者命令行,以便程序在指定的环境变量中查找值。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: hub.xixihaha.com/library/mynginx:v1
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

效果如下:

[root@k8s-master01 secret]# kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
secret-env-pod   1/1     Running   0          2m45s

[root@k8s-master01 secret]# kubectl exec secret-env-pod -it /bin/sh
# echo $SECRET_USERNAME
admin
# echo $SECRET_PASSWORD
123456

ImagePullSecret

假如我们创建 Pod 时,Pod 里面所使用的镜像是私有的,需要进行身份认证才能够拉取,那么我们可以创建一个具有该私有仓库认证信息的 Secret,然后使用 ImagePullSecrets 指定。

下面我们使用 harbor 私有仓库进行举例。在 harbor 中创建一个私有项目 tom,并向该项目中 push 一个镜像 hub.xixihaha.com/tom/nginx:v1。然后将集群中的所有节点都退出 harbor 的登录,并删除所有节点中的 hub.xixihaha.com/tom/nginx:v1 镜像。

认证失败示例

apiVersion: v1
kind: Pod
matadata:
  name: my-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xixihaha.com/tom/nginx:v1

创建这个 Pod:

[root@k8s-master01 secret]# kubectl create -f my-pod.yaml 
pod/my-pod created

[root@k8s-master01 secret]# kubectl get pod
NAME     READY   STATUS             RESTARTS   AGE
my-pod   0/1     ImagePullBackOff   0          3s

[root@k8s-master01 secret]# kubectl describe pod my-pod
Name:         my-pod
Namespace:    default
......
Events:
  Type     Reason     Age                From                 Message
  ----     ------     ----               ----                 -------
  Normal   Scheduled  13s                default-scheduler    Successfully assigned default/my-pod to k8s-node02
  Normal   Pulling    12s                kubelet, k8s-node02  Pulling image "hub.xixihaha.com/tom/nginx:v1"
  Warning  Failed     12s                kubelet, k8s-node02  Failed to pull image "hub.xixihaha.com/tom/nginx:v1": rpc error: code = Unknown desc = Error response from daemon: pull access denied for hub.xixihaha.com/tom/nginx, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
  Warning  Failed     12s                kubelet, k8s-node02  Error: ErrImagePull
  Normal   BackOff    10s (x2 over 11s)  kubelet, k8s-node02  Back-off pulling image "hub.xixihaha.com/tom/nginx:v1"
  Warning  Failed     10s (x2 over 11s)  kubelet, k8s-node02  Error: ImagePullBackOff

从上面信息中很容易知道是因为没有权限拉取镜像导致的失败。

创建认证信息 Secret

kubectl create secret docker-registry my-registry-secret --docker-server=hub.xixihaha.com --docker-username=admin --docker-password=Harbor12345 --docker-email=admin@example.com

说明:

  • docker-registry:固定字段。
  • my-registry-secret:该 secret 的名称。

在 yaml 中添加 ImagePullSecrets 信息:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xixihaha.com/tom/nginx:v1
  imagePullSecrets:
  - name: my-registry-secret

创建 Pod ,可以发现已经正常拉取镜像并运行:

[root@k8s-master01 secret]# kubectl create -f my-pod.yaml 
pod/my-pod created

[root@k8s-master01 secret]# kubectl get pod
NAME     READY   STATUS    RESTARTS   AGE
my-pod   1/1     Running   0          7s

-- end --


评论