使用密钥安全地分发凭据
此页面展示了如何安全地将敏感数据(例如密码和加密密钥)注入到 Pod 中。
开始之前
你需要有一个 Kubernetes 集群,并且必须配置 kubectl 命令行工具以与你的集群通信。建议在至少有两个不充当控制平面主机的节点的集群上运行本教程。如果你还没有集群,可以使用 minikube 创建一个,或者你可以使用以下 Kubernetes 游乐场之一
将你的机密数据转换为 base-64 表示形式
假设你想拥有两段机密数据:用户名 my-app
和密码 39528$vdg7Jb
。首先,使用 base64 编码工具将你的用户名和密码转换为 base64 表示形式。以下是使用常用的 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'
这更方便。前面显示的详细方法会显式地运行每个步骤,以演示正在发生的事情。
创建一个可以通过 Volume 访问机密数据的 Pod
以下是一个配置文件,你可以使用它来创建一个 Pod
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: nginx
volumeMounts:
# name must match the volume name below
- name: secret-volume
mountPath: /etc/secret-volume
readOnly: true
# The secret data is exposed to Containers in the Pod through a Volume.
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
获取正在你的 Pod 中运行的 Container 的 shell
kubectl exec -i -t secret-test-pod -- /bin/bash
机密数据通过挂载在
/etc/secret-volume
下的 Volume 暴露给 Container。在你的 shell 中,列出
/etc/secret-volume
目录中的文件# Run this in the shell inside the container ls /etc/secret-volume
输出显示两个文件,每个文件对应一段机密数据
password username
在你的 shell 中,显示
username
和password
文件的内容# Run this in the shell inside the container echo "$( cat /etc/secret-volume/username )" echo "$( cat /etc/secret-volume/password )"
输出是你的用户名和密码
my-app 39528$vdg7Jb
修改你的镜像或命令行,以便程序在 mountPath
目录中查找文件。Secret data
映射中的每个键都成为此目录中的文件名。
将 Secret 键投影到特定的文件路径
你还可以控制 Volume 中 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 中。否则,不会创建 Volume。
为 Secret 键设置 POSIX 权限
你可以为单个 Secret 键设置 POSIX 文件访问权限位。如果你不指定任何权限,则默认使用 0644
。你还可以为整个 Secret Volume 设置默认的 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 Volume 挂载创建的所有文件都具有权限 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'
将 Secret 中定义的
backend-username
值分配给 Pod 规范中的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 v1.6 及更高版本中可用。创建一个包含多个键值对的 Secret
kubectl create secret generic test-secret --from-literal=username='my-app' --from-literal=password='39528$vdg7Jb'
使用 envFrom 将 Secret 的所有数据定义为容器环境变量。Secret 中的键将成为 Pod 中的环境变量名称。
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 提供 prod/test 凭据
本示例演示了一个 Pod 如何使用包含生产环境凭据的 Secret,以及另一个 Pod 如何使用包含测试环境凭据的 Secret。
为生产环境凭据创建一个 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-user
使用prod-db-secret
test-user
使用test-db-secret
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