目录
什么是Secret
Secret资源的功能类似于ConfigMap,但它专用于存放敏感数据,如密码、数字证书、私钥、令牌和ssh key等。
敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活。
用户可以创建自己的secret,系统也会有自己的secret。
Secret对象存储数据以键值方式存储数据,再pod资源中通过环境变量或存储卷进行数据访问。
Secret对象仅会被分发至调用了此对象的pod资源所在的工作节点,且只能由节点将其存储于内存中。
另外,Secret对象的数据的存储及打印格式为base64编码的字符串,因此用户在创建对象时也要提供此种编码格式的数据。
不过,在容器中以环境变量或存储卷的方式访问时,他们会被自动解码为明文格式。
在master节点上,Secret对象以非加密的格式存储于etcd中,因此管理员必须加以精心管控以确保敏感数据的机密性,必须确保etcd集群节点间以及与APIserver的安全通信,etcd服务的访问授权,还包括用户访问APIServer时的授权,因为拥有创建pod资源的用户都可以使用Secret资源并能够通过pod中的容器访问其数据。
Secret对象主要有两种用途:
一是作为存储卷注入到pod上,由容器应用程序所使用,
二是用于kubelet为pod里的容器拉取镜像时向私有仓库提供认证信息。
Secret资源主要有四种类型组成:
1、Opaque:自定义数据内容;base64编码,用来存储密码、密钥、信息、证书等数据,类型标识符为generic。
2、kubernetes.io/service-account-token:ServiceAccount的认证信息,可在创建service account时由kubernetes自动创建。
3、kubernetes.io/dockerconfigjson:用来存储Docker镜像仓库的认证信息,类型标识符为docker-registry。
4、kubernetes.io/tls:用于为ssl通信模式存储证书和私钥文件,命令式创建时类型标识符为tls。
Pod需要先引用才能使用某个secret,Pod 可以用两种方式使用 secret:
1、作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里。
2、当 kubelet 为 pod 拉取镜像时使用
secret的创建
內建的Secrets
由ServiceAccount创建的API证书附加的秘钥。
k8s自动生成的用来访问apiserver的Secret,所有Pod会默认使用这个Secret与apiserver通信。
对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount
目录中。
1 2 3 4 5 6 7 8 9 10 |
$ kubectl run nginx --image=nginx $ kubectl get pod NAME READY STATUS RESTARTS AGE nginx-6db489d4b7-886wq 1/1 Running 0 4s $ kubectl exec nginx-6db489d4b7-886wq ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token |
每个namespace下有一个名为default的默认的ServiceAccount对象
1 2 3 |
$ kubectl get Secret NAME TYPE DATA AGE default-token-mmkdj kubernetes.io/service-account-token 3 8d |
ServiceAccount里有一个名为Tokens的可以作为Volume一样被Mount到Pod里的Secret,当Pod启动时这个Secret会被自动Mount到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权过程。
1 |
kubectl get pod -o yaml |
创建自己的Secret
通过命令创建
使用kubectl create secret命令创建Secret
假如mougePod要访问数据库,需要用户名密码,分别存放在2个文件中:username.txt,password.txt
1 2 |
$ echo -n 'admin' > ./username.txt $ echo -n '1f2d1e2e67df' > ./password.txt |
kubectl create secret
指令将用户名密码写到secret中,并在apiserver创建Secret
1 2 |
$ kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt secret "db-user-pass" created |
查看创建结果:
1 2 3 |
$ kubectl get secrets NAME TYPE DATA AGE db-user-pass Opaque 2 51s |
1 2 3 4 5 6 7 8 9 10 11 12 |
$ kubectl describe secrets/db-user-pass Name: db-user-pass Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password.txt: 12 bytes username.txt: 5 bytes |
get或describe指令都不会展示secret的实际内容,这是出于对数据的保护的考虑,如果想查看实际内容请继续往下看。
通过yaml创建Secret
注意:
1、只能使用key-value的形式创建secret,不支持嵌套。
2、如果使用data
标签,那么value必须是经过base64编码的值。如果value必须是明文,那么把data
标签替换成stringData
即可。
创建一个secret.yaml文件,内容用base64编码
1 2 3 4 5 6 |
$ echo -n 'admin' | base64 YWRtaW4= $ echo -n '1f2d1e2e67df' | base64 MWYyZDFlMmU2N2Rm |
yaml文件内容:
1 2 3 4 5 6 7 8 |
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm |
如果使用明文记录,把data
替换成stringData
:
1 2 3 4 5 6 7 8 |
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque stringData: username: admin password: 1f2d1e2e67df |
创建:
1 2 |
$ kubectl create -f ./secret.yaml secret "mysecret" created |
解析Secret中内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ kubectl get secret mysecret -o yaml apiVersion: v1 data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm kind: Secret metadata: creationTimestamp: 2016-01-22T18:41:56Z name: mysecret namespace: default resourceVersion: "164619" selfLink: /api/v1/namespaces/default/secrets/mysecret uid: cfee02d6-c137-11e5-8d73-42010af00002 type: Opaque |
base64解码:
1 2 |
$ echo 'MWYyZDFlMmU2N2Rm' | base64 --decode 1f2d1e2e67df |
使用Secret
secret可以作为数据卷挂载或者作为环境变量暴露给Pod中的容器使用,也可以被系统中的其他资源使用。比如可以用secret导入与外部系统交互需要的证书文件等。
在Pod中以文件的形式使用secret
- 创建一个Secret,多个Pod可以引用同一个Secret
- 修改Pod的定义,在
spec.volumes[]
加一个volume,给这个volume起个名字,spec.volumes[].secret.secretName
记录的是要引用的Secret名字 - 在每个需要使用Secret的容器中添加一项
spec.containers[].volumeMounts[]
,指定spec.containers[].volumeMounts[].readOnly = true
,spec.containers[].volumeMounts[].mountPath
要指向一个未被使用的系统路径。 - 修改镜像或者命令行使系统可以找到上一步指定的路径。此时Secret中
data
字段的每一个key都是指定路径下面的一个文件名
下面是一个Pod中引用Secret的列子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret |
每一个被引用的Secret都要在spec.volumes
中定义
如果Pod中的多个容器都要引用这个Secret那么每一个容器定义中都要指定自己的volumeMounts
,但是Pod定义中声明一次spec.volumes
就好了。
映射secret key到指定的路径
可以控制secret key被映射到容器内的路径,利用spec.volumes[].secret.items
来修改被映射的具体路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret items: - key: username path: my-group/my-username |
发生了什么呢?
- username被映射到了文件
/etc/foo/my-group/my-username
而不是/etc/foo/username
- password没有变
Secret文件权限
可以指定secret文件的权限,类似linux系统文件权限,如果不指定默认权限是0644
,等同于linux文件的-rw-r--r--
权限
设置默认权限位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" volumes: - name: foo secret: secretName: mysecret defaultMode: 256 |
上述文件表示将secret挂载到容器的/etc/foo
路径,每一个key衍生出的文件,权限位都将是0400
由于JSON不支持八进制数字,因此用十进制数256表示0400,如果用yaml格式的文件那么就很自然的使用八进制了
同理可以单独指定某个key的权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" volumes: - name: foo secret: secretName: mysecret items: - key: username path: my-group/my-username mode: 511 |
从volume中读取secret的值
值得注意的一点是,以文件的形式挂载到容器中的secret,他们的值已经是经过base64解码的了,可以直接读出来使用。
1 2 3 4 5 6 7 8 9 |
$ ls /etc/foo/ username password $ cat /etc/foo/username admin $ cat /etc/foo/password 1f2d1e2e67df |
被挂载的secret内容自动更新
也就是如果修改一个Secret的内容,那么挂载了该Secret的容器中也将会取到更新后的值,但是这个时间间隔是由kubelet的同步时间决定的。最长的时间将是一个同步周期加上缓存生命周期(period+ttl)
特例:以subPath形式挂载到容器中的secret将不会自动更新
以环境变量的形式使用Secret
- 创建一个Secret,多个Pod可以引用同一个Secret
- 修改pod的定义,定义环境变量并使用
env[].valueFrom.secretKeyRef
指定secret和相应的key - 修改镜像或命令行,让它们可以读到环境变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
apiVersion: v1 kind: Pod metadata: name: secret-env-pod spec: containers: - name: mycontainer image: redis env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: mysecret key: username - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password restartPolicy: Never |
容器中读取环境变量,已经是base64解码后的值了:
1 2 3 4 5 |
$ echo $SECRET_USERNAME admin $ echo $SECRET_PASSWORD 1f2d1e2e67df |
使用imagePullSecrets
创建一个专门用来访问镜像仓库的secret,当创建Pod的时候由kubelet访问镜像仓库并拉取镜像,具体描述文档在 这里
设置自动导入的imagePullSecrets
可以手动创建一个,然后在serviceAccount中引用它。所有经过这个serviceAccount创建的Pod都会默认使用关联的imagePullSecrets来拉取镜像,参考文档
自动挂载手动创建的Secret
限制
需要被挂载到Pod中的secret需要提前创建,否则会导致Pod创建失败。
secret是有命名空间属性的,只有在相同namespace的Pod才能引用它。
单个Secret容量限制的1Mb,这么做是为了防止创建超大的Secret导致apiserver或kubelet的内存耗尽。但是创建过多的小容量secret同样也会耗尽内存,这个问题在将来可能会有方案解决。
kubelet只支持由API server创建出来的Pod中引用secret,使用特殊方式创建出来的Pod是不支持引用secret的,比如通过kubelet的–manifest-url参数创建的pod,或者–config参数创建的,或者REST API创建的。
通过secretKeyRef引用一个不存在你secret key会导致pod创建失败。
用例
Pod中的ssh keys
创建一个包含ssh keys的secret
1 |
kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub |
创建一个Pod,其中的容器可以用volume的形式使用ssh keys
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
kind: Pod apiVersion: v1 metadata: name: secret-test-pod labels: name: secret-test spec: volumes: - name: secret-volume secret: secretName: ssh-key-secret containers: - name: ssh-test-container image: mySshImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" |
Pod中区分生产和测试证书
创建2种不同的证书,分别用在生产和测试环境
1 2 3 4 5 |
$ kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11 secret "prod-db-secret" created $ kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests secret "test-db-secret" created |
再创建2个不同的Pod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
apiVersion: v1 kind: List items: - kind: Pod apiVersion: v1 metadata: name: prod-db-client-pod labels: name: prod-db-client spec: volumes: - name: secret-volume secret: secretName: prod-db-secret containers: - name: db-client-container image: myClientImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" - kind: Pod apiVersion: v1 metadata: name: test-db-client-pod labels: name: test-db-client spec: volumes: - name: secret-volume secret: secretName: test-db-secret containers: - name: db-client-container image: myClientImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" |
两个容器中都会有下列的文件
1 2 |
/etc/secret-volume/username /etc/secret-volume/password |
以“.”开头的key可以产生隐藏文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
kind: Secret apiVersion: v1 metadata: name: dotfile-secret data: .secret-file: dmFsdWUtMg0KDQo= --- kind: Pod apiVersion: v1 metadata: name: secret-dotfiles-pod spec: volumes: - name: secret-volume secret: secretName: dotfile-secret containers: - name: dotfile-test-container image: k8s.gcr.io/busybox command: - ls - "-l" - "/etc/secret-volume" volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" |
会在挂载目录下产生一个隐藏文件,/etc/secret-volume/.secret-file
参考
k8s中secret解析
kubernetes(k8s) 学习 (九) k8s存储之 Secret ( 简介+两种使用方式)