Secret

发布时间: 更新时间: 总字数:2417 阅读时间:5m 作者: IP上海 分享 网址

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 示例

  • 创建与查看 secret
$ 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
  • Secret 是可以解码的
$ 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
  • 现在使用文件创建 Secrets:

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/config.json 方式

  • 方法一:使用 docker login 生成 ~/.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="
         }
    }
}
  • 创建secret
$ 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

  • 通过 ssh key 注入
$ 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 示例

  • pod-secret.yaml
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

参考

  1. https://kubernetes.io/zh/docs/concepts/configuration/secret/
  2. https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
  3. https://engineering.bitnami.com/articles/sealed-secrets.html
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数