Kubernetesにおけるetcd内のデータの暗号化

問題点:etcdには平文でデータが格納されている。

k8sの各種マニフェストははetcdに平文で保存されている。 secretであっても平文で保存されている。

twoという文字列を隠蔽したくてone-plus-oneというsecretを作成する。

$ k create secret generic one-plus-one --from-literal=ans=two

etcdの中を見てみと、twoという文字が確認できる。

$ etcdctl get /registry/secrets/default/one-plus-one | hexdump -C | grep two
000000f0  00 12 0a 0a 03 61 6e 73  12 03 74 77 6f 1a 06 4f  |.....ans..two..O|

解決策

Kubernetesにおいてetcdと通信を行うアプリはkube-api-serverのみである。
そのため、kube-api-serverでデータの暗号化と復号化を行う。つまり、データをetcdに書き込む際には暗号化して書き込みを行い、データを読み出す場合にはetcdから読み出したデータを復号化して使う。

方法

EncryptionConfigurationを用意して、それをkube-api-serverに--encryption-provider-configオプションを使って読み込ませる。

EncryptionConfiguration

サンプル (参照元: Encrypting Confidential Data at Rest | Kubernetes)

#
#
# CAUTION: this is an example configuration.
#          Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example # a custom resource API
    providers:
      - identity: {}
      - aesgcm:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
            - name: key2
              secret: dGhpcyBpcyBwYXNzd29yZA==
      - aescbc:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
            - name: key2
              secret: dGhpcyBpcyBwYXNzd29yZA==
  - resources:
      - events
    providers:
      - identity: {}
  - resources:
      - '*.apps'
    providers:
      - aescbc:
          keys:
          - name: key2
            secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
  • .resources.resourcesで暗号化、復号化を行う対象を選択する
  • .resources.providersで暗号化、復号化を行うproviderを選択する

.resources.providersに指定するproviderの順序には以下のような意味があるので、注意が必要。

  • 暗号化する際: .resources.providersに指定されている最初のproviderが利用される。
  • 復号化する際: .resources.providersに指定されているproviderの中で適したものが自動で選択され使用される。

各種providerの意味は下記に記載されている。

kubernetes.io

試してみる

まずはEncryptionConfigurationを作成する

#
#
# CAUTION: this is an example configuration.
#          Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key1
          secret: aG9nZWhvZ2Vob2dlaG9nZQ==
    - identity: {}

aG9nZWhvZ2Vob2dlaG9nZQ==echo -n hogehogehogehoge | base64で適当に作ったものになっている。

このファイルは/etc/kubernetes/etcd/ec.yamlに保存した。(別にファイルの場所はkube-api-serverが渡せるならばどこでもよい。)

kube-api-serverに先ほど作成したEncryptionConfigurationを読み込ませる。

以下のような感じでkube-api-serverのマニフェストを修正。

@@ -13,6 +13,7 @@
   containers:
   - command:
     - kube-apiserver
+    - --encryption-provider-config=/etc/kubernetes/etcd/ec.yaml
     - --allow-privileged=true
     - --authorization-mode=Node,RBAC
@@ -92,6 +93,9 @@
     - mountPath: /usr/share/ca-certificates
       name: usr-share-ca-certificates
       readOnly: true
+    - mountPath: /etc/kubernetes/etcd
+      name: etcd
+      readOnly: true
   hostNetwork: true
   priority: 2000001000
   priorityClassName: system-node-critical
@@ -119,4 +123,8 @@
       path: /usr/share/ca-certificates
       type: DirectoryOrCreate
     name: usr-share-ca-certificates
+  - hostPath:
+      path: /etc/kubernetes/etcd
+      type: DirectoryOrCreate
+    name: etcd
 status: {}

one-minus-oneというsecretを新たに作成してみる

k create secret generic one-minus-one --from-literal=ans=zero

etcdctlでちゃんと暗号化されているか確認

$etcdctl get /registry/secrets/default/one-minus-one | hexdump -C | grep  zero

大丈夫そう

元々あったone-plus-oneというsecretも暗号化してみる

この時点では元々あったsecretは暗号化されてない。

$ etcdctl get /registry/secrets/default/one-plus-one | hexdump -C | grep two
000000f0  00 12 0a 0a 03 61 6e 73  12 03 74 77 6f 1a 06 4f  |.....ans..two..O|

元々あったsecretを全て再作成する。

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

改めてetcdctlでone-plus-oneを見てみる

$ etcdctl get /registry/secrets/default/one-plus-one | hexdump -C | grep two

何も出ない。つまり、暗号化された。

参照

kubernetes.io