在另外一章里面分享了ConfigMap的使用,我们知道ConfigMap这个资源对象是Kubernetes当中非常重要的一个对象,一般情况下ConfigMap是用来存储一些非安全的配置信息,如果涉及到一些安全相关的数据的话用ConfigMap就非常不安全,因为ConfigMap是明文存储的,我们说这个时候我们就需要用到另外一个资源对象了:Secret,Secret用来保存敏感信息,例如密码、OAuth 令牌和 ssh key等等,将这些信息放在Secret中比放在Pod的定义中或者docker镜像中来说更加安全和灵活,也便于更改,分发和使用。
Secret有四种TYPE(类型):
Opaque 类型的数据是一个 map 类型,要求value是base64编码格式,比如我们来创建一个用户名为 admin,密码为 Abcd@123的 Secret 对象,首先我们先把这用户名和密码做 base64 编码,如下:
[root@k8s-m1 tmp]# echo -n "admin" | base64
YWRtaW4=
[root@k8s-m1 tmp]# echo -n "Abcd@123" | base64
QWJjZEAxMjM=
然后我们就可以使用上面编码得到的数据来编写一个YML文件:secret-opaque.yml
[root@k8s-m1 tmp]# cat secret-opaque.yml
apiVersion: v1
kind: Secret
metadata:
name: opaquesecret
type: Opaque
data:
username: YWRtaW4=
password: QWJjZEAxMjM=
#创建
[root@k8s-m1 tmp]# kubectl apply -f secret-opaque.yml
secret/opaquesecret created
#查看
[root@k8s-m1 tmp]# kubectl get secrets
NAME TYPE DATA AGE
NAME TYPE DATA AGE
default-token-glxls kubernetes.io/service-account-token 3 417d
opaquesecret Opaque 2 18m
##其中default-token-glxls为创建集群时默认创建的secret,被serviceacount/default 引用。
#查看详情:
[root@k8s-m1 tmp]# kubectl describe secrets opaquesecret
Name: opaquesecret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 8 bytes
username: 5 bytes
从上面我们可以看到利用describe命令查看到的Data没有直接显示出来,如果想看到Data里面的详细信息,同样我们可以通过输出成YAML文件进行查看:
[root@k8s-m1 tmp]# kubectl get secrets opaquesecret -o yaml
apiVersion: v1
data:
password: QWJjZEAxMjM=
username: YWRtaW4=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"password":"QWJjZEAxMjM=","username":"YWRtaW4="},"kind":"Secret","metadata":{"annotations":{},"name":"opaquesecret","namespace":"default"},"type":"Opaque"}
creationTimestamp: "2023-07-05T05:51:57Z"
......
通过describe可以查看编码后的值,然而通过这个值是可以反向解码得到最初的值,所以说使用secret也只能从一定程度上解决了安全问题。
[root@k8s-m1 tmp]# echo "YWRtaW4="|base64 -d
admin[root@k8s-m1 tmp]# echo "QWJjZEAxMjM="|base64 -d
Abcd@123[root@k8s-m1 tmp]#
#可以看到最初设置的账号和密码
[root@k8s-m1 tmp]# echo -n 'admin' > ./username.txt
[root@k8s-m1 tmp]# echo -n 'Abcd@123' > ./password.txt
[root@k8s-m1 tmp]# kubectl create secret generic opaquesecret-1 \
--from-file=./username.txt \
--from-file=./password.txt
secret/opaquesecret-1 created
[root@k8s-m1 tmp]# kubectl get secrets opaquesecret-1 -o yaml
apiVersion: v1
data:
password.txt: QWJjZEAxMjM=
username.txt: YWRtaW4=
[root@k8s-m1 tmp]# echo "QWJjZEAxMjM=" |base64 -d
Opaque类型也可以用于存放证书类型,如下通过secret创建一个dashboard使用的证书:
[root@k8s-m1 ~]# kubectl create secret generic ingress-secret --from-file=./dashboard.crt --from-file=dashboard.key=./dashboard.key -n kube-system
secret/ingress-secret created
[root@k8s-m1 ~]# kubectl get secrets -n kube-system
NAME TYPE DATA AGE
default-token-w64jw kubernetes.io/service-account-token 3 38m
ingress-secret Opaque 2 11s
[root@k8s-m1 ~]# kubectl describe secrets -n kube-system ingress-secret
除了上面的Opaque这种类型外,我们还可以来创建用户docker registry认证的Secret,直接使用kubectl create命令创建即可,如下:
[root@k8s-m1 ~]# kubectl create secret docker-registry myregistry --docker-server=DOCKER_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
#或者
[root@k8s-m1 ~]# kubectl create secret generic docker-registry --from-file=.dockerconfigjson=./.docker/config.json --type=kubernetes.io/dockerconfigjson
secret/docker-registry created
#注意config.json文件是我们手动登录镜像仓库自动生成的
然后查看Secret列表:
[root@k8s-m1 ~]# kubectl get secrets
NAME TYPE DATA AGE
docker-registry kubernetes.io/dockerconfigjson 1 2m42s
myregistry kubernetes.io/dockerconfigjson 1 4m44s
注意看上面的TYPE类型,都是kubernetes.io/dockerconfigjson,同样的可以使用describe命令来查看详细信息:
[root@k8s-m1 ~]# kubectl describe secrets myregistry
Name: myregistry
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/dockerconfigjson
Data
====
.dockerconfigjson: 92 bytes
同样的可以看到Data区域没有直接展示出来,如果想查看的话可以使用-o yaml来输出展示出来:
[root@k8s-m1 ~]# kubectl describe secrets myregistry
Name: myregistry
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/dockerconfigjson
Data
====
.dockerconfigjson: 92 bytes
[root@k8s-m1 ~]# kubectl get secret myregistry \-o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyIxOTIuMTY4LjIuMTQwIjp7InVzZXJuYW1lIjoicm9vdCIsInBhc3N3b3JkIjoibWFyZ3UiLCJhdXRoIjoiY205dmREcHRZWEpuZFE9PSJ9fX0=
kind: Secret
metadata:
creationTimestamp: "2023-07-05T06:31:29Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:.dockerconfigjson: {}
f:type: {}
manager: kubectl-create
operation: Update
time: "2023-07-05T06:31:29Z"
name: myregistry
namespace: default
resourceVersion: "84486293"
selfLink: /api/v1/namespaces/default/secrets/myregistry
uid: 402c3738-4784-42ff-9bba-af4245d08d6c
type: kubernetes.io/dockerconfigjson
##使用base64 反解码查看
[root@k8s-m1 ~]# echo eyJhdXRocyI6eyIxOTIuMTY4LjIuMTQwIjp7InVzZXJuYW1lIjoicm9vdCIsInBhc3N3b3JkIjoibWFyZ3UiLCJhdXRoIjoiY205dmREcHRZWEpuZFE9PSJ9fX0=|base64 -d
{"auths":{"192.168.2.140":{"username":"root","password":"margu","auth":"cm9vdDptYXJndQ=="}}}[root@k8s-m1 ~]#
如果我们需要拉取私有仓库中的docker镜像的话就需要使用到上面的myregistry这个Secret:
[root@k8s-m1 tmp]# cat imagesecret-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: busybox
image: 192.168.2.140/busybox:v1
imagePullSecrets:
- name: myregistry
#前提保证镜像仓库已经有这个镜像,且凭证正确创建
[root@k8s-m1 tmp]# kubectl apply -f imagesecret-pod.yml
pod/busybox created
如上我们通过创建的Secret凭证,从私有仓库中拉取了镜像192.168.2.140/busybox:v1部署业务。
kubernetes.io/tls用于为SSL通信模式存储证书和私钥文件,一般用于构建TLS站点(https类型的网站)。
[root@k8s-m1 tmp]# openssl genrsa -out tls.key 2048
Generating RSA private key, 2048 bit long modulus
...................................+++
.................................................................................................................................................+++
e is 65537 (0x10001)
[root@k8s-m1 tmp]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=margu.com
[root@k8s-m1 tmp]# ls -al tls.*
-rw-r--r-- 1 root root 1273 Jul 5 15:08 tls.crt
-rw-r--r-- 1 root root 1679 Jul 5 15:07 tls.key
[root@k8s-m1 tmp]# kubectl create secret tls nginx-secret --cert=tls.crt --key=tls.key -n ingress-nginx
secret/nginx-secret created
[root@k8s-m1 tmp]# kubectl describe secrets -n ingress-nginx nginx-secret
Name: nginx-secret
Namespace: ingress-nginx
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.key: 1679 bytes
tls.crt: 1273 bytes
#如何引用请查看ingress章节
有些情况下,我们希望在 pod 内部访问 apiserver,获取集群的信息,甚至对集群进行改动。针对这种情况,kubernetes 提供了一种特殊的认证方式:Service Account。 Service Account 是面向 namespace 的,每个 namespace 创建的时候,kubernetes 会自动在这个 namespace 下面创建一个默认的 Service Account;并且这个 Service Account 只能访问该 namespace 的资源。Service Account 和 pod、service、deployment 一样是kubernetes 集群中的一种资源,用户也可以创建自己的 serviceaccount。
ServiceAccount 主要包含了三个内容:namespace、Token 和 CA。namespace 指定了 pod 所在的 namespace,CA 用于验证 apiserver 的证书,token 用作身份验证。它们都通过mount 的方式挂载到 pod 中,其中 token 保存的路径是 /var/run/secrets/kubernetes.io/serviceaccount/token ,是 kube-controller-manager 通过–service-account-private-key-file私钥签发的token; CA 保存的路径是 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt ,namespace 保存的路径是 /var/run/secrets/kubernetes.io/serviceaccount/namespace 。
如果 token 能够通过认证,那么请求的用户名将被设置为 system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT) ,而请求的组名有两个: system:serviceaccounts 和 system:serviceaccounts:(NAMESPACE)。
以kube-system namespace下的“default” service account为例,Pod的usrname全称为:system:serviceaccount:kube-system:default。有了username,那么credentials呢?就是上面提到的service-account-token中的token。API Server的验证环节支持多种身份校验方式:CA 证书认证、Token 认证、Base 认证。一旦API Server发现client发起的request使用的是service account token的方式,API Server就会自动采用signed bearer token方式进行身份校验。而request就会使用携带的service account token参与验证。该token是API Server在创建service account时用API server启动参数:–service-account-key-file=/etc/kubernetes/pki/sa.pub的值签署(sign)生成的。如果–service-account-key-file=未传入任何值,那么将默认使用–tls-private-key-file的值,即API Server的私钥。
这里我们使用一个nginx镜像来验证一下,大家想一想为什么不是呀busybox镜像来验证?当然也是可以的,但是我们就不能在command里面来验证了,因为token是需要Pod运行起来过后才会被挂载上去的,直接在command命令中去查看肯定是还没有 token 文件的。
[root@k8s-m1 tmp]# cat test-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default
labels:
name: myapp
spec:
containers:
- name: test-pod
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c"," sleep 3600"]
[root@k8s-m1 tmp]# kubectl apply -f test-pod.yml
[root@k8s-m1 tmp]# kubectl exec -it liveness-exec-pod-1 -- /bin/sh
/ # ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt namespace token
/ # cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6IjJMRm8zNktIWkdvaHRDZ2szX2Vyd3JFQTE5OEZ6TjNuRnUyYTFueGE3emMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tZ2x4bHMiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjE4YjkxN2ZkLTYyZTAtNDViYi1hMjNkLWQ2NDQwYTAyZDJjNyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.GFkJpzoThq4C4lGgMBiNpgQiMxktaSMl0Bg2-wZYn0dNJ-hW5HFJJzRFODVkZOjo15NTiBC234qZq6xx1lCGBx-pZJRM4mjuXM93U0AJEGeOL-LyvgUJLG4ar3popNpkQxLvEMn4qpVgdiuU7oQuFT0qw70j4cjR1-jE9ew2yS_7iYiCDBUHmNBKues-hr4EvfNKDUvN45DdEWBiWH2WrNlsh9G-0S8GKYDYRByhR0CU4lPPNeCGz-VsfrEQ9PUnU-q4Fufc2nmokv5nRJuY7eWYJf67HXpTd7tPh_hXCvn1XmEqlCCH1uE9zFMFatmIWTbXtyIwWRMZePnBQ3BlVQ
/ #
那么创建好Secret对象后,我们如何使用它呢,目前有两种方式来使用它:
方法一:可以指定使用某些key
首先我们来测试下环境变量的方式,我们使用一个简单的busybox镜像来测试:
#创建测试yaml文件
[root@k8s-m1 tmp]# cat secret-env-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: secret-env
image: busybox
command: [ "/bin/sh", "-c", "sleep 3600s" ]
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: opaquesecret
key: username
# - name: PASSWORD
# valueFrom:
# secretKeyRef:
# name: opaquesecret
# key: password
##部署
[root@k8s-m1 tmp]# kubectl apply -f secret-env-pod.yml
pod/secret-env-pod created
##检查测试
[root@k8s-m1 tmp]# kubectl exec -it secret-env-pod -- /bin/sh
/ # env |grep -Ei 'user|pass'
USERNAME=admin
#PASSWORD=Abcd@123
/ #
说明:可以只使用部分key
方法二:一次性使用全部的key
[root@k8s-m1 tmp]# cat secret-env-pod-all.yml
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod-all
spec:
containers:
- name: secret-env-all
image: busybox
command: [ "/bin/sh", "-c", "sleep 3600s" ]
envFrom:
- secretRef:
name: opaquesecret
restartPolicy: Never
[root@k8s-m1 tmp]# kubectl apply -f secret-env-pod-all.yml
pod/secret-env-pod-all created
[root@k8s-m1 tmp]# kubectl exec -it secret-env-pod-all -- /bin/sh
/ # env |grep -Ei 'user|pass'
username=admin
password=Abcd@123
/ #
可以看到上面两种方法env都有对应的环境变量,第一种可以指定使用某些key,第二种直接将全部的key生效,实际使用中我们偏向第二种,都生效至于调不调用无所谓。
同样的我们用一个Pod来验证下Volume挂载
#创建测试yaml文件
[root@k8s-m1 tmp]# cat secret-volume-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-pod
spec:
containers:
- name: secret-volume
image: busybox
command: ["/bin/sh", "-c", "sleep 3600s"]
volumeMounts:
- name: secrets
mountPath: /etc/secrets
volumes:
- name: secrets
secret:
secretName: opaquesecret
#部署
[root@k8s-m1 tmp]# kubectl apply -f secret-volume-pod.yml
pod/secret-volume-pod created
#检查测试
[root@k8s-m1 tmp]# kubectl exec -it secret-volume-pod -- /bin/sh
/ # ls /etc/secrets/
password username
/ # cat /etc/secrets/username
admin/ #
/ # cat /etc/secrets/password
Abcd@123/ #
/ #
可以看到secret把两个key挂载成了两个对应的文件。
最后我们来对比下Secret和ConfigMap这两种资源对象的异同点:
更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出