使用 Secret 安全分发凭证
本页面演示如何安全地将敏感数据(例如密码和加密密钥)注入 Pod 中。
准备工作
你需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。 建议在至少有两个节点(不作为控制平面主机)的集群上运行本教程。 如果你还没有集群,可以使用 minikube 创建一个,或者使用这些 Kubernetes 游乐场之一
将你的 Secret 数据转换为 Base64 格式
假设你有两段 Secret 数据:用户名 my-app
和密码 39528$vdg7Jb
。 首先,使用 Base64 编码工具将你的用户名和密码转换为 Base64 格式。 以下是使用常用 Base64 程序的示例
echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64
输出显示你的用户名的 Base64 编码为 bXktYXBw
,密码的 Base64 编码为 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'
这更方便。前面展示的详细方法明确地展示了每个步骤的发生过程。
创建一个 Pod,通过卷访问 Secret 数据
以下是你可用于创建 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 中运行的容器的 Shell
kubectl exec -i -t secret-test-pod -- /bin/bash
Secret 数据通过挂载在
/etc/secret-volume
下的卷暴露给容器。在你的 Shell 中,列出
/etc/secret-volume
目录中的文件# Run this in the shell inside the container ls /etc/secret-volume
输出显示两个文件,每个 Secret 数据对应一个
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 键映射到特定的文件路径
你还可以控制卷内 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'
将 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
使用多个 Secrets 中的数据定义容器环境变量
与前面的例子一样,首先创建 Secrets。
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
示例:使用 Secrets 为 Pod 提供生产/测试凭据
此示例演示了一个 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
你可以通过使用两个 Service Account 进一步简化基础 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