程序员在旅途

用这生命中的每一秒,给自己一个不后悔的未来!

0%

k8S中Secret创建及使用分析

一、Secret资源对象概述

Secret 是Kubernetes的一个用来保存敏感信息,例如密码、OAuth 令牌和 ssh key的资源对象,它可以让应用程序在容器内使用这些敏感信息,同时又不需要将这些信息直接写在容器配置文件中,将应用中的这些敏感信息放在 Secret 中比放在 pod 的定义中或者 容器镜像中更加安全和灵活。

1.1 Secret的安全性

默认情况下,Secret资源对象以不加密方式存储在集群的 ETCD数据库中。 对API服务器或ETCD有操作权限的情况下都可以对Secret资源对象进行更新等行为,此外,对在Secret的namespaces中有资源操作权限的用户也可以对Secret进行同样权限的行为。

1.2 Secret的类型

一般主要有三种类型的Secret,分别为 kubernetes.io/service-account-token、kubernetes.io/dockerconfigjson、Opaque 三种类型

Opaque 用户定义的任意数据
kubernetes.io/service-account-token 服务账号令牌,用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中。
kubernetes.io/dockerconfigjson ~/.docker/config.json 文件的序列化形式,用来存储私有docker registry的认证信息。

二、Secret的创建

Secret的数据存放在ETCD中,每个 Secret 的尺寸最多为 1MiB,data 字段中所有键值都必须是 base64 编码的字符串。
一般通过以下两种方式创建Secret:

使用 kubectl 命令来创建 Secret;
基于配置文件来创建 Secret。

在不了解命令的情况下,可以使用 kubectl create secret -h 来获取命令创建secret的详细参数。

2.1 Opaque类型secret

使用kubectl命令创建:
先生成需要的文件:
1
2
$ echo -n "admin" > ./username.txt
$ echo -n "password1234" > ./password.txt
1
2
kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
kubectl get secret db-user-pass -oyaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@master secret-test]# oc get secret db-user-pass -oyaml
apiVersion: v1
data:
password.txt: cGFzc3dvcmQxMjM0
username.txt: YWRtaW4=
kind: Secret
metadata:
creationTimestamp: 2022-08-15T14:15:53Z
name: db-user-pass
namespace: nginx-demo
resourceVersion: "4203612"
selfLink: /api/v1/namespaces/nginx-demo/secrets/db-user-pass
uid: c5c822de-1ca4-11ed-9dc1-000c29874c9f
type: Opaque

基于配置文件创建:
将字符串转为base64编码:

1
2
echo -n 'admin' | base64
echo -n '1f2d1e2e67df' | base64

得到字符串:

1
2
YWRtaW4=
MWYyZDFlMmU2N2Rm

创建yaml文件清单:

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm

执行apply创建命令:

1
kubectl apply -f ./secret.yaml

2.2 docker registry 认证的 secret

对于需要认证才能使用的Docker registry,需要经过认证以后得到授权才能操作镜像仓库,docker使用以下命令登录registry镜像仓库:

1
$ docker login DOCKER REGISTRY SERVER --username=DOCKER USER --password=DOCKER PASSWORD --email=DOCKER _EMAIL

该命令会创建 ~/.dockercfg ,该文件保存了镜像仓库认证信息,与registry做交互需要依赖这个认证信息。
kubernetes环境下,可以使用以下命令生成用于拉取镜像时认证的Secret:

1
kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL

也可以使用已有的dockercfg文件来生成secret:

1
2
$ kubectl create secret docker-registry myregistrykey \
--from-file="~/.dockercfg"

如果需要更新该Secret的话,可以使用–dry-run -oyaml 生成修改后的yaml,然后使用 apply 更新该对象:

1
kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL  --dry-run -oyaml | kubectl apply -f -

拉取镜像的过程中如果需要secret,可以在 Pod 层面来配置:

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: nginx
spec:
containers:
- name: nginx
image: 192.168.7.2:5000/nginx:1.0
imagePullSecrets:
- name: myregistrykey
## 当然也可以在 ServiceAccount中配置imagePullSecrets来让所有使用了该SA的Pods默认配置该
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
imagePullSecrets:
- name: myregistrykey

2.3 tls类型的 secret

TLS secret 是一种特殊的Secret,它从给定的(public/private)公钥/私钥对创建而来,公钥证书必须是.PEM编码并匹配指定的私钥。
TLS Secret 的一种典型用法是为 Ingress/Router 资源配置传输过程中的数据加密,不过也可以用于其他资源或者直接在负载中使用。 当使用此类型的 Secret 时,Secret 配置中的 data (或 stringData)字段必须包含 tls.key 和 tls.crt 主键。
创建TLS secret 的语法命令如下:

1
kubectl -n <namespaces> create secret tls <secret-name> --cert=path/to/tls.cert --key=path/to/tls.key

该命令生成的tls类型的secret如下:

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
apiVersion: v1
kind: Secret
metadata:
name: secret-tls
type: kubernetes.io/tls
data:
# 值为 base64 编码,这样会掩盖它们,但不会提供任何有用的机密性级别
tls.crt: |
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNVakNDQWJzQ0FnMytNQTBHQ1NxR1NJYjNE
UUVCQlFVQU1JR2JNUXN3Q1FZRFZRUUdFd0pLVURFT01Bd0cKQTFVRUNCTUZWRzlyZVc4eEVEQU9C
Z05WQkFjVEIwTm9kVzh0YTNVeEVUQVBCZ05WQkFvVENFWnlZVzVyTkVSRQpNUmd3RmdZRFZRUUxF
dzlYWldKRFpYSjBJRk4xY0hCdmNuUXhHREFXQmdOVkJBTVREMFp5WVc1ck5FUkVJRmRsCllpQkRR
VEVqTUNFR0NTcUdTSWIzRFFFSkFSWVVjM1Z3Y0c5eWRFQm1jbUZ1YXpSa1pDNWpiMjB3SGhjTk1U
TXcKTVRFeE1EUTFNVE01V2hjTk1UZ3dNVEV3TURRMU1UTTVXakJMTVFzd0NRWURWUVFHREFKS1VE
RVBNQTBHQTFVRQpDQXdHWEZSdmEzbHZNUkV3RHdZRFZRUUtEQWhHY21GdWF6UkVSREVZTUJZR0Ex
VUVBd3dQZDNkM0xtVjRZVzF3CmJHVXVZMjl0TUlHYU1BMEdDU3FHU0liM0RRRUJBUVVBQTRHSUFE
Q0JoQUo5WThFaUhmeHhNL25PbjJTbkkxWHgKRHdPdEJEVDFKRjBReTliMVlKanV2YjdjaTEwZjVN
Vm1UQllqMUZTVWZNOU1vejJDVVFZdW4yRFljV29IcFA4ZQpqSG1BUFVrNVd5cDJRN1ArMjh1bklI
QkphVGZlQ09PekZSUFY2MEdTWWUzNmFScG04L3dVVm16eGFLOGtCOWVaCmhPN3F1TjdtSWQxL2pW
cTNKODhDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQmdRQU1meTQzeE15OHh3QTUKVjF2T2NS
OEtyNWNaSXdtbFhCUU8xeFEzazlxSGtyNFlUY1JxTVQ5WjVKTm1rWHYxK2VSaGcwTi9WMW5NUTRZ
RgpnWXcxbnlESnBnOTduZUV4VzQyeXVlMFlHSDYyV1hYUUhyOVNVREgrRlowVnQvRGZsdklVTWRj
UUFEZjM4aU9zCjlQbG1kb3YrcE0vNCs5a1h5aDhSUEkzZXZ6OS9NQT09Ci0tLS0tRU5EIENFUlRJ
RklDQVRFLS0tLS0K
# 在这个例子中,密钥数据不是真正的 PEM 编码的私钥
tls.key: |
RXhhbXBsZSBkYXRhIGZvciB0aGUgVExTIGNydCBmaWVsZA==

三、Secret的使用方式

一般在Pod 可以两种方式使用Secret:

1.Volume方式,作为挂载到一个或多个容器上的卷中的文件。
2.环境变量方式,作为Pods容器的环境变量。

3.1 Volume方式

可以将Secret对象作为一个Volume挂载到Pods中的容器中,从而使容器中的应用程序能够访问其中存储的敏感信息。

在Pods的定义文件中添加一个Volume对象,其中指定使用的Secret对象和挂载路径:

1
2
3
4
volumes:
- name: secret-volume
secret:
secretName: my-secret

在容器的定义中添加一个VolumeMount对象,将Volume挂载到容器中的指定路径:

1
2
3
volumeMounts:
- name: secret-volume
mountPath: /path/to/secrets

在容器中的应用程序中使用挂载到的Secret文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-image
volumeMounts:
- name: secret-volume
mountPath: /etc/my-secret
command: ["/bin/sh"]
args: ["-c", "cat /etc/my-secret/my-secret-file"]
volumes:
- name: secret-volume
secret:
secretName: my-secret

3.2环境变量方式

可以将Secret对象的数据作为环境变量传递给Pods中的容器,从而使容器中的应用程序能够访问其中存储的敏感信息
在容器的定义中添加一个环境变量,将Secret对象的数据传递给容器:

1
2
3
4
5
6
env:
- name: MY_SECRET
valueFrom:
secretKeyRef:
name: my-secret
key: my-secret-key

valueFrom.secretKeyRef.name 指定要使用的Secret对象的名称;
valueFrom.secretKeyRef.key指定要使用的Secret对象的数据键。

在容器中的应用程序中使用环境变量,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-image
env:
- name: MY_SECRET
valueFrom:
secretKeyRef:
name: my-secret
key: my-secret-key
command: ["/bin/sh"]
args: ["-c", "echo $MY_SECRET"]

在使用环境变量方式时,Secret对象中的数据需要经过base64编码,以便于在环境变量中传递。