在K8S中,如何安全管理Secrets?

爲何要加密?

在Kubernetes中,Secret是用來幫我們存儲敏感信息的,比如密碼、證書等,但是在默認的情況下,Secret只是做了簡單的base64編碼,任何人都可以非常容易的對其進行解密獲取到原始數據。

比如通過以下方法生成一個secret對象:

echo -n "coolops" | kubectl create secret generic mysecret --dry-run --from-file=secret=/dev/stdin -o yaml > secret.yaml
$ cat secret.yaml 
apiVersion: v1
data:
  secret: Y29vbG9wcw==
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret

其他人只要拿到secret的值,就可以對其進行解密獲取到真實數據,如下:

echo "Y29vbG9wcw==" | base64 -d
coolops

這樣就非常的不安全,有點"掩耳盜鈴"的意思。

在Kubernetes集羣中,Etcd是集羣數據庫,存儲着集羣所以的資源數據,其中也包括Secrets,所以拿下了這個數據庫就等於拿下了整個集羣,所以在生產環境中對其進行加密是非常有必要的。

如何進行加密?

靜態加密

kubernetes 1.13版本之後,提供靜態加密方式,其主要是通過kube-apiserver來控制Secrets的加解密,而在Etcd中存儲的是加密後的信息,所以攻擊者拿下了etcd,也無法輕易的拿到Secrets保存的敏感數據。

!! 當前集羣是使用kubeadm安裝,版本1.18.9

(1)創建加密配置文件,保存到master節點/etc/kubernetes/pki/static-secret-encryption.yaml中

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: mysecret
          secret: DJqYaMMpY2DNlHz+HYrFYOUSh5SXKWiVOwLf6nQX9ss=
    - identity: {}

其中secret是加密密鑰,使用如下命令生成:

$ head -c 32 /dev/urandom | base64

(2)修改kube-apiserver啓動參數,位置/etc/kubernetes/manifests/kube-apiserver.yaml

......
spec:
  containers:
  - command:
    - kube-apiserver
    - --encryption-provider-config=/etc/kubernetes/pki/static-secret-encryption.yaml
......    

!! 注意:kube-apiserver的加密參數,在1.14之後是--encryption-provider-config

(3)重啓kube-apiserver

(4)驗證加密

首先創建一個secret資源,如下:

$ kubectl create secret generic secret1 -n default --from-literal=mykey=mydata

然後查看etcd中的數據

$ ETCDCTL_API=3 etcdctl --endpoints=https://[127.0.0.1]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key get /registry/secrets/default/secret1  | hexdump -C
00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
00000010  73 2f 64 65 66 61 75 6c  74 2f 73 65 63 72 65 74  |s/default/secret|
00000020  31 0a 6b 38 73 3a 65 6e  63 3a 61 65 73 63 62 63  |1.k8s:enc:aescbc|
00000030  3a 76 31 3a 6d 79 73 65  63 72 65 74 3a b6 90 7d  |:v1:mysecret:..}|
00000040  b3 40 9b 4d 63 eb 71 cf  ca 16 52 a0 91 82 c3 69  |[email protected]|
00000050  2b 6f e5 1c ae 88 58 0d  4f 08 c9 29 57 69 f5 e6  |+o....X.O..)Wi..|
00000060  e2 7c 42 79 bb 84 22 3a  90 54 5e d4 ac f6 a6 a6  |.|By..":.T^.....|
00000070  47 e2 b2 67 29 d8 c4 c6  61 9e 84 62 9d 3c 7c b0  |G..g)...a..b.<|.|
00000080  6c 5f 5e f3 da 08 34 17  ef a3 a7 9c 76 31 02 98  |l_^...4.....v1..|
00000090  54 5c 21 05 af 8d a8 dc  39 04 4d 84 bf a8 d1 f6  |T\!.....9.M.....|
000000a0  58 f4 90 30 22 46 14 a5  e6 19 3a 51 48 86 99 a7  |X..0"
F....:QH...|
000000b0  ed f1 5f 8e 4a 1c 30 cb  5f ec ba 3d e2 0a 1d 93  |.._.J.0._..=....|
000000c0  7c 57 68 6b d2 01 51 49  fd 81 56 72 6d ca 98 e6  ||Whk..QI..Vrm...|
000000d0  99 59 84 15 bc 5d 7d f7  95 75 b2 cb 4f ff 8d d1  |.Y...]}..u..O...|
000000e0  ae 29 0d 27 df fa 59 b4  e2 37 2c 33 83 9e e4 73  |.).'..Y..7,3...s|
000000f0  55 ce 89 cc c0 5f 3d e4  df 90 8d 70 91 f9 81 b1  |U...._=....p....|
00000100  e7 0c ee 71 cf 81 22 6f  6c 45 74 51 0c f7 5f 4d  |...q.."olEtQ.._M|
00000110  1f 9a be 51 05 cd fd b2  74 0b 29 30 e2 24 ea 57  |...Q....t.)0.$.W|
00000120  0e 8a cb 1f 55 7a 2e 4c  0e 1a 4e 09 c6 0a        |....Uz.L..N...|
0000012e

注意在數據頭部出現k8s:enc:aescbc:v1:,說明數據已經被正確加密,使用的是aescbc算法,使用的密鑰爲mysecret

接下來查看讀取的時候能否正常被解密,如下:

$ kubectl get secrets secret1 -o yaml
apiVersion: v1
data:
  mykey: bXlkYXRh
kind: Secret
metadata:
  creationTimestamp: "2021-01-22T02:27:48Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:mykey: {}
      f:type: {}
    manager: kubectl
    operation: Update
    time: "2021-01-22T02:27:48Z"
  name: secret1
  namespace: default
  resourceVersion: "26907503"
  selfLink: /api/v1/namespaces/default/secrets/secret1
  uid: 9020c914-3785-404f-a7b2-0743ff49c19d
type: Opaque

echo "bXlkYXRh" | base64 -d
mydata

可以發現能否正常解密。

不知道你有沒有發現,只有存在etcd裏的數據被加密了,我們在集羣使用kubectl get secrets secret1 -o yaml獲取到的仍然只是簡單的進行了base64轉碼,所以一旦我們的節點被攻破,secrets也就暴露在外面了。

密鑰管理服務KMS plugin

KMS 加密驅動使用封套加密模型來加密 etcd 中的數據。數據使用數據加密祕鑰(DEK)加密;每次加密都生成一個新的 DEK。這些 DEK 經一個祕鑰加密祕鑰(KEK)加密後在一個遠端的 KMS 中存儲和管理。KMS 驅動使用 gRPC 與一個特定的 KMS 插件通信。這個 KMS 插件作爲一個 gRPC 服務器被部署在 Kubernetes 主服務器的同一個主機上,負責與遠端 KMS 的通信。

現在基本雲廠商都提供KMS服務。這裏以阿里云爲例。

(1)開通密鑰管理服務

image.png

(2)創建密鑰

image.png

(3)然後部署kms-plugin

apiVersion: apps/v1
kind: Deployment 
metadata:
  name: ack-kms-plugin
  namespace: kube-system
spec:
  selector:
   matchLabels:
     name: ack-kms-plugin
  template:
   metadata:
     labels:
       name: ack-kms-plugin
   spec:
     affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - preference: {}
              weight: 100
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: node
                    operator: In
                    values:
                      - master
     restartPolicy: Always
     tolerations:
     - key: node-role.kubernetes.io/master
       effect: NoSchedule
     volumes:
     - name: kmssocket
       hostPath:
         path: /var/run/kmsplugin
         type: DirectoryOrCreate
     containers:
     - name: ack-kms-plugin
       image: registry.{{ .Region }}.aliyuncs.com/acs/ack-kms-plugin:v1.0.2
       imagePullPolicy: Always
       command:
       - ack-kms-plugin
       - --gloglevel=5
       - --key-id={{ .KeyId }}
       - --path-to-unix-socket=/var/run/kmsplugin/grpc.sock
       livenessProbe:
         exec:
           command:
           - ack-kms-plugin
           - health
           - --path-to-unix-socket=/var/run/kmsplugin/grpc.sock
         initialDelaySeconds: 30
         failureThreshold: 3
         timeoutSeconds: 5
         periodSeconds: 300
       env:
         - name: ACCESS_KEY_ID    #not required if you want plugin help to pull the sts credentials
           value: {{ .AK }}
         - name: ACCESS_KEY_SECRET   #not required if you want plugin help to pull the sts credentials
           value: {{ .AK_Secret }}
         - name: CREDENTIAL_INTERVAL   #not required if you want plugin help to pull the sts credentials
           value: {{ .Credential_Interval }}
       volumeMounts:
       - name: kmssocket
         mountPath: /var/run/kmsplugin
         readOnly: false

其中需要更改的地方:

  • {{ .Region }}:阿里巴巴雲區域 ID, 如果您的羣集部署在 ECS 上, 您可以通過curl http://100.100.100.200/latest/meta-data/region-id
  • {{ .KeyId }}:Kms 服務列表中用於祕密加密的阿里雲 Kms 密鑰 ID
  • {{ .AK }}:授權的角色ID
  • {{ .AK_Secret }}:授權的角色密鑰
  • {{ .Credential_Interval }}:憑據輪詢間隔

(4)創建kms插件配置文件/etc/kubernetes/kmsplugin/encryptionconfig.yaml

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - kms:
        name: grpc-kms-provider
        endpoint: unix:///var/run/kmsplugin/grpc.sock
        cachesize: 1000
        timeout: 3s
    - identity: {}

(5)修改kube-apiserver配置清單文件/etc/kubernetes/manifests/kube-apiserver.yaml

在啓動參數里加入:

......
spec:
  containers:
  - command:
    - kube-apiserver
    - --encryption-provider-config=/etc/kubernetes/kmsplugin/encryptionconfig.yaml
......

然後再配置掛載,如下:

...
  volumeMounts:
  - name: kms-sock
    mountPath: /var/run/kmsplugin
  - name: kms-config
    mountPath: /etc/kubernetes/kmsplugin
...
  volumes:
  - name: kms-sock
    hostPath:
      path: /var/run/kmsplugin
  - name: kms-config
    hostPath:
      path: /etc/kubernetes/kmsplugin

(6)重啓kube-apiserver

(7)驗證

現在,羣集應使用信封加密方案,使用阿里雲 KMS 的給定密鑰加密密鑰 (KEK) 對 中的祕密進行加密

1. 創建新機密

$ kubectl create secret generic secret1 -n default --from-literal=mykey=mydata

2. 使用 etcdtl,讀取主節點中的 etcd中的機密

ps:[.local-ip] 應替換爲主節點 IP 之一。

$ ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.pem --cert=/etc/kubernetes/pki/etcd/etcd-client.pem --key=/etc/kubernetes/pki/etcd/etcd-client-key.pem --endpoints=https://{{.local-ip}}:2379 get /registry/secrets/default/secret1

3. 驗證存儲的機密是否前綴,指示我們的 kms 提供商已加密生成的數據。k8s:enc:kms:v1:grpc-kms-provider

4. 驗證密鑰是否已正確解密:

$ kubectl get secrets secret1 -o yaml

使用第三方插件

還可以使用其他插件,比如sealed-secrets和vault。

參考文件:

  • https://kubernetes.io/zh/docs/tasks/administer-cluster/encrypt-data/
  • https://kubernetes.io/zh/docs/tasks/administer-cluster/kms-provider/
  • https://github.com/AliyunContainerService/ack-kms-plugin
  • https://github.com/hashicorp/vault
  • https://github.com/bitnami-labs/sealed-secrets

本文分享自微信公衆號 - YP小站(ypxiaozhan)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章