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资源主要由四种类型组成,具体如下:
- 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对象
[root@k8s-master01 configmap]# kubectl create secret generic mysql-auth --from-literal=username=root --from-literal=password=ikubernetes
secret/mysql-auth created
[root@k8s-master01 configmap]#
而后即可查看新建资源的属性信息,由下面的命令及其输出结果可以看出,以generic标识符创建的Secret对象是为Opaque类型,其键值数据会议Base64的编码格式进行保存和打印:
[root@k8s-master01 configmap]# kubectl get secret mysql-auth -o yaml
apiVersion: v1
data:
password: aWt1YmVybmV0ZXM=
username: cm9vdA==
kind: Secret
metadata:
creationTimestamp: "2020-07-17T07:01:15Z"
name: mysql-auth
namespace: default
resourceVersion: "6407214"
selfLink: /api/v1/namespaces/default/secrets/mysql-auth
uid: fde4afca-11ea-44d3-a1eb-fa0fd38349a0
type: Opaque
[root@k8s-master01 configmap]#
不过,Kubernetes系统的Secret对象的Base64编码的数据并非加密格式,许多相关的工具都可以轻松完成解码。如下面命令所示
[root@k8s-master01 configmap]# echo cm9vdA== | base64 --decode
root
[root@k8s-master01 configmap]#
[root@k8s-master01 configmap]# echo aWt1YmVybmV0ZXM= | base64 --decode
ikubernetes
[root@k8s-master01 configmap]#
对于已经存储于文静中的数据,也可以在创建generic格式secret对象时使用”—from-file”选项从文件中直接进行加载,例如创建用于ssh认证的secret对象时,如果没有认证信息文件,则需要首先使用命令生成一对认证文件
[root@k8s-master01 configmap]# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:5iRod5USoXDotUMW5LYdz8nfKGKj2q5y/fKNTTQ4q6A root@k8s-master01
The key's randomart image is:
+---[RSA 2048]----+
| .o+.o. |
| .++. . . |
| . ++.o o |
| .ooo O . |
| o +.S B |
| . . * + o o |
| .. * o o . |
| ...oo+ B . |
| Eoo+=++ o |
+----[SHA256]-----+
[root@k8s-master01 configmap]#
而后使用
kubectl create secret generic <SECRET_NAME> --from-file=[KEY1]=/PATH/TO/FILE
命令加载文件内容并生成为Secret对象
[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
secret/ssh-key-secret created
[root@k8s-master01 .ssh]#
另外,如要基于私钥和数字证书文件创建用于SSL/TLS通信的Secret对象,则需要使用
kubectl create secret tls <SECRET_NAME> --cert= --key=
命令来进行创建,注意其类型标识符为TLS。例如,假设需要为Nginx测试创建SSL虚拟主机,用户首先使用了类型如下的命令生成私钥和自签证书
[root@k8s-master01 .ssh]# (umask 077; openssl genrsa -out nginx.key 2048)
Generating RSA private key, 2048 bit long modulus
.............................................................................+++
...............................................................................................+++
e is 65537 (0x10001)
[root@k8s-master01 .ssh]#
[root@k8s-master01 .ssh]#
[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
[root@k8s-master01 .ssh]#
而后即可使用如下命令将这两个文件创建为Secret对象。需要注意的是,无论用户提供的证书和私钥文件使用的是什么名称,他们一律会被转换为分别以tls.key和tls.crt为其键名
[root@k8s-master01 .ssh]# kubectl create secret tls nginx-ssl --key=./nginx.key --cert=./nginx.crt
secret/nginx-ssl created
[root@k8s-master01 .ssh]#
注意其类型应该为”kubernetes.io/tls”,例如下面命令结果中的显示:
[root@k8s-master01 .ssh]# kubectl get secret nginx-ssl
NAME TYPE DATA AGE
nginx-ssl kubernetes.io/tls 2 47s
[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提供了明文格式的键值数据,从而免去了事先进行手动编码的麻烦
apiVersion: v1
kind: Secret
metadata:
name: secret-demo
stringData:
username: redis
password: redis@password
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目录下
apiVersion: apps/v1
kind: Deployment
metadata:
name: secret-volume-demo
spec:
replicas: 1
selector:
matchLabels:
web: web-server
template:
metadata:
labels:
web: web-server
spec:
containers:
- name: web-server
image: docker.io/nginx:alpine
volumeMounts:
- name: ngxconfig
mountPath: /etc/nginx/conf.d/myserver.conf
subPath: myserver.conf
readOnly: true
- name: ngxconfig
mountPath: /etc/nginx/conf.d/myserver-status.cfg
subPath: myserver-status.cfg
readOnly: true
- name: nginxcert
mountPath: /etc/nginx/ssl/
readOnly: true
volumes:
- name: ngxconfig
configMap:
name: nginx-web-files
- name: nginxcert
secret:
secretName: nginx-ssl
将上面资源清单文件中定义的资源创建于K8S系统之上,而后再查看容器挂载点目录中的文件,以确认其挂载是否成功。下面的命令结果显示,私钥文件tls.key和证书文件tls.crt已经成功挂载于挂载点路径之下
[root@k8s-master01 configmap]# kubectl apply -f secret-volume-demo.yaml
deployment.apps/secret-volume-demo created
[root@k8s-master01 configmap]#
[root@k8s-master01 configmap]# kubectl get pods -l "web=web-server"
NAME READY STATUS RESTARTS AGE
secret-volume-demo-68b559ff98-gq66b 1/1 Running 0 85s
[root@k8s-master01 configmap]#
[root@k8s-master01 configmap]# kubectl exec secret-volume-demo-68b559ff98-gq66b ls /etc/nginx/ssl
tls.crt
tls.key
[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对象
[root@k8s-master01 configmap]# kubectl create secret docker-registry local-regisrtry --docker-username=Ops --docker-password=Opspass --docker-email=ops@ilinux.io
secret/local-regisrtry created
[root@k8s-master01 configmap]#
此类Secret对象打印的类型信息为”kubernetes.io/dockerconfigjson”,如下面的命令结果所示
[root@k8s-master01 configmap]# kubectl get secret local-regisrtry
NAME TYPE DATA AGE
local-regisrtry kubernetes.io/dockerconfigjson 1 96s
[root@k8s-master01 configmap]#
而后,使用相应的私有registry中镜像的Pod资源的定义,即可通过imagePullSecrets字段使用此Secret。如下示例
spec:
imagePullSecrets:
- name: local-regisrtry
containers:
.....
....
小结
配置容器应用的核心目标在于说明配置容器应用的常用方式,这在将同一Pod资源分别以不通的配置运行于不同的环境中时特别有用。主要描述了如下几种配置容器化应用的方式
- 自定义命令行选项,为容器化应用传递特定参数
- 通过环境变量向容器注入自定义参数
- 基础configmap对象,以环境变量或存储卷的形式向容器提供配置信息
- 借助secret对象,以存储卷的形式向容器应用提供敏感信息