前提条件
- 拥有Kubernetes集群环境,可参考:Kubernetes集群搭建
- 理解Kubernetes部署知识,可参考:使用Kubernetes部署第一个应用 、Deloyment控制器
Secret简介
Kubernetes Secret 是一种用于存储敏感信息(如密码、令牌、密钥等)的对象。它可以让你将敏感数据以加密的方式存储在 Kubernetes 集群中,避免在配置文件或容器镜像中以明文形式暴露这些信息。这对于安全地部署应用程序至关重要,例如,在一个包含数据库连接的应用场景中,数据库的用户名和密码就可以存储在 Secret 中。
Secret如何保证数据安全
- 存储加密
- Base64 编码混淆 :虽然 Base64 编码不是真正的加密,但它可以对数据进行一定程度的混淆。在 Kubernetes 中,Secret 数据在存储时会进行 Base64 编码。例如,密码等敏感信息被编码后,以一种不可直接阅读的格式存储在
data
字段下。这使得在存储层面,数据内容不会以明文形式轻易暴露。 - etcd 加密(可选):etcd 是存储 Kubernetes 集群状态包括 Secret 的数据库。可以启用 etcd 加密来增强存储安全。通过配置 API Server 与 etcd 之间的通信加密,使用 TLS(Transport Layer Security)来保护数据传输路径。同时,etcd 本身也支持加密存储数据,防止数据在存储介质上被未授权访问。
- Base64 编码混淆 :虽然 Base64 编码不是真正的加密,但它可以对数据进行一定程度的混淆。在 Kubernetes 中,Secret 数据在存储时会进行 Base64 编码。例如,密码等敏感信息被编码后,以一种不可直接阅读的格式存储在
- 访问控制
- RBAC 严格授权:Role - Based Access Control(RBAC)是 Kubernetes 保障 Secret 安全访问的关键机制。通过定义精确的角色(Role)和角色绑定(RoleBinding),可以限制谁(用户、组或者服务账户)能够对 Secret 进行何种操作(如创建、读取、更新和删除)。例如,对于生产环境中的敏感 Secret,只有特定的运维团队角色可以进行更新操作,开发人员角色可能只有读取权限。
- 命名空间隔离防护:Secret 是命名空间(Namespace)范围内的资源。不同命名空间中的 Secret 相互隔离,这提供了天然的访问屏障。例如,开发环境的 Secret 存放在开发命名空间,生产环境的 Secret 存放在生产命名空间,默认情况下,开发环境的用户无法访问生产命名空间的 Secret,有效防止了跨环境的非授权访问。
- 传输安全
- TLS 加密传输:在集群内部,Secret 数据在传输过程中可以通过 TLS 进行加密。当 API Server 与节点之间或者不同组件之间传输 Secret 相关的数据时,TLS 通道确保数据在网络上以加密的形式传输。这防止了数据在传输过程中被中间人窃取或者篡改,保证了数据从存储位置到使用位置的安全性。
- 使用安全措施
- 容器内权限管理:当 Secret 在容器中作为环境变量或者挂载文件使用时,Kubernetes 会谨慎处理。如果是挂载文件,容器内的应用程序可以被配置为以只读权限访问这些文件,避免了文件内容被意外修改。对于作为环境变量注入的 Secret 数据,容器内的进程只能在其运行权限范围内使用这些变量,减少了数据泄露风险。
- 更新谨慎处理:更新 Secret 时,Kubernetes 采取谨慎的策略。已经运行的容器不会自动获取更新后的 Secret 内容,这是为了避免配置突然变化导致应用程序异常。这种机制确保了 Secret 更新过程的可控性,防止因配置更新导致的潜在安全漏洞。只有在经过适当的操作(如重新部署容器或者采用支持热更新且经过安全验证的方式)后,容器才会获取新的 Secret 内容。
Secret 的类型
内置类型 | 用法 |
---|---|
Opaque |
用户定义的任意数据 |
kubernetes.io/service-account-token |
服务账号令牌 |
kubernetes.io/dockercfg |
~/.dockercfg 文件的序列化形式 |
kubernetes.io/dockerconfigjson |
~/.docker/config.json 文件的序列化形式 |
kubernetes.io/basic-auth |
用于基本身份认证的凭据 |
kubernetes.io/ssh-auth |
用于 SSH 身份认证的凭据 |
kubernetes.io/tls |
用于 TLS 客户端或者服务器端的数据 |
这里选取Opaque、service-account-token、dockerconfigjson
类型作案例演示。
Base64编码/解码
- Base64工具:Base64 工具
- 命令行
# 编码
[root@k8s-master01 ~]# echo -n "admin" | base64
YWRtaW4=
# 解码
[root@k8s-master01 ~]# echo -n "YWRtaW4=" | base64 -d
admin
Opaque Secret
创建Secret
Opaque 类型的数据是一个 map 类型,要求value是 base64
编码格式,比如创建一个用户名为 admin,密码为 admin321 的 Secret 对象,首先将用户名和密码做 base64 编码
$ echo -n "admin" | base64
YWRtaW4=
$ echo -n "admin321" | base64
YWRtaW4zMjE=
然后用base64编码过后的数据来编写一个 YAML
文件:(secret-demo.yaml)
创建yaml文件
# 目录准备
[root@k8s-master01 test]# mkdir secrettest
[root@k8s-master01 test]# cd secrettest/
# 创建yaml文件
[root@k8s-master01 secrettest]# vi secret-demo.yaml
内容如下
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: YWRtaW4zMjE=
使用 kubectl
命令来创建了:
[root@k8s-master01 secrettest]# kubectl create -f secret-demo.yaml
secret/mysecret created
利用 get secret
命令查看:
[root@k8s-master01 secrettest]# kubectl get secret
NAME TYPE DATA AGE
mysecret Opaque 2 8s
使用 describe
命令,查看详情:
[root@k8s-master01 secrettest]# kubectl get secret
NAME TYPE DATA AGE
mysecret Opaque 2 8s
[root@k8s-master01 secrettest]# kubectl describe secret mysecret
Name: mysecret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 8 bytes
username: 5 bytes
可以看到利用 describe
命令查看到的 Data
没有直接显示出来,如果想看到 Data
里面的详细信息,可以输出成 YAML
文件进行查看:
[root@k8s-master01 secrettest]# kubectl get secret mysecret -o yaml
apiVersion: v1
data:
password: YWRtaW4zMjE=
username: YWRtaW4=
kind: Secret
metadata:
creationTimestamp: "2024-11-28T15:58:49Z"
name: mysecret
namespace: default
resourceVersion: "563446"
uid: 092ffdfc-8ba7-4ac5-aae2-2787e0fe9cb7
type: Opaque
使用Secret
创建好 Secret
对象后,有两种方式来使用它:
-
以环境变量的形式
-
以Volume的形式挂载
环境变量
创建yaml文件
vi secret1-pod.yaml
内容如下
apiVersion: v1
kind: Pod
metadata:
name: secret1-pod
spec:
containers:
- name: secret1
image: busybox
command: [ "/bin/sh", "-c", "env" ]
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
创建pod
[root@k8s-master01 secrettest]# kubectl create -f secret1-pod.yaml
pod/secret1-pod created
查看pod
[root@k8s-master01 secrettest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
secret1-pod 0/1 ContainerCreating 0 35s
[root@k8s-master01 secrettest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
secret1-pod 0/1 Completed 0 56s
等到pod的状态为Completed,查看 Pod
的日志输出:
[root@k8s-master01 secrettest]# kubectl logs secret1-pod
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.0.0.1:443
HOSTNAME=secret1-pod
SHLVL=1
HOME=/root
USERNAME=admin
KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443
KUBERNETES_SERVICE_HOST=10.0.0.1
PWD=/
PASSWORD=admin321
可以看到有 USERNAME 和 PASSWORD 两个环境变量输出出来。
Volume 挂载
创建yaml文件
[root@k8s-master01 secrettest]# vi secret2-pod.yaml
内容如下
apiVersion: v1
kind: Pod
metadata:
name: secret2-pod
spec:
containers:
- name: secret2
image: busybox
command: ["/bin/sh", "-c", "ls /etc/secrets"]
volumeMounts:
- name: secrets
mountPath: /etc/secrets
volumes:
- name: secrets
secret:
secretName: mysecret
创建 Pod
:
[root@k8s-master01 secrettest]# kubectl create -f secret2-pod.yaml
pod/secret2-pod created
查看pod
[root@k8s-master01 secrettest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
secret1-pod 0/1 CrashLoopBackOff 4 (40s ago) 4m29s
secret2-pod 0/1 Completed 0 36s
查看输出日志:
[root@k8s-master01 secrettest]# kubectl logs secret2-pod
password
username
可以看到 secret
把两个key挂载成了两个对应的文件。
dockerconfigjson Secret
创建docker registry
认证的 Secret
,直接使用 kubectl create
命令创建,如下:
[root@k8s-master01 secrettest]# kubectl create secret docker-registry myregistry --docker-server=DOCKER_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret/myregistry created
然后查看 Secret
列表:
[root@k8s-master01 secrettest]# kubectl get secret
NAME TYPE DATA AGE
myregistry kubernetes.io/dockerconfigjson 1 10s
mysecret Opaque 2 11m
注意看上面的 TYPE
类型,myregistry
是不是对应的 kubernetes.io/dockerconfigjson
,使用 describe
命令来查看详细信息:
[root@k8s-master01 secrettest]# kubectl describe secret myregistry
Name: myregistry
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/dockerconfigjson
Data
====
.dockerconfigjson: 152 bytes
看到 Data
区域没有直接展示出来,如果想查看的话可以使用get命令 -o yaml
来输出展示出来:
[root@k8s-master01 secrettest]# kubectl get secret myregistry -o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJET0NLRVJfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImVtYWlsIjoiRE9DS0VSX0VNQUlMIiwiYXV0aCI6IlJFOURTMFZTWDFWVFJWSTZSRTlEUzBWU1gxQkJVMU5YVDFKRSJ9fX0=
kind: Secret
metadata:
creationTimestamp: "2024-11-28T16:10:35Z"
name: myregistry
namespace: default
resourceVersion: "564657"
uid: 0c379366-e94e-4fa0-9027-28a040d24ecf
type: kubernetes.io/dockerconfigjson
把上面的 data.dockerconfigjson
下面的数据做一个 base64
解码,可以查看docker registry
认证信息
[root@k8s-master01 secrettest]# echo eyJhdXRocyI6eyJET0NLRVJfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImVtYWlsIjoiRE9DS0VSX0VNQUlMIiwiYXV0aCI6IlJFOURTMFZTWDFWVFJWSTZSRTlEUzBWU1gxQkJVMU5YVDFKRSJ9fX0= | base64 -d
{"auths":{"DOCKER_SERVER":{"username":"DOCKER_USER","password":"DOCKER_PASSWORD","email":"DOCKER_EMAIL","auth":"RE9DS0VSX1VTRVI6RE9DS0VSX1BBU1NXT1JE"}}}
如果需要拉取私有仓库中的 docker
镜像的话就需要使用到上面的 myregistry
这个 Secret
:
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
containers:
- name: foo
image: 192.168.1.100:5000/test:v1
imagePullSecrets:
- name: myregistrykey
需要拉取私有仓库镜像 192.168.1.100:5000/test:v1
,就需要针对该私有仓库来创建一个如上的 Secret
,然后在 Pod
的 YAML 文件中指定 imagePullSecrets
service-account-token Secret
service-account-token
,用于被 serviceaccount
引用。serviceaccout 创建时 Kubernetes 会默认创建对应的 secret。
Pod 如果使用了 serviceaccount,对应的secret会自动挂载到Pod的 /run/secrets/kubernetes.io/serviceaccount
目录中。
使用一个 nginx
镜像来验证一下
[root@k8s-master01 secrettest]# kubectl run secret-pod3 --image nginx:1.14.2
pod/secret-pod3 created
[root@k8s-master01 secrettest]# kubectl get pods
NAME READY STATUS RESTARTS AGE
secret-pod3 1/1 Running 0 12s
secret1-pod 0/1 CrashLoopBackOff 6 (4m53s ago) 13m
secret2-pod 0/1 CrashLoopBackOff 6 (74s ago) 9m50s
[root@k8s-master01 secrettest]# kubectl exec secret-pod3 -- ls /run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token
[root@k8s-master01 secrettest]# kubectl exec secret-pod3 -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6Ii1MZ29NaWJ3R0lpQ1hCSTB4bzQzbkp5V0hiXzJsODA2M0lKakRPRkZ2eEEifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzY0MzQ2NjAyLCJpYXQiOjE3MzI4MTA2MDIsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiNDRjYWMwNTktYjQzYi00OTY5LThjNDUtNDBhN2M4NWJhY2QxIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoiazhzLW5vZGUwMSIsInVpZCI6ImQxZDU3ZTgwLWVlYjUtNDRjOC1hZDk4LWYxZjllNzk4MWQ4ZSJ9LCJwb2QiOnsibmFtZSI6InNlY3JldC1wb2QzIiwidWlkIjoiMjRhYmRhYWEtZDhjMC00ODkzLTk4ZTQtNWQyZjM5M2YwN2NkIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiYzBhYjc4ZTAtZDZkMC00NWFmLTlkNTQtOTFhMmI3MGZjOTdkIn0sIndhcm5hZnRlciI6MTczMjgxNDIwOX0sIm5iZiI6MTczMjgxMDYwMiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6ZGVmYXVsdCJ9.GPtgJy4-o3-l2ZmIsc4yY2tdjpTlofS1uOoGaAFoDAjtZ0Xh0ZvRxD6-Ae8NHgLv9Wmu2jY4jiE54-C56R_7N7iDby4zzwa9DuMtytIDk4ZMv85X7GcoBd7z5kfvWU3mMIVZP8dKYopblNYTQX80Homv-HKX-vD1fRasiF13xGmxCWkcxcCM2pZamXDJ2_YZcZ-_buGvZK5Ejfmfm51lmUzICFbcWWd9ZKTx5W5PnlCfUgaCsfq7ZBVz_NjUP9Ufw9a8qGc9in9iGnrOdTetcn1-UW9oqXeVoFUxRAYjYXJR-22iwlCKme5uvtr63r2WPEn-b_-j_tWzTvV_T_BEmQ