kubeadmクラスターでetcdでのSecretの暗号化を有効にする

kubeadmで作成したクラスターでetcdでのSecretの暗号化を有効にする方法のメモ。

デフォルト状態

デフォルト状態ではSecretは暗号化されていないので、etcdctlを使ってetcdに直接アクセスすれば見ることができる。

Secretを作成する。

root@cks-master:~# k create secret generic secret1 --from-literal=user=admin
secret/secret1 created
root@cks-master:~# k get secret secret1 -o yaml
apiVersion: v1
data:
  user: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: "2020-12-28T16:29:53Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:user: {}
      f:type: {}
    manager: kubectl-create
    operation: Update
    time: "2020-12-28T16:29:53Z"
  name: secret1
  namespace: default
  resourceVersion: "2187"
  selfLink: /api/v1/namespaces/default/secrets/secret1
  uid: 5f47402f-e1eb-44f8-a672-c4a56806b626
type: Opaque

etcdctlをインストールする。

apt-get install -y etcd-client

kube-apiserverがetcdに接続している情報を確認する。

root@cks-master:~# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep etcd
    - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
    - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
    - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
    - --etcd-servers=https://127.0.0.1:2379

この情報を使ってetcdに接続する。

ETCDCTL_API=3 etcdctl \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
    --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
    --endpoints=https://127.0.0.1:2379 \
    endpoint health
root@cks-master:~# ETCDCTL_API=3 etcdctl \
>     --cacert=/etc/kubernetes/pki/etcd/ca.crt \
>     --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
>     --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
>     --endpoints=https://127.0.0.1:2379 \
>     endpoint health
https://127.0.0.1:2379 is healthy: successfully committed proposal: took = 1.523763ms

Secretを読み取る。

ETCDCTL_API=3 etcdctl \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
    --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
    --endpoints=https://127.0.0.1:2379 \
    get /registry/secrets/default/secret1
root@cks-master:/var/log/containers# ETCDCTL_API=3 etcdctl \
>     --cacert=/etc/kubernetes/pki/etcd/ca.crt \
>     --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
>     --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
>     --endpoints=https://127.0.0.1:2379 \
>     get /registry/secrets/default/secret1
/registry/secrets/default/secret1
k8s


v1Secret�
�
secret1default"*$5f47402f-e1eb-44f8-a672-c4a56806b6262����z�_
kubectl-createUpdatev����FieldsV1:-
+{"f:data":{".":{},"f:user":{}},"f:type":{}}
useradminOpaque"

暗号化

設定ファイルを置くディレクトリを作成する。

mkdir -p /etc/kubernetes/etcd
cd /etc/kubernetes/etcd

暗号化に使うキーを作成する。

# KEY=$(echo -n passwordpassword | base64)
KEY=$(head -c 32 /dev/urandom | base64)

EncryptionConfigurationのyamlを作成する。

cat <<EOF > /etc/kubernetes/etcd/ec.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key1
          secret: ${KEY}
    - identity: {}
EOF

kube-apiserverの起動引数でこのyamlを渡す。ファイルをマウントしてあげる必要もあることに注意。

vi /etc/kubernetes/manifests/kube-apiserver.yaml
(省略)
  - command:
    - kube-apiserver
    - --encryption-provider-config=/etc/kubernetes/etcd/ec.yaml
(省略)
    volumeMounts:
    - mountPath: /etc/kubernetes/etcd
      name: etcd
      readOnly: true
(省略)
  volumes:
  - hostPath:
      path: /etc/kubernetes/etcd
      type: DirectoryOrCreate
    name: etcd

既存のSecretは暗号化されない。暗号化時は一番上のプロバイダーが使われ、復号時は上から順番に使われる。一番下にidentity: {}の設定があるので、暗号化されていないものも読み取りが可能。

root@cks-master:/var/log/containers# ETCDCTL_API=3 etcdctl \
>     --cacert=/etc/kubernetes/pki/etcd/ca.crt \
>     --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
>     --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
>     --endpoints=https://127.0.0.1:2379 \
>     get /registry/secrets/default/secret1
/registry/secrets/default/secret1
k8s


v1Secret�
�
secret1default"*$5f47402f-e1eb-44f8-a672-c4a56806b6262����z�_
kubectl-createUpdatev����FieldsV1:-
+{"f:data":{".":{},"f:user":{}},"f:type":{}}
useradminOpaque"

新しくSecretを作成する。

root@cks-master:/var/log/containers# k create secret generic very-secure --from-literal=cc=1234
secret/very-secure created
root@cks-master:/var/log/containers# k get secret very-secure -o yaml
apiVersion: v1
data:
  cc: MTIzNA==
kind: Secret
metadata:
  creationTimestamp: "2020-12-28T17:49:20Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:cc: {}
      f:type: {}
    manager: kubectl-create
    operation: Update
    time: "2020-12-28T17:49:20Z"
  name: very-secure
  namespace: default
  resourceVersion: "12256"
  selfLink: /api/v1/namespaces/default/secrets/very-secure
  uid: 1883c04d-0e66-4179-816d-b9401174c125
type: Opaque

etcdctlで直接見てみる。

ETCDCTL_API=3 etcdctl \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
    --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
    --endpoints=https://127.0.0.1:2379 \
    get /registry/secrets/default/very-secure
root@cks-master:/var/log/containers# ETCDCTL_API=3 etcdctl \
>     --cacert=/etc/kubernetes/pki/etcd/ca.crt \
>     --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
>     --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
>     --endpoints=https://127.0.0.1:2379 \
>     get /registry/secrets/default/very-secure
/registry/secrets/default/very-secure
k8s:enc:aescbc:v1:key1:�1� �l�Tk.8�谶
                                     ���[A�d��h�U0m9���J�(v2��Э|Ь�����u��󌶮���"�pe���oؖA&�7���^'�Us�
                                                                                                  P��%͖gOO/jJ�ʰif'��!��)K���M��
j�ܿN;e��j������ �w��}��}�r�E?���~s� ��R�N��p�L��,
                                                 �ƙZ���v�+B�ק��涻�(�Y�W*ݭ?YN/&�K{��`��?�Ktz�ď�����I�

既存のSecretの暗号化

全部読み取って、置き換える。一番下のidentity: {}で暗号化されていないものも読み取られて、保存されるときには一番上の設定で暗号化される。

k get secret -A -o yaml | k replace -f -
root@cks-master:/etc/kubernetes/manifests# k get secret -A -o yaml | k replace -f -
secret/default-token-grb2j replaced
secret/secret1 replaced
secret/secret2 replaced
secret/very-secure replaced
secret/default-token-hbjtd replaced
secret/default-token-7n9bx replaced
secret/attachdetach-controller-token-vndrq replaced
secret/bootstrap-signer-token-7pfcp replaced
secret/bootstrap-token-jsf0sg replaced
secret/bootstrap-token-xmh1o0 replaced
secret/certificate-controller-token-8vfv8 replaced
secret/clusterrole-aggregation-controller-token-h2tnb replaced
secret/coredns-token-khz9t replaced
secret/cronjob-controller-token-ntwtd replaced
secret/daemon-set-controller-token-vd4dj replaced
secret/default-token-8glxq replaced
secret/deployment-controller-token-hxbfp replaced
secret/disruption-controller-token-jhq8d replaced
secret/endpoint-controller-token-69pxz replaced
secret/endpointslice-controller-token-xp5gr replaced
secret/endpointslicemirroring-controller-token-9dnsj replaced
secret/expand-controller-token-5vwd2 replaced
secret/generic-garbage-collector-token-qgt5j replaced
secret/horizontal-pod-autoscaler-token-ghvpj replaced
secret/job-controller-token-sb5dp replaced
secret/kube-proxy-token-nzcmr replaced
secret/namespace-controller-token-cl54l replaced
secret/node-controller-token-vmhjm replaced
secret/persistent-volume-binder-token-6fv9z replaced
secret/pod-garbage-collector-token-25dbr replaced
secret/pv-protection-controller-token-n9p4z replaced
secret/pvc-protection-controller-token-qw9d7 replaced
secret/replicaset-controller-token-52drf replaced
secret/replication-controller-token-5fzp8 replaced
secret/resourcequota-controller-token-f2vfc replaced
secret/service-account-controller-token-dqrsn replaced
secret/service-controller-token-j8w9n replaced
secret/statefulset-controller-token-5twbl replaced
secret/token-cleaner-token-2q8jc replaced
secret/ttl-controller-token-4w56d replaced
secret/weave-net-token-vk5lm replaced

全部暗号化できたら、identity: {}は消してもOK。