Secret 是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。默认情况下 Secrets 的信息在 etcd 中是以 base64 编码形式保存的明文。
介绍
创建命令:
root@k8s-master:~/manifests/configmap# kubectl create secret --help
Create a secret using specified subcommand.
Available Commands: # 支持三种类型
docker-registry Create a secret for use with a Docker registry
generic Create a secret from a local file, directory, or literal value
tls Create a TLS secret
Usage:
kubectl create secret [flags] [options]
Use "kubectl <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all commands).
其中:
docker-registry
类型的 Secret 可以在 kubectl explain pod.spec.imagePullSecrets
时使用
- generic 通用的
- tls 放 SSL 证书
secret generic 示例
$ kubectl create secret generic mysql-root-pwd --from-literal=pwd=123456
secret/mysql-root-pwd created
$ kubectl get secrets mysql-root-pwd
NAME TYPE DATA AGE
mysql-root-pwd Opaque 1 32s
$ kubectl get secrets mysql-root-pwd -o yaml
apiVersion: v1
data:
pwd: MTIzNDU2
kind: Secret
metadata:
creationTimestamp: "2022-03-20T18:51:10Z"
name: mysql-root-pwd
namespace: default
resourceVersion: "1036369"
uid: ae7920fd-e464-42d5-82e4-2ff0d7a2d139
type: Opaque
$ echo MTIzNDU2 | base64 -d
123456
secret tls 示例
$ kubectl create secret tls nginx-secrets --cert ./xiexianbin.cn.bundle.crt --key ./xiexianbin.cn.key
secret/nginxsecret created
$ kubectl get secrets nginxsecret
NAME TYPE DATA AGE
nginxsecret kubernetes.io/tls 2 8s
$ kubectl get secrets nginxsecret -o yaml
apiVersion: v1
data:
tls.crt: Q2VydGlmaWNhdGU6CiAgIC...
tls.key: LS0tLS1CRUdJTiBQUklWQV...
kind: Secret
metadata:
creationTimestamp: "2022-03-06T18:22:54Z"
name: nginxsecret
namespace: default
resourceVersion: "196835"
uid: 7489fe98-9bbd-4c1a-8653-78c618648d9d
type: kubernetes.io/tls
nginx-secrets.yaml
apiVersion: "v1"
kind: "Secret"
metadata:
name: "nginxsecret"
namespace: "default"
type: kubernetes.io/tls
data:
tls.crt: "base64 code"
tls.key: "base64 code"
创建命令
kubectl apply -f nginx-secrets.yaml
docker-registry 示例
- kubectl create secret docker-registry 方式
kubectl create secret docker-registry <NAME> \
--docker-server=DOCKER_REGISTRY_SERVER \
--docker-username=DOCKER_USER \
--docker-password=DOCKER_PASSWORD \
--docker-email=DOCKER_EMAIL
root@k8s-master:~# kubectl create secret docker-registry docker-secret --docker-server=https://index.docker.io/v1/ --docker-username=xianbinxie --docker-password=abc --docker-email=abc@q.com
secret/docker-secret created
root@k8s-master:~# kubectl get secrets
NAME TYPE DATA AGE
docker-secret kubernetes.io/dockerconfigjson 1 11s
root@k8s-master:~# kubectl describe secrets docker-secret
Name: docker-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/dockerconfigjson
Data
====
.dockerconfigjson: 116 bytes
# 挂载
volumes:
- name: docker-secret
projected:
sources:
- secret:
name: docker-secret
items:
- key: .dockerconfigjson
path: .docker/config.json
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: xiexianbin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
$ cat ~/.docker/config.json
{
"auths": {
"": {
"auth": "xxx"
}
}
}
说明: ""
为 hub.docker.com,参考 https://docs.docker.com/engine/reference/commandline/login/#credentials-store
- 方法二:手动生成 ~/.docker/config.json
# 1 auth生成base64加密,此用户密码为docker镜像仓库的账号密码
$ echo -n "<user>:<password>" | base64
# 2 生成config.json
$ mkdir ${HOME}/.docker
$ vim ${HOME}/.docker/config.json
{
"auths": {
"https://hub.docker.com": {
"auth": "PHVzZXI+OjxwYXNzd29yZD4="
}
}
}
$ kubectl create secret generic docker-secret --from-file=config.json=/root/.docker/config.json -o yaml --dry-run=client
apiVersion: v1
data:
.dockerconfigjson: <cat /root/.docker/config.json | base64>
kind: Secret
metadata:
creationTimestamp: null
name: docker-secret
- 基于已有的 Docker 凭据创建 Secret,通过配置
pod.spec.ImagePullSecrets
在 Pod 中设置容器镜像仓库的密钥
cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: xiexianbin/busybox:1
imagePullSecrets:
- name: docker-secret
EOF
git credential
$ kubectl create secret generic ssh-key-secret --from-file=id_rsa=/root/.ssh/id_rsa --from-file=id_rsa.pub=/root/.ssh/id_rsa.pub -o yaml --dry-run=client
apiVersion: v1
data:
id_rsa: <cat id_rsa | base64>
id_rsa.pub: <cat id_rsa.pub | base64>
kind: Secret
metadata:
creationTimestamp: null
name: ssh-key-secret
kubectl create secret generic git-credential --from-file=.git-credentials=/root/.git-credentials --from-file=.gitconfig=/root/.gitconfig -o yaml --dry-run=client
apiVersion: v1
data:
.git-credentials: <cat /root/.git-credentials | base64>
.gitconfig: <cat /root/.gitconfig | base64>
kind: Secret
metadata:
creationTimestamp: null
name: git-credential
以 ENV + Volume 方式将 Secret 注入 Pod 示例
apiVersion: v1
kind: Pod
metadata:
name: secret-pod
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PWD
valueFrom:
secretKeyRef:
name: mysql-root-pwd
key: pwd
$ kubectl apply -f pod-secret.yaml
pod/secret-pod created
$ kubectl exec -it secret-pod -- printenv | grep MYSQL_ROOT
MYSQL_ROOT_PWD=123456
Sealed Secrets 介绍
Sealed Secrets 主要由两个组件组成:
- 一个是集群内的 Kubernetes Operator
- 一个是名为 kubeseal 的客户端工具
kubeseal 允许我们使用非对称加密算法来加密 Kubernetes Secrets 对象,SealedSecret 是包含加密 Secret 的 Kubernetes CRD 资源对象,只有控制器可以进行解密,所以即使把 SealedSecret 存储在公共的代码仓库中也是非常安全的。
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.13.1/kubeseal-linux-amd64
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
# kubeseal --version
kubeseal version: v0.13.1
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.13.1/controller.yaml
$ kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.13.1/controller.yaml
Warning: rbac.authorization.k8s.io/v1beta1 Role is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 Role
role.rbac.authorization.k8s.io/sealed-secrets-key-admin created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRoleBinding
clusterrolebinding.rbac.authorization.k8s.io/sealed-secrets-controller created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRole is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRole
clusterrole.rbac.authorization.k8s.io/secrets-unsealer created
service/sealed-secrets-controller created
Warning: rbac.authorization.k8s.io/v1beta1 RoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 RoleBinding
rolebinding.rbac.authorization.k8s.io/sealed-secrets-service-proxier created
role.rbac.authorization.k8s.io/sealed-secrets-service-proxier created
rolebinding.rbac.authorization.k8s.io/sealed-secrets-controller created
serviceaccount/sealed-secrets-controller created
deployment.apps/sealed-secrets-controller created
Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition
customresourcedefinition.apiextensions.k8s.io/sealedsecrets.bitnami.com created
$ kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
sealed-secrets-controller-5b54cbfb5f-h8mnx 1/1 Running 0 43s
[xiexianbin@proxy ~]$ kubectl get pods -n kube-system -l name=sealed-secrets-controller
NAME READY STATUS RESTARTS AGE
sealed-secrets-controller-5b54cbfb5f-h8mnx 1/1 Running 0 3m22s
$ echo -n "This is a secret" | kubectl create secret generic mysecret --dry-run --from-file=secret=/dev/stdin -o yaml > secret.yaml
W1222 15:09:02.021124 32263 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
[xiexianbin@proxy ~]$ cat secret.yaml
apiVersion: v1
data:
secret: VGhpcyBpcyBhIHNlY3JldA==
kind: Secret
metadata:
creationTimestamp: null
name: mysecret
$ kubeseal --format yaml < secret.yaml > sealedsecret.yaml
[xiexianbin@proxy ~]$ cat sealedsecret.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: mysecret
namespace: xiexianbin
spec:
encryptedData:
secret: AgAlQZk9qp0e8yUCATh9FYZXpd4DsmXs/bAx7a3OdELUZCIygv/4sY8B5xZ4H6Fve5rujYCfZBaf2nPFwgRbDouKT0qNi8B4Y7945o/p5BUkIier4IAii+35JE6jDYQcV5/AT+6TFtJu7ciLJxpPDPFR6QAjskH3eFW0ntMIzHS20+/0/GNP/bLvV0pHk0cI532/Llhy79rI3yPM+w/fnFFAfXI88l7POkmiVBpicg/NuPIwzKyvue9aH68/W+CaTkL3NQ0vN3enzQjndJJ5i3U+ImYI+X4fU6Cl9a6tR/6avHFlktc3OJ2x1TwD40dOyGeAdvdwKCUE6ym3fGh6fxOGlg8bnKFP2YVgVReq83+8ojrEekJr5CkID80ksZ6uViHWT22nWOdzOR+rGnrPLd7E3+fT/bqs2iz+shPun3Lz4XU6SIOJrzcmjJWufqWG3sX4JPYR0MXa+gUfkhpdtBYOtkOejudppxSa670oeIxWxXGg3h2Dug6tkg2/Q6j4ekA+ZucCBNpIzSN+EA8onXx8FIO7UfNd5Mn8n8uoEP3dPiVhRUY2886ljwx/QG8B5LZGVhXmLbWEzlOhM6AIumhD/Exm8vdn2UzQZCKsWScOJQ+DfF1FWSQG25ggCmRsI8xJuGOwJjy9GkVrEMz0uKfXVQUlBLMSdwmca23mJtXJNL0ALPuovVHCO6+laNGihHRdfJRLCBR7k3K6rWG36QwM
template:
metadata:
creationTimestamp: null
name: mysecret
namespace: xiexianbin
$ kubectl apply -f sealedsecret.yaml
sealedsecret.bitnami.com/mysecret created
[xiexianbin@proxy ~]$ kubectl get secrets
NAME TYPE DATA AGE
default-token-csh6d kubernetes.io/service-account-token 3 84s
mysecret Opaque 1 10s
[xiexianbin@proxy ~]$ kubectl get secrets mysecret -o yaml
apiVersion: v1
data:
secret: VGhpcyBpcyBhIHNlY3JldA==
kind: Secret
metadata:
creationTimestamp: "2020-12-22T07:24:11Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:secret: {}
f:metadata:
f:ownerReferences:
.: {}
k:{"uid":"b45fea4e-2621-42b6-b895-f9ec16637264"}:
.: {}
f:apiVersion: {}
f:controller: {}
f:kind: {}
f:name: {}
f:uid: {}
f:type: {}
manager: controller
operation: Update
time: "2020-12-22T07:24:11Z"
name: mysecret
namespace: xiexianbin
ownerReferences:
- apiVersion: bitnami.com/v1alpha1
controller: true
kind: SealedSecret
name: mysecret
uid: b45fea4e-2621-42b6-b895-f9ec16637264
resourceVersion: "1498197"
uid: 55961a0c-10fb-438d-8d74-45f5e98987e5
type: Opaque
查看:
kubectl apply -f - << EOF
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
app: busybox
spec:
containers:
- name: test
image: busybox
imagePullPolicy: IfNotPresent
command:
- sleep
- "3600"
volumeMounts:
- name: mysecretvol
mountPath: "/tmp/mysecret"
readOnly: true
volumes:
- name: mysecretvol
secret:
secretName: mysecret
EOF
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- cat /tmp/mysecret/secret
This is a secret
修改命名空间
cp sealedsecret.yaml sealedsecret-default.yaml
sed -i 's/xiexianbin/default/g' sealedsecret-test.yaml
$ kubectl apply -f sealedsecret-default.yaml -n default
sealedsecret.bitnami.com/mysecret created
[xiexianbin@proxy ~]$ kubectl get secrets -n default
NAME TYPE DATA AGE
$ kubectl get sealedsecrets -A
NAMESPACE NAME AGE
default mysecret 90s
xiexianbin mysecret 9m34s
kubectl get sealedsecret -n default
NAME AGE
mysecret 106s
$ kubectl get sealedsecrets.bitnami.com
NAME AGE
mysecret 10m
可以看到 SealedSecret 对象是存在的,但是没有生成对应的 Secret,我们去查看下控制器的日志:
$ kubectl logs -f sealed-secrets-controller-5b54cbfb5f-h8mnx -n kube-system
...
2020/12/22 07:32:14 Error updating default/mysecret, giving up: no key could decrypt secret (secret)
E1222 07:32:14.961964 1 controller.go:200] no key could decrypt secret (secret)
2020/12/22 07:32:14 Event(v1.ObjectReference{Kind:"SealedSecret", Namespace:"default", Name:"mysecret", UID:"f71eb11b-7bfa-4c23-977e-36ff391372df", APIVersion:"bitnami.com/v1alpha1", ResourceVersion:"1498915", FieldPath:""}): type: 'Warning' reason: 'ErrUnsealFailed' Failed to unseal: no key could decrypt secret (secret)
可以看到了出现了 no key could decrypt secret (secret) 这样的错误信息,这是因为我们使用的默认的 strict 作用域,所以更改 SealedSecret 的命名空间是不生效的。
将 Secret 挂载到 Volume 中
apiVersion: v1
kind: Pod
metadata:
labels:
name: db
name: db
spec:
volumes:
- name: secrets
secret:
secretName: mysecret
containers:
- image: gcr.io/my_project_id/pg:v1
name: db
volumeMounts:
- name: secrets
mountPath: "/etc/secrets"
readOnly: true
ports:
- name: cp
containerPort: 5432
hostPort: 5432
查看 Pod 中对应的信息:
# ls /etc/secrets
password username
# cat /etc/secrets/username
admin
# cat /etc/secrets/password
1f2d1e2e67df将 Secret 挂载到 Volume 中
apiVersion: v1
kind: Pod
metadata:
labels:
name: db
name: db
spec:
volumes:
- name: secrets
secret:
secretName: mysecret
containers:
- image: gcr.io/my_project_id/pg:v1
name: db
volumeMounts:
- name: secrets
mountPath: "/etc/secrets"
readOnly: true
ports:
- name: cp
containerPort: 5432
hostPort: 5432
查看 Pod 中对应的信息:
# ls /etc/secrets
password username
# cat /etc/secrets/username
admin
# cat /etc/secrets/password
1f2d1e2e67df
$ kubectl get secrets
NAME TYPE DATA AGE
default-token-csh6d kubernetes.io/service-account-token 3 61m
mysecret Opaque 2 8s
[xiexianbin@proxy ~]$ kubectl get secrets mysecret
NAME TYPE DATA AGE
mysecret Opaque 2 10s
[xiexianbin@proxy ~]$ kubectl get secrets mysecret -o wide
NAME TYPE DATA AGE
mysecret Opaque 2 16s
[xiexianbin@proxy ~]$ kubectl get secrets mysecret -o yaml
apiVersion: v1
data:
password: MWYyZDFlMmU2N2Rm
username: YWRtaW4=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"password":"MWYyZDFlMmU2N2Rm","username":"YWRtaW4="},"kind":"Secret","metadata":{"annotations":{},"name":"mysecret","namespace":"xiexianbin"},"type":"Opaque"}
creationTimestamp: "2020-12-22T08:24:43Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:password: {}
f:username: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
f:type: {}
manager: kubectl-client-side-apply
operation: Update
time: "2020-12-22T08:24:43Z"
name: mysecret
namespace: xiexianbin
resourceVersion: "1503527"
uid: 716d74ef-60d9-47e2-be01-c868023eea30
type: Opaque
Opaque Secret 的使用
创建好 secret 之后,有两种方式来使用它:
以 Volume 方式
以环境变量方式
将 Secret 挂载到 Volume 中
apiVersion: v1
kind: Pod
metadata:
labels:
name: busybox
name: busybox
spec:
volumes:
- name: secrets
secret:
secretName: mysecret
containers:
- image: busybox
name: busybox
command:
- sleep
- "3600"
volumeMounts:
- name: secrets
mountPath: "/etc/secrets"
readOnly: true
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 8s
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- ls /etc
group hosts mtab passwd secrets
hostname localtime network resolv.conf shadow
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- ls /etc/secrets
password username
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- cat /etc/secrets/username
admin[xiexianbin@proxy ~]$ kubectl exec -it busybox -- cat /etc/secrets/password
1f2d1e2e67df[xiexianbin@proxy ~]$
将 Secret 导出到环境变量中
apiVersion: v1
kind: Pod
metadata:
labels:
name: busybox
name: busybox
spec:
volumes:
- name: secrets
secret:
secretName: mysecret
containers:
- image: busybox
name: busybox
command:
- sleep
- "3600"
env:
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
$ kubectl exec -it pod/busybox -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=busybox
TERM=xterm
WORDPRESS_DB_USER=admin
WORDPRESS_DB_PASSWORD=1f2d1e2e67df
KUBERNETES_PORT_443_TCP=tcp://10.20.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.20.0.1
KUBERNETES_SERVICE_HOST=10.20.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.20.0.1:443
HOME=/root
将 Secret 挂载指定的 key
apiVersion: v1
kind: Pod
metadata:
labels:
name: busybox
name: busybox
spec:
volumes:
- name: secrets
secret:
secretName: mysecret
items:
- key: password
mode: 0511
path: tst/psd
- key: username
mode: 0511
path: tst/usr
containers:
- image: busybox
name: busybox
command:
- sleep
- "3600"
volumeMounts:
- name: secrets
mountPath: "/etc/secrets"
readOnly: true
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 8s
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- ls /etc/
group hosts mtab passwd secrets
hostname localtime network resolv.conf shadow
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- ls /etc/secrets
tst
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- ls /etc/secrets/tst
psd usr
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- cat /etc/secrets/tst/psd
1f2d1e2e67df
[xiexianbin@proxy ~]$ kubectl exec -it busybox -- cat /etc/secrets/tst/usr
admin
v1.7 + 版本支持将 Secret 数据加密存储到 etcd 中,只需要在 apiserver 启动时配置 --experimental-encryption-provider-config
。
加密插件只是加密了etcd中保存的数据,这意味着你执行kubectl get secrets mysecret -o yaml
这样的命令看到的仍然是明文,在容器内部注入的secrets文件或者环境变量看到的也是明文,原因当然是kube-apiserver在从etcd中取出数据的时候已经帮你自动解密了。
其他
从 kubernetes v1.14 开始,可以使用 Kustomize 管理 Secret