Information in this document may be out of date
This document has an older update date than the original, so the information it contains may be out of date. If you're able to read English, see the English version for the most up-to-date information: Distribute Credentials Securely Using Secrets
使用 Secret 安全地分发凭据
本文展示如何安全地将敏感数据(如密码和加密密钥)注入到 Pod 中。
准备开始
你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
将 Secret 数据转换为 base-64 形式
假设用户想要有两条 Secret 数据:用户名 my-app
和密码 39528$vdg7Jb
。
首先使用 Base64 编码将用户名和密码转化为 base-64 形式。
下面是一个使用常用的 base64 程序的示例:
echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64
结果显示 base-64 形式的用户名为 bXktYXBw
,
base-64 形式的密码为 Mzk1MjgkdmRnN0pi
。
使用你的操作系统所能信任的本地工具以降低使用外部工具的风险。
创建 Secret
这里是一个配置文件,可以用来创建存有用户名和密码的 Secret:
apiVersion: v1
kind: Secret
metadata:
name: test-secret
data:
username: bXktYXBw
password: Mzk1MjgkdmRnN0pi
创建 Secret:
kubectl apply -f https://k8s.io/examples/pods/inject/secret.yaml
查看 Secret 相关信息:
kubectl get secret test-secret
输出:
NAME TYPE DATA AGE test-secret Opaque 2 1m
查看 Secret 相关的更多详细信息:
kubectl describe secret test-secret
输出:
Name: test-secret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 13 bytes username: 7 bytes
直接用 kubectl 创建 Secret
如果你希望略过 Base64 编码的步骤,你也可以使用 kubectl create secret
命令直接创建 Secret。例如:
kubectl create secret generic test-secret --from-literal='username=my-app' --from-literal='password=39528$vdg7Jb'
这是一种更为方便的方法。 前面展示的详细分解步骤有助于了解究竟发生了什么事情。
创建一个可以通过卷访问 Secret 数据的 Pod
这里是一个可以用来创建 Pod 的配置文件:
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: nginx
volumeMounts:
# name 必须与下面的卷名匹配
- name: secret-volume
mountPath: /etc/secret-volume
readOnly: true
# Secret 数据通过一个卷暴露给该 Pod 中的容器
volumes:
- name: secret-volume
secret:
secretName: test-secret
创建 Pod:
kubectl apply -f https://k8s.io/examples/pods/inject/secret-pod.yaml
确认 Pod 正在运行:
kubectl get pod secret-test-pod
输出:
NAME READY STATUS RESTARTS AGE secret-test-pod 1/1 Running 0 42m
获取一个 Shell 进入 Pod 中运行的容器:
kubectl exec -i -t secret-test-pod -- /bin/bash
Secret 数据通过挂载在
/etc/secret-volume
目录下的卷暴露在容器中。在 Shell 中,列举
/etc/secret-volume
目录下的文件:# 在容器中 Shell 运行下面命令 ls /etc/secret-volume
输出包含两个文件,每个对应一个 Secret 数据条目:
password username
在 Shell 中,显示
username
和password
文件的内容:# 在容器中 Shell 运行下面命令 echo "$( cat /etc/secret-volume/username )" echo "$( cat /etc/secret-volume/password )"
输出为用户名和密码:
my-app 39528$vdg7Jb
修改你的镜像或命令行,使程序在 mountPath
目录下查找文件。
Secret data
映射中的每个键都成为该目录中的文件名。
映射 Secret 键到特定文件路径
你还可以控制卷内 Secret 键的映射路径。
使用 .spec.volumes[].secret.items
字段来改变每个键的目标路径。
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
当你部署此 Pod 时,会发生以下情况:
- 来自
mysecret
的键username
可以在路径/etc/foo/my-group/my-username
下供容器使用,而不是路径/etc/foo/username
。 - 来自该 Secret 的键
password
没有映射到任何路径。
如果你使用 .spec.volumes[].secret.items
明确地列出键,请考虑以下事项:
- 只有在
items
字段中指定的键才会被映射。 - 要使用 Secret 中全部的键,那么全部的键都必须列在
items
字段中。 - 所有列出的键必须存在于相应的 Secret 中。否则,该卷不被创建。
为 Secret 键设置 POSIX 权限
你可以为单个 Secret 键设置 POSIX 文件访问权限位。
如果不指定任何权限,默认情况下使用 0644
。
你也可以为整个 Secret 卷设置默认的 POSIX 文件模式,需要时你可以重写单个键的权限。
例如,可以像这样指定默认模式:
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: 0400
Secret 被挂载在 /etc/foo
目录下;所有由 Secret 卷挂载创建的文件的访问许可都是 0400
。
如果使用 JSON 定义 Pod 或 Pod 模板,请注意 JSON 规范不支持数字的八进制形式,
因为 JSON 将 0400
视为十进制的值 400
。
在 JSON 中,要改为使用十进制的 defaultMode
。
如果你正在编写 YAML,则可以用八进制编写 defaultMode
。
使用 Secret 数据定义容器变量
在你的容器中,你可以以环境变量的方式使用 Secret 中的数据。
如果容器已经使用了在环境变量中的 Secret,除非容器重新启动,否则容器将无法感知到 Secret 的更新。 有第三方解决方案可以在 Secret 改变时触发容器重启。
使用来自 Secret 中的数据定义容器变量
定义环境变量为 Secret 中的键值偶对:
kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
在 Pod 规约中,将 Secret 中定义的值
backend-username
赋给SECRET_USERNAME
环境变量。apiVersion: v1 kind: Pod metadata: name: env-single-secret spec: containers: - name: envars-test-container image: nginx env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: backend-user key: backend-username
创建 Pod:
kubectl create -f https://k8s.io/examples/pods/inject/pod-single-secret-env-variable.yaml
在 Shell 中,显示容器环境变量
SECRET_USERNAME
的内容:kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
输出类似于:
backend-admin
使用来自多个 Secret 的数据定义环境变量
和前面的例子一样,先创建 Secret:
kubectl create secret generic backend-user --from-literal=backend-username='backend-admin' kubectl create secret generic db-user --from-literal=db-username='db-admin'
在 Pod 规约中定义环境变量:
apiVersion: v1 kind: Pod metadata: name: envvars-multiple-secrets spec: containers: - name: envars-test-container image: nginx env: - name: BACKEND_USERNAME valueFrom: secretKeyRef: name: backend-user key: backend-username - name: DB_USERNAME valueFrom: secretKeyRef: name: db-user key: db-username
创建 Pod:
kubectl create -f https://k8s.io/examples/pods/inject/pod-multiple-secret-env-variable.yaml
在你的 Shell 中,显示容器环境变量的内容:
kubectl exec -i -t envvars-multiple-secrets -- /bin/sh -c 'env | grep _USERNAME'
输出类似于:
DB_USERNAME=db-admin BACKEND_USERNAME=backend-admin
将 Secret 中的所有键值偶对定义为环境变量
此功能在 Kubernetes 1.6 版本之后可用。
创建包含多个键值偶对的 Secret:
kubectl create secret generic test-secret --from-literal=username='my-app' --from-literal=password='39528$vdg7Jb'
使用
envFrom
来将 Secret 中的所有数据定义为环境变量。 Secret 中的键名成为容器中的环境变量名:apiVersion: v1 kind: Pod metadata: name: envfrom-secret spec: containers: - name: envars-test-container image: nginx envFrom: - secretRef: name: test-secret
创建 Pod:
kubectl create -f https://k8s.io/examples/pods/inject/pod-secret-envFrom.yaml
在 Shell 中,显示环境变量
username
和password
的内容:kubectl exec -i -t envfrom-secret -- /bin/sh -c 'echo "username: $username\npassword: $password\n"'
输出类似于:
username: my-app password: 39528$vdg7Jb
示例:使用 Secret 为 Pod 提供生产环境或测试环境的凭据
此示例展示的是一个使用了包含生产环境凭据的 Secret 的 Pod 和一个使用了包含测试环境凭据的 Secret 的 Pod。
创建用于生产环境凭据的 Secret:
kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
输出类似于:
secret "prod-db-secret" created
为测试环境凭据创建 Secret。
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
输出类似于:
secret "test-db-secret" created
说明:$
、\
、*
、=
和!
这类特殊字符会被你的 Shell 解释,需要进行转义。在大多数 Shell 中,最简单的密码转义方法是使用单引号(
'
)将密码包起来。 例如,如果你的实际密码是S!B\*d$zDsb=
,则应执行以下命令:kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password='S!B\*d$zDsb='
你无需转义来自文件(
--from-file
)的密码中的特殊字符。
创建 Pod 清单:
cat <<EOF > pod.yaml 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" EOF
说明:这两个 Pod 的规约只在一个字段上有所不同;这样便于从一个通用的 Pod 模板创建具有不同权能的 Pod。
通过运行以下命令将所有这些对象应用到 API 服务器:
kubectl create -f pod.yaml
两个容器的文件系统中都将存在以下文件,其中包含每个容器环境的值:
/etc/secret-volume/username
/etc/secret-volume/password
你可以通过使用两个服务账号进一步简化基础 Pod 规约:
- 带有
prod-db-secret
的prod-user
- 带有
test-db-secret
的test-user
Pod 规约精简为:
apiVersion: v1
kind: Pod
metadata:
name: prod-db-client-pod
labels:
name: prod-db-client
spec:
serviceAccount: prod-db-client
containers:
- name: db-client-container
image: myClientImage