Secret资源

Secret资源的功能类似于ConfigMao,但它专用于存储敏感数据,例如密码,数字证书,秘钥,令牌和sshkey等

Secret概述

Secret对象存储数据的方式及使用方式类似于ConfigMao对象,以键值方式存储数据,在Pod资源中通过环境变量或存储卷进行数据访问。不同的是,Secret对象仅会被分发至调用了此对象的Pod资源所在的工作节点,且只能由节点将其存储于内存中。另外,Secret对象的数据的存储及打印格式为Base64编码的字符串,因此用户在创建Secret对象时也要提供此种编码格式的数据。不过,在容器中以环境变量或存储卷的方式访问时,它们会被自动解码为明文格式

需要注意的是,在Master节点上,Secret对象以非加密的格式存储于ETCD中,因此管理员必须加以精心管控以确保敏感数据的机密性,必须确保etcd集群节点间以及与API Server的安全通信,etcd服务的访问授权,还包括用户访问API Server时的授权,因为拥有创建Pod资源的用户都可以用secret资源并能够通过Pod中的容器访问其数据

Secret对象主要有两种用途,一是作为存储卷注入到Pod上由容器应用程序所使用,二是用于kubelet为Pod里的容器拉取镜像时向私有仓库提供认证信息。不过,后面使用ServiceAccount资源自建的Secret对象是一种更具安全性的方式。通过ConfigMap和Secret配置容器的方式如下图: 配置容器应用(四)之Secret资源 - 图1

Secret资源主要由四种类型组成,具体如下:

  • Opaque:自定义数据内容;base64编码,用来存储密码,秘钥,信息,证书等数据,类型表示符为generic
  • kubernetes.io/service-account-token: Service Account的认证信息,可以在创建ServiceAccount时由kubernetes自动创建
  • kubernetes.io/dockerconfigjson:用来存储docker镜像仓库的认证信息,类型标识为docker-registry
  • kubernetes.io/tls:用于为ssl通信模式存储证书和私钥文件,命令式创建时类型标识为TLS

注意:base64编码并非加密机制,其编码的数据可使用”base64 —decode”一类的命令进行解码

创建Secret资源

手动创建Secret对象的方式有两种,第一种是通过kubectl create进行命令式创建,第二种是使用Secret资源清单进行创建

命令式创建

不少场景中,Pod中的应用需要通过用户名和密码访问其他服务,例如访问数据系统等。创建此类的Secret对象,可以使用kubectl create secret generic <SECRET_NAME> --from-literal=key=value命令直接进行创建,不过为用户认证之需进行创建时,其使用的键名通常是username和password。例如下面的命令,以”root/ikubernetes”分别为用户名和密码创建了一个名为mysql-auth的secret对象

  1. [root@k8s-master01 configmap]# kubectl create secret generic mysql-auth --from-literal=username=root --from-literal=password=ikubernetes
  2. secret/mysql-auth created
  3. [root@k8s-master01 configmap]#

而后即可查看新建资源的属性信息,由下面的命令及其输出结果可以看出,以generic标识符创建的Secret对象是为Opaque类型,其键值数据会议Base64的编码格式进行保存和打印:

  1. [root@k8s-master01 configmap]# kubectl get secret mysql-auth -o yaml
  2. apiVersion: v1
  3. data:
  4. password: aWt1YmVybmV0ZXM=
  5. username: cm9vdA==
  6. kind: Secret
  7. metadata:
  8. creationTimestamp: "2020-07-17T07:01:15Z"
  9. name: mysql-auth
  10. namespace: default
  11. resourceVersion: "6407214"
  12. selfLink: /api/v1/namespaces/default/secrets/mysql-auth
  13. uid: fde4afca-11ea-44d3-a1eb-fa0fd38349a0
  14. type: Opaque
  15. [root@k8s-master01 configmap]#

不过,Kubernetes系统的Secret对象的Base64编码的数据并非加密格式,许多相关的工具都可以轻松完成解码。如下面命令所示

  1. [root@k8s-master01 configmap]# echo cm9vdA== | base64 --decode
  2. root
  3. [root@k8s-master01 configmap]#
  4. [root@k8s-master01 configmap]# echo aWt1YmVybmV0ZXM= | base64 --decode
  5. ikubernetes
  6. [root@k8s-master01 configmap]#

对于已经存储于文静中的数据,也可以在创建generic格式secret对象时使用”—from-file”选项从文件中直接进行加载,例如创建用于ssh认证的secret对象时,如果没有认证信息文件,则需要首先使用命令生成一对认证文件

  1. [root@k8s-master01 configmap]# ssh-keygen -t rsa
  2. Generating public/private rsa key pair.
  3. Enter file in which to save the key (/root/.ssh/id_rsa):
  4. Enter passphrase (empty for no passphrase):
  5. Enter same passphrase again:
  6. Your identification has been saved in /root/.ssh/id_rsa.
  7. Your public key has been saved in /root/.ssh/id_rsa.pub.
  8. The key fingerprint is:
  9. SHA256:5iRod5USoXDotUMW5LYdz8nfKGKj2q5y/fKNTTQ4q6A root@k8s-master01
  10. The key's randomart image is:
  11. +---[RSA 2048]----+
  12. | .o+.o. |
  13. | .++. . . |
  14. | . ++.o o |
  15. | .ooo O . |
  16. | o +.S B |
  17. | . . * + o o |
  18. | .. * o o . |
  19. | ...oo+ B . |
  20. | Eoo+=++ o |
  21. +----[SHA256]-----+
  22. [root@k8s-master01 configmap]#

而后使用kubectl create secret generic <SECRET_NAME> --from-file=[KEY1]=/PATH/TO/FILE命令加载文件内容并生成为Secret对象

  1. [root@k8s-master01 .ssh]# kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=${HOME}/.ssh/id_rsa --from-file=ssh-publickey=${HOME}/.ssh/id_rsa.pub
  2. secret/ssh-key-secret created
  3. [root@k8s-master01 .ssh]#

另外,如要基于私钥和数字证书文件创建用于SSL/TLS通信的Secret对象,则需要使用kubectl create secret tls <SECRET_NAME> --cert= --key=命令来进行创建,注意其类型标识符为TLS。例如,假设需要为Nginx测试创建SSL虚拟主机,用户首先使用了类型如下的命令生成私钥和自签证书

  1. [root@k8s-master01 .ssh]# (umask 077; openssl genrsa -out nginx.key 2048)
  2. Generating RSA private key, 2048 bit long modulus
  3. .............................................................................+++
  4. ...............................................................................................+++
  5. e is 65537 (0x10001)
  6. [root@k8s-master01 .ssh]#
  7. [root@k8s-master01 .ssh]#
  8. [root@k8s-master01 .ssh]# openssl req -new -x509 -key nginx.key -out nginx.crt -subj /C=CN/ST=Hubei/L=Wuhan/O=Devops/CN=www.ilinux.io
  9. [root@k8s-master01 .ssh]#

而后即可使用如下命令将这两个文件创建为Secret对象。需要注意的是,无论用户提供的证书和私钥文件使用的是什么名称,他们一律会被转换为分别以tls.key和tls.crt为其键名

  1. [root@k8s-master01 .ssh]# kubectl create secret tls nginx-ssl --key=./nginx.key --cert=./nginx.crt
  2. secret/nginx-ssl created
  3. [root@k8s-master01 .ssh]#

注意其类型应该为”kubernetes.io/tls”,例如下面命令结果中的显示:

  1. [root@k8s-master01 .ssh]# kubectl get secret nginx-ssl
  2. NAME TYPE DATA AGE
  3. nginx-ssl kubernetes.io/tls 2 47s
  4. [root@k8s-master01 .ssh]#

由上述操作过程可见,命令式创建Secret对象与ConfigMap对象的方式几乎没有明显区别

清单式创建

Secret资源是标准的K8S API对象,除了标准的apiVersion、kind和metadata字段,它可用的其他字段具体如下

  • .secret.data <map[string]string>: “Key:Value”格式的数据,通常是敏感信息,数据格式需要是Base64格式编码的字符串,因此需要用户事先完成编码。
  • .secret.stringData <map[string]string>: 以明文格式(非Base64编码)定义的”Key:Value”数据;无需用户事先对数据进行base64编码,而是创建为Secret对象时自动进行编码并保存于data字段中;stringData字段中的明文不会被API Server输出,不过如果是使用kubectl apply命令进行的创建,那么注解信息中还是可能会直接输出这些信息的
  • .secret.type <string>: 仅是为了便于编程方式处理Secret数据而提供的类型标识

下面是保存于配置文件secret-demo.yaml中的Secret资源定义示例,其使用stringData提供了明文格式的键值数据,从而免去了事先进行手动编码的麻烦

  1. apiVersion: v1
  2. kind: Secret
  3. metadata:
  4. name: secret-demo
  5. stringData:
  6. username: redis
  7. password: redis@password
  8. type: Opaque

Secret对象也是K8S系统的“一等公民”,因此,使用标准资源创建命令即可完成创建。相比较来说,基于清单文件将保存于文件中的敏感信息创建Secret对象时,用户首先需要将敏感信息读出,转为base64编码格式,而后再将其创建为清单文件,过程繁琐,反不如命令式创建来得便捷。不过,如果存在多次创建或重构之需,那么将其保存为配置清单也是情势所需

Secret存储卷

类似于Pod消费ConfigMap对象的方式,Secret对象可用注入为环境变量,也可以存储为卷形式挂载使用。不过,容器应用通常会在发生错误时将所有环境变量保存于日志信息中,甚至有些应用在启动时就会运行环境打印到日志中;另外,容器应用调用第三方程序为子进程时,这些子进程能够继承并使用父进程的所有环境变量。因此,使用环境变量引用Secret对象中的敏感信息实在算不上明智之举

在Pod中使用Secret存储卷的方式,除了其类型及引用标识要替换为Secret及SecretName之外,几乎完全类似于ConfigMap存储卷,包括支持使用挂载整个存储,只挂载存储卷中的指定键值以及独立挂载存储卷中的键等使用方式

下面是定义在配置文件secret-volume-demo.yaml中的secret资源使用示例,它将nginx-ssl关联为Pod资源的名为nginxcert的Sercret存储,而后由容器web-server挂载至/etc/nginx/ssl目录下

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: secret-volume-demo
  5. spec:
  6. replicas: 1
  7. selector:
  8. matchLabels:
  9. web: web-server
  10. template:
  11. metadata:
  12. labels:
  13. web: web-server
  14. spec:
  15. containers:
  16. - name: web-server
  17. image: docker.io/nginx:alpine
  18. volumeMounts:
  19. - name: ngxconfig
  20. mountPath: /etc/nginx/conf.d/myserver.conf
  21. subPath: myserver.conf
  22. readOnly: true
  23. - name: ngxconfig
  24. mountPath: /etc/nginx/conf.d/myserver-status.cfg
  25. subPath: myserver-status.cfg
  26. readOnly: true
  27. - name: nginxcert
  28. mountPath: /etc/nginx/ssl/
  29. readOnly: true
  30. volumes:
  31. - name: ngxconfig
  32. configMap:
  33. name: nginx-web-files
  34. - name: nginxcert
  35. secret:
  36. secretName: nginx-ssl

将上面资源清单文件中定义的资源创建于K8S系统之上,而后再查看容器挂载点目录中的文件,以确认其挂载是否成功。下面的命令结果显示,私钥文件tls.key和证书文件tls.crt已经成功挂载于挂载点路径之下

  1. [root@k8s-master01 configmap]# kubectl apply -f secret-volume-demo.yaml
  2. deployment.apps/secret-volume-demo created
  3. [root@k8s-master01 configmap]#
  4. [root@k8s-master01 configmap]# kubectl get pods -l "web=web-server"
  5. NAME READY STATUS RESTARTS AGE
  6. secret-volume-demo-68b559ff98-gq66b 1/1 Running 0 85s
  7. [root@k8s-master01 configmap]#
  8. [root@k8s-master01 configmap]# kubectl exec secret-volume-demo-68b559ff98-gq66b ls /etc/nginx/ssl
  9. tls.crt
  10. tls.key
  11. [root@k8s-master01 configmap]#

此时,通过ConfigMap对象为容器应用Nginx提供HTTPS虚拟主机配置,它只要使用由Secret对象生成的私钥和证书文件,即可定义出容器化运行的nginx服务

imagePullSecret资源对象

imagePullSecret资源可用辅助kubelet从需要认证的私有镜像仓库获取镜像,它通过将secret提供的密码传递给kubelet从而在拉取镜像前完成必要的认证过程。

使用imagePullSecret的方式有两种:一是创建docker-registry类型的secret对象,并在定义Pod资源时明确通过”imagePullSecrets”字段给出;另一个是创建docker-registry类型的secret对象,将其添加到某特定的SeviceAccount对象中,那些使用该ServiceAccount资源创建的Pod对象,以及默认使用该ServiceAccount的Pod对象都会直接使用imagePullSecrets中的认证信息。由于还没有写关于ServiceAccount的文章,所以这里先介绍第一种

创建Docker-registry类型的secret对象时,需要使用kubectl create secret docker-registry <SECRET_NAME> --docker-user=<USERNAME> --docker-password=<PASSWORD> --docker-emial=<DOCKER_USER_EMAIL>的名令格式,其中的用户名、密码及邮件信息是在使用docker login命令登陆时使用的认证信息。例如,下面的命令创建了名为local-registry的image pull secret对象

  1. [root@k8s-master01 configmap]# kubectl create secret docker-registry local-regisrtry --docker-username=Ops --docker-password=Opspass --docker-email=ops@ilinux.io
  2. secret/local-regisrtry created
  3. [root@k8s-master01 configmap]#

此类Secret对象打印的类型信息为”kubernetes.io/dockerconfigjson”,如下面的命令结果所示

  1. [root@k8s-master01 configmap]# kubectl get secret local-regisrtry
  2. NAME TYPE DATA AGE
  3. local-regisrtry kubernetes.io/dockerconfigjson 1 96s
  4. [root@k8s-master01 configmap]#

而后,使用相应的私有registry中镜像的Pod资源的定义,即可通过imagePullSecrets字段使用此Secret。如下示例

  1. spec:
  2. imagePullSecrets:
  3. - name: local-regisrtry
  4. containers:
  5. .....
  6. ....

小结

配置容器应用的核心目标在于说明配置容器应用的常用方式,这在将同一Pod资源分别以不通的配置运行于不同的环境中时特别有用。主要描述了如下几种配置容器化应用的方式

  • 自定义命令行选项,为容器化应用传递特定参数
  • 通过环境变量向容器注入自定义参数
  • 基础configmap对象,以环境变量或存储卷的形式向容器提供配置信息
  • 借助secret对象,以存储卷的形式向容器应用提供敏感信息