目的
- ETCDの暗複合化を理解する
手段
killercodaで手を動かす
環境
killercoda
ETCDの暗複合化 とは
ETCDはKubernetesの脳のようなもので、クラスターの状態に関する全ての情報(Secret、ConfigMap、Podの定義など)を保存しています。これらの情報が万が一漏洩すると大変なことになるため、ETCDに保存されるデータを暗号化しておくことがセキュリティ上非常に重要です。
つまり、ETCDに保存する重要なAPIリソース(Secrets等)は暗号化してセキュリティ強度を高めようということですね。k8s 公式ドキュメントで 暗号化鍵をクラスター内に保存することは大してセキュリティ向上に寄与しない と明言されています。暗号化鍵は 外部のKMS に保管しましょう。
設定に必要な手順は以下の通りです
- 暗号化に利用するランダム文字列生成
- 暗号化に利用する鍵設定を作成
- APIサーバはETCD 暗号化設定を利用するように設定及び設定ファイルマウント
1つずつ手順を確認します。
- 暗号化に利用するランダム文字列生成
controlplane:~$ head -c 32 /dev/urandom | base64
K6tBMZgu+Isixi4wXOB7LIhk/v6FVHc7lqAVcH5co+I=
# ランダム文字列生成
controlplane:~$ echo -n this-is-very-sec | base64
dGhpcy1pcy12ZXJ5LXNlYw==
# 特定文字列を基に文字列生成
- 暗号化に利用する鍵設定を作成
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
# 暗号化対象のリソースを指定
- secrets
providers:
- aesgcm:
keys:
- name: key1
secret: dGhpcy1pcy12ZXJ5LXNlYw==
# 暗号化鍵を指定。複数の鍵や暗号プロバイダーを指定可能
- identity: {}
# プレーンテキスト読み取り用の設定。この行削除で平文リソース読み取り不可
kube-apiサーバに対して etcd 暗号化設定を適用する前に、既存 secret を確認します。
controlplane:/etc/kubernetes/etcd$ k get secrets -A
NAMESPACE NAME TYPE DATA AGE
one s1 Opaque 1 5m13s
one s2 Opaque 1 5m13s
controlplane:/etc/kubernetes/etcd$ k get secrets -n one s1 -o yaml
apiVersion: v1
data:
data: c2VjcmV0
kind: Secret
metadata:
creationTimestamp: "2025-10-16T07:51:01Z"
name: s1
namespace: one
resourceVersion: "2379"
uid: df9247cd-6143-4ee1-adfb-560dc239fe0e
type: Opaque
controlplane:/etc/kubernetes/etcd$ echo c2VjcmV0 | base64 --decode
secret
# secret は平文で保存されていることを確認
念の為、etcdctl コマンドでも確認します。
controlplane:/etc/kubernetes/etcd$ ETCDCTL_API=3 etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get /registry/secrets/one/s1 | hexdump -C
00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret|
00000010 73 2f 6f 6e 65 2f 73 31 0a 6b 38 73 00 0a 0c 0a |s/one/s1.k8s....|
00000020 02 76 31 12 06 53 65 63 72 65 74 12 c0 01 0a a5 |.v1..Secret.....|
00000030 01 0a 02 73 31 12 00 1a 03 6f 6e 65 22 00 2a 24 |...s1....one".*$|
00000040 64 66 39 32 34 37 63 64 2d 36 31 34 33 2d 34 65 |df9247cd-6143-4e|
00000050 65 31 2d 61 64 66 62 2d 35 36 30 64 63 32 33 39 |e1-adfb-560dc239|
00000060 66 65 30 65 32 00 38 00 42 08 08 e5 c7 c2 c7 06 |fe0e2.8.B.......|
00000070 10 00 8a 01 61 0a 0e 6b 75 62 65 63 74 6c 2d 63 |....a..kubectl-c|
00000080 72 65 61 74 65 12 06 55 70 64 61 74 65 1a 02 76 |reate..Update..v|
00000090 31 22 08 08 e5 c7 c2 c7 06 10 00 32 08 46 69 65 |1".........2.Fie|
000000a0 6c 64 73 56 31 3a 2d 0a 2b 7b 22 66 3a 64 61 74 |ldsV1:-.+{"f:dat|
000000b0 61 22 3a 7b 22 2e 22 3a 7b 7d 2c 22 66 3a 64 61 |a":{".":{},"f:da|
000000c0 74 61 22 3a 7b 7d 7d 2c 22 66 3a 74 79 70 65 22 |ta":{}},"f:type"|
000000d0 3a 7b 7d 7d 42 00 12 0e 0a 04 64 61 74 61 12 06 |:{}}B.....data..|
000000e0 73 65 63 72 65 74 1a 06 4f 70 61 71 75 65 1a 00 |secret..Opaque..|
000000f0 22 00 0a |"..|
000000f3
# 先頭に `k8s:enc` 等の文字列はありません。暗号化されていないことを表しています
- APIサーバはETCD 暗号化設定を利用するように設定及び設定ファイルマウント
- --encryption-provider-config=/etc/kubernetes/etcd/ec.yaml
- mountPath: /etc/kubernetes/etcd
name: enc-config
readOnly: true
- hostPath:
path: /etc/kubernetes/etcd
type: DirectoryOrCreate
name: enc-config
# 関連設定のみ抜粋。設定ファイルを作成して特定パスにマウントする形です
watch crictl ps コマンドで kube-api server の再起動を確認します。
controlplane:/etc/kubernetes/manifests$ ps -aux |grep api
root 14353 3.0 13.1 1526252 288608 ? Ssl 08:08 0:04 kube-apiserver --encryption-provider-config=/etc/kubernetes/etcd/ec.yaml --advertise-address=172.30.1.2 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/etc/kubernetes/pki/ca.crt --enable-admission-
# encryption-provider-config=/etc/kubernetes/etcd/ec.yaml 設定が渡されています
暗号化設定前に作成された API リソースは平文で ETCD に保存されています。暗号化設定後に対象APIリソースを暗号化する場合は下記コマンドを実行します。
controlplane:/etc/kubernetes/manifests$ k get -n one secrets -o json | kubectl replace -f -
secret/s1 replaced
secret/s2 replaced
ETCD の暗号化設定フラグを無効化し、暗号化された secret を読み取れるか確認します。
controlplane:/etc/kubernetes/manifests$ cat kube-apiserver.yaml |grep "#"
#- --encryption-provider-config=/etc/kubernetes/etcd/ec.yaml
# - mountPath: /etc/kubernetes/etcd
# name: enc-config
# readOnly: true
# - hostPath:
# path: /etc/kubernetes/etcd
# type: DirectoryOrCreate
# name: enc-config
controlplane:/etc/kubernetes/manifests$ k get -n one secrets s1 -o yaml
Error from server (InternalError): Internal error occurred: identity transformer tried to read encrypted data
# 暗号化された secret の読み取りに失敗しています。
次に平文の secret を確認します。
controlplane:/etc/kubernetes/manifests$ k get -n two secrets secret1 -o yaml
apiVersion: v1
data:
data: c2VjcmV0
kind: Secret
metadata:
creationTimestamp: "2025-10-16T07:51:01Z"
name: secret1
namespace: two
resourceVersion: "2381"
uid: f1b19148-2947-40b6-bead-f547b4e7be90
type: Opaque
controlplane:/etc/kubernetes/manifests$ echo c2VjcmV0 |base64 --decode
secret
# 平文のsecretは読み取り可能です。
etcd 暗号化設定を再有効化して暗号化した secret を確認します。
controlplane:/etc/kubernetes/manifests$ k get -n one secrets s1 -o yaml
apiVersion: v1
data:
data: c2VjcmV0
kind: Secret
metadata:
creationTimestamp: "2025-10-16T07:51:01Z"
name: s1
namespace: one
resourceVersion: "4156"
uid: df9247cd-6143-4ee1-adfb-560dc239fe0e
type: Opaque
# 暗号化された secret を読み取り可能です
secretcontrolplane:/etc/kubernetes/manifests$ k get -n two secrets secret1 -o yaml
apiVersion: v1
data:
data: c2VjcmV0
kind: Secret
metadata:
creationTimestamp: "2025-10-16T07:51:01Z"
name: secret1
namespace: two
resourceVersion: "2381"
uid: f1b19148-2947-40b6-bead-f547b4e7be90
type: Opaque
# 平文の secret も読み取り可能です
etcd 暗号化設定後は、平文のAPIリソースに対する読み取りは identity: {} 行で制御します。
暗号化設定ファイルから削除して kube-apiserver を再起動します。
controlplane:/etc/kubernetes/etcd$ cat ec.yaml |grep "#"
#- identity: {}
# identity 行をコメントアウトします
controlplane:/etc/kubernetes/etcd$ k get -n one secrets s1 -o yaml
apiVersion: v1
data:
data: c2VjcmV0
kind: Secret
metadata:
creationTimestamp: "2025-10-16T07:51:01Z"
name: s1
namespace: one
resourceVersion: "4156"
uid: df9247cd-6143-4ee1-adfb-560dc239fe0e
type: Opaque
controlplane:/etc/kubernetes/etcd$ k get -n two secrets secret1 -o yaml
Error from server (InternalError): Internal error occurred: no matching prefix found
# 平文の API リソース読み取りに失敗しています。
おさらい
- etcd 暗号化設定前に作成した API リソースは etcd暗号化設定後に再作成(暗号化)する必要がある
- etcd暗号化設定後の平文APIリソース読み取りは etcd 暗号化設定ファイルの identity: {} で制御する
- 暗号化鍵は外部 KMS 等できちっと管理する
あとがき
Kubernetes は奥が深い...