【Kubernetes 012】Secret原理和实际操作详解

上一节我们学习了ConfigMap,k8s集群的配置中心。但是因为采用明文存储,ConfigMap并不适合用来存储私密信息,例如密钥等。这一节我们来学习k8s中专门存储私密信息的组件:Secret。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

Secret

很多场景都会用到敏感信息,例如OAuth的token,各种密码,私钥文件。但是k8s采用声明式(Declarative)来定义各种资源,也就是说资源的一些配置信息都存储在定义的yaml或者json文件中。如果直接将这些敏感信息放在yaml文件中,很显然是非常不安全的。于是,k8s引入了Secret组件。

Secret和ConfigMap一样也是k8s中单独的资源,专门用来存储一些敏感信息。把Secret想象成更安全的ConfigMap,就不难理解接下来要学习的对其创建和使用了。

创建Secret

和ConfigMap一样,也是可以通过文件或文件夹来将较长内容加入Secret,或者是通过yaml文件和命令行来将较短内容加入Secret。

下面分开来进行演示。

文件或文件夹创建Secret

用下面的格式来根据文件或者文件夹创建secret

kubectl create secret generic NAME --from-file=xxx

例如有下面两个文件

[root@k8s-master Secret]# ll
total 8
-rw-r--r--. 1 root root 68 May 10 16:40 secret1
-rw-r--r--. 1 root root 58 May 10 16:40 secret2
[root@k8s-master Secret]# cat secret1
d23hehuye8rq340p98du312rpur9er3eru038dfh3ry2098iuerewriu32987er98er
[root@k8s-master Secret]# cat secret2
jp3oiur98sd7re=er=23r-sdf13i4%(eiru2p398(eur1p8u+o3iru2o3

创建两个secret,分别是根据文件和文件夹

[root@k8s-master Secret]# kubectl create secret generic test-secret-1 --from-file=secret1
secret/test-secret-1 created
[root@k8s-master Secret]# kubectl create secret generic test-secret-2 --from-file=.
secret/test-secret-2 created

直接用describe查看是看不到secret的内容的

[root@k8s-master Secret]# kubectl describe secret test-secret-1
Name:         test-secret-1
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
secret1:  68 bytes

可以用下面的方法来查看内容

[root@k8s-master Secret]# kubectl get secret test-secret-1 -o yaml
apiVersion: v1
data:
  secret1: ZDIzaGVodXllOHJxMzQwcDk4ZHUzMTJycHVyOWVyM2VydTAzOGRmaDNyeTIwOThpdWVyZXdyaXUzMjk4N2VyOThlcgo=
kind: Secret
metadata:
  creationTimestamp: "2020-05-10T08:41:18Z"
  name: test-secret-1
  namespace: default
  resourceVersion: "1416449"
  selfLink: /api/v1/namespaces/default/secrets/test-secret-1
  uid: 2e43bebe-dda8-485a-9592-b7ecb32852f6
type: Opaque

有几点要注意下:

  • 自己创建的secret类型都是Opaque
  • secret中还是以键值对存储信息,key是文件名,value是文件的内容
  • 但是value却并不是文件中原始内容,而是经过了某种编码的转换

k8s中通过Base64的方式对原始数据进行了一次编码,注意是编码而不是加密,编码通常意味着很容易就能解码得到原始数据。

我们也可以用Base64命令来解码看看

[root@k8s-master Secret]# echo ZDIzaGVodXllOHJxMzQwcDk4ZHUzMTJycHVyOWVyM2VydTAzOGRmaDNyeTIwOThpdWVyZXdyaXUzMjk4N2VyOThlcgo= | base64 -d -
d23hehuye8rq340p98du312rpur9er3eru038dfh3ry2098iuerewriu32987er98er

yaml文件或者命令行创建Secret

通过yaml文件创建的时候可以直接在里面写原始数据,也可以写base64编码后的数据,两者的字段不一样,分别是datastringData

以下两个文件创建的secret内容一致

apiVersion: v1
kind: Secret
metadata:
  name: test-secret-3
type: Opaque
data:
  superuser: YWRtaW4=
  password: USVGdnFTJCpGJGteNmk=
apiVersion: v1
kind: Secret
metadata:
  name: test-secret-3
type: Opaque
stringData:
  superuser: 'superuser'
  password: 'Q%FvqS$*F$k^6i'

结果都如下

[root@k8s-master Secret]# kubectl get secret test-secret-3 -o yaml
apiVersion: v1
data:
  password: USVGdnFTJCpGJGteNmk=
  superuser: YWRtaW4=
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"password":"USVGdnFTJCpGJGteNmk=","superuser":"YWRtaW4="},"kind":"Secret","metadata":{"annotations":{},"name":"test-secret-3","namespace":"default"},"type":"Opaque"}
  creationTimestamp: "2020-05-10T09:30:18Z"
  name: test-secret-3
  namespace: default
  resourceVersion: "1420756"
  selfLink: /api/v1/namespaces/default/secrets/test-secret-3
  uid: ac6f64b2-aab8-4043-aac9-052a139418ce
type: Opaque

直接在命令行创建secret的格式如下

kubectl create secret generic NAME --from-literal=KEY1=VALUE1 --from-literal=KEY2=VALUE2

不建议用这种方式创建secret,因为用户认证信息会保存在history中,有可能泄露给别的用户

容器中使用secret

和ConfigMap一样,也是可以通过环境变量和volume的方式去使用Secret。环境变量适合命令行需要输入密码等场景,而volume适合用文件去进行远端验证场景

以下所有yaml文件托管在我的Github仓库

环境变量使用secret

通过yaml文件test-secret-env.yaml创建pod

apiVersion: v1
kind: Pod
metadata:
  name: test-secret-env
spec:
  containers:
    - name: test-secret-env
      image: alpine
      imagePullPolicy: IfNotPresent
      command: [ "/bin/sh", "-c", "echo login to as user ${SUPERUSER} with password ${PASSWORD};env" ]
      env:
        - name: SUPERUSER
          valueFrom:
            secretKeyRef:
              name: test-secret-3
              key: superuser
        - name: PASSWORD
          valueFrom:
            secretKeyRef:
              name: test-secret-3
              key: password
      envFrom:
        - secretRef:
            name: test-secret-2
  restartPolicy: Never

这里的格式和ConfigMap差不多,也是单独导入和整体导入两种方法。

创建好pod查看下日志

[root@k8s-master Secret]# kubectl logs test-secret-env
login to as user admin with password Q%FvqS$*F$k^6i
secret1=d23hehuye8rq340p98du312rpur9er3eru038dfh3ry2098iuerewriu32987er98er

secret2=jp3oiur98sd7re=er=23r-sdf13i4%(eiru2p398(eur1p8u+o3iru2o3

KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=test-secret-env
SHLVL=1
HOME=/root
MYNGINX_SERVICE_SERVICE_PORT_HTTP=8080
MYNGINX_SERVICE_SERVICE_HOST=10.97.205.233
MYNGINX_SERVICE_PORT_8080_TCP_ADDR=10.97.205.233
SUPERUSER=admin
MYNGINX_SERVICE_PORT_8080_TCP_PORT=8080
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
MYNGINX_SERVICE_PORT_8080_TCP_PROTO=tcp
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MYNGINX_SERVICE_SERVICE_PORT=8080
MYNGINX_SERVICE_PORT=tcp://10.97.205.233:8080
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
MYNGINX_SERVICE_PORT_8080_TCP=tcp://10.97.205.233:8080
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/
PASSWORD=Q%FvqS$*F$k^6i

有两点要说明下:

  • 这里的secret的内容已经经过了base64解码
  • 通过文件创建的那两个secret的内容似乎多了一个换行符,要去掉

注意,如果是通过文件的方式创建的secret,要去掉每行后面的换行符才可以。

用下面的方法修改下文件,再重新创建secret即可。echo -n能去掉末尾的换行符。

[root@k8s-master Secret]# echo -n "d23hehuye8rq340p98du312rpur9er3eru038dfh3ry2098iuerewriu32987er98er" > secret1
[root@k8s-master Secret]# echo -n "jp3oiur98sd7re=er=23r-sdf13i4%(eiru2p398(eur1p8u+o3iru2o3" > secret2
[root@k8s-master Secret]# kubectl delete secret/test-secret-2
secret "test-secret-2" deleted
[root@k8s-master Secret]# kubectl delete pod test-secret-env
pod "test-secret-env" deleted
[root@k8s-master Secret]# kubectl create secret generic test-secret-2 --from-file=secret1 --from-file=secret2
secret/test-secret-2 created
[root@k8s-master Secret]# kubectl apply -f test-secret-env.yaml
pod/test-secret-env created
[root@k8s-master Secret]# kubectl logs test-secret-env
login to as user admin with password Q%FvqS$*F$k^6i
secret1=d23hehuye8rq340p98du312rpur9er3eru038dfh3ry2098iuerewriu32987er98er
secret2=jp3oiur98sd7re=er=23r-sdf13i4%(eiru2p398(eur1p8u+o3iru2o3
...
...

volume使用secret

通过yaml文件test-secret-volume.yaml创建pod

apiVersion: v1
kind: Pod
metadata:
  name: test-secret-volume
spec:
  containers:
    - name: test-secret-volume
      image: alpine
      command: ["/bin/sh","-c","ping 8.8.8.8"]
      volumeMounts:
        - name: secret-volume
          mountPath: /etc/secret
          readOnly: true
  volumes:
    - name: secret-volume
      secret:
        secretName: test-secret-2
  restartPolicy: Never

这里也是和ConfigMap差不多,只是多了readOnly: true这一条是为了防止容器对文件进行修改。

创建成功以后进入容器,发现两个文件已经被同步

[root@k8s-master Secret]# kubectl exec test-secret-volume -it -- /bin/sh
/ # cd /etc/secret/
/etc/secret # ls -al
total 0
drwxrwxrwt    3 root     root           120 May 10 12:07 .
drwxr-xr-x    1 root     root            20 May 10 12:07 ..
drwxr-xr-x    2 root     root            80 May 10 12:07 ..2020_05_10_12_07_22.158909929
lrwxrwxrwx    1 root     root            31 May 10 12:07 ..data -> ..2020_05_10_12_07_22.158909929
lrwxrwxrwx    1 root     root            14 May 10 12:07 secret1 -> ..data/secret1
lrwxrwxrwx    1 root     root            14 May 10 12:07 secret2 -> ..data/secret2
/etc/secret # cat secret1
d23hehuye8rq340p98du312rpur9er3eru038dfh3ry2098iuerewriu32987er98er/etc/secret #
/etc/secret # cat secret2
jp3oiur98sd7re=er=23r-sdf13i4%(eiru2p398(eur1p8u+o3iru2o3/etc/secret #

因为是走的volume,所以也是支持热更新的。

访问私有仓库

k8s里面还有一种特殊的secret,专门用于docker去私有仓库拉取镜像时使用。

DockerHub和Google GCR是两个使用最多的docker镜像仓库,如果是公共仓库里的镜像,则可以直接拉取,而如果是用户个人的私有仓库,必须要登录以后才能拉取,不然会出现ImagePullBackOff报错。

DockerHub提供了docker login命令去在本地生成一个用于认证的个人信息文件,这样以后在该机器上使用docker去拉取私有镜像就不会失败了。如下

[root@k8s-node1 ~]# 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: victor2019
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

如果是希望k8s也能拉取私有镜像,要将这份认证信息存储到k8s的secret中。

有认证文件情况

如果本地已经使用docker login登陆过,生成了~/.docker/config.json文件,就可以用下面的方法来生成secret

kubectl create secret generic NAME --from-file=.dockerconfigjson=~/.docker/config.json --type=kubernetes.io/dockerconfigjson

这里必须要注意:

  • secret的key一定要是.dockerconfigjson
  • secret的type一定要是kubernetes.io/dockerconfigjson

没有认证文件情况

没有认证文件,直接用命令行来创建secret

kubectl create secret docker-registry NAME --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
  • 如果是DockerHub那么docker-server就是https://index.docker.io/v1/
  • 虽然这里是docker-registry,但是GCR也是可以这样子写的

不建议用这种方式创建secret,因为用户认证信息会保存在history中,有可能泄露给别的用户

私有仓库拉取镜像验证

在master上利用docker login生成认证文件,然后用第一种方法生成secret如下

[root@k8s-master ~]# kubectl get secret dockerhub-secret
NAME               TYPE                             DATA   AGE
dockerhub-secret   kubernetes.io/dockerconfigjson   1      14s

然后去dockerhub我的账号里面创建一个私有仓库叫test,在本地将mynginx:v2镜像重新打标签为victor2019/test:v1,并推送到dockerhub

[root@k8s-node1 .docker]# docker tag mynginx:v2 victor2019/test:v1
[root@k8s-node1 .docker]# docker push victor2019/test:v1
The push refers to repository [docker.io/victor2019/test]
e2955e25f234: Pushed
b3003aac411c: Mounted from library/nginx
216cf33c0a28: Mounted from library/nginx
c2adabaecedb: Mounted from library/nginx
v1: digest: sha256:8d6e63ac92a295ebb867c779f3daf373480f3966d8c1a1ef5c759cfac0715286 size: 1155

之后创建yaml文件test-secret-registry.yaml去拉取这个私有镜像生成pod

apiVersion: v1
kind: Pod
metadata:
 name: test-secret-registry
spec:
 containers:
   - name: test-secret-registry
     image: victor2019/test:v1
 imagePullSecrets:
   - name: test-secret-registry

创建出来

[root@k8s-master Secret]# kubectl apply -f test-secret-registry.yaml
pod/test-secret-registry created
[root@k8s-master Secret]# kubectl get pod test-secret-registry -o wide
NAME                   READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
test-secret-registry   1/1     Running   0          29s   10.244.1.109   k8s-node1   <none>           <none>
[root@k8s-master Secret]# curl 10.244.1.109
this is mynginx v2

私有镜像拉取成功

secret的潜在问题

虽然说k8s提供了secret这种方式去存储私密信息,但是其实还是有很多方面做的并不好

  • etcd安全性 - secret都以未加密形式保存在etcd中,需要用户去额外考虑etcd的加密和访问权限等问题
  • 容器安全性 - 不能有效确保容器在使用secret的过程中不会泄露出去
  • 加密性 - k8s里面的secret都是用base64编码保存,极易可逆还原,不够安全

鉴于这些原因,越来越多的k8s集群开始引入第三方的secret管理方案,其中Hashcorp公司的Vault是比较流行的开源解决方案。

Hashicorp Vault provides secrets management and data protection, with advanced features like dynamic secrets, namespaces, leases, and revocation for secrets data

总结

总结下secret的知识点

  • 4种创建secret的方法:文件/文件夹/yaml/命令行
  • 2种使用secret的方法:环境变量/volume
  • 还有一种特殊的secret专门用于私有仓库的镜像拉取,可以通过认证文件或者命令行来创建
  • 命令行的方式都不推荐,因为可以在history中查看
  • k8s自带的secret还有很多不足,可以考虑第三方的secret管理方案,例如Hashcorp Vault
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值