今日覚えること
etcd
Kubernetesの全てのクラスター情報の保存場所として利用されている、一貫性、高可用性を持ったキーバリューストアのこと
EncryptionConfiguration
APIサーバーが永続的なデータを保存するときに、etcd に保存されるデータを暗号化するための設定ファイル
Secret ETCD Encryption
Enable ETCD Encryption
- Create an EncryptionConfiguration file at
/etc/kubernetes/etcd/ec.yaml
and make ETCD use it.- One provider should be of type
aesgcm
with passwordthis-is-very-sec
. All new secrets should be encrypted using this one.- One provider should be the
identity
one to still be able to read existing unencrypted secrets.
いろいろ書いていますが、etcdで暗号化できるようにせよ、と言っています。
etcdとは、 一貫性、高可用性を持ったキーバリューストアで、Kubernetesの全てのクラスター情報の保存場所として利用されています。
各ワーカーノードに対して、etcdに記載されている状態に一致するようにkubeletへ指令が出され、その指令をもとに各ワーカーノードにリソースが作成されます。
(このあたりかなりややこしいので細かく見たら多分間違ってます。なんとなくそういう感じなんだな、ぐらいに思っていてください。)
そして、kubernetesクラスターに保管しているデータを暗号化するにはEncryptionConfigurationというリソースを利用します。
今回は、EncryptionConfigurationリソースを作成して、etcdで保存するデータを暗号化していきます。
EncryptionConfigurationについては以下が参考になります。
ポイントを列挙します
- 各 resources 配列アイテムが独立した固有の設定である
- resources.resources フィールドは、暗号化の対象となるkubernetesリソースを指定している(Secrets、ConfigMapsなど)
- providers 配列は、指定されたAPI(リソース)に対して使用可能な暗号化プロバイダーの順序付きリストである
- resources[*].providers.<provider name>.key 配下の配列に、使用する鍵の名前&base64でエンコードした鍵を記載する
こんなところでしょうか。
続いて、マニフェストファイルのサンプルです。
---
#
# 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:
# This configuration does not provide data confidentiality. The first
# configured provider is specifying the "identity" mechanism, which
# stores resources as plain text.
#
- identity: {} # plain text, in other words NO encryption
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- resources:
- events
providers:
- identity: {} # do not encrypt Events even though *.* is specified below
- resources:
- '*.apps' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key2
secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
- resources:
- '*.*' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
EncryptionConfigurationについてなんとなく理解したところで、構築作業を進めていきましょう。
まず、EncryptionConfigurationのマニフェストファイルを格納するディレクトリを作成します。
controlplane $ mkdir -p /etc/kubernetes/etcd
次に、指定されたパスワードをbase64で暗号化します。
このとき、改行コードを含めず暗号化できるように -n オプションをつけるようにします。
このオプションを忘れると出力結果も変わってしまうので要注意です。
controlplane $ echo -n this-is-very-sec | base64
dGhpcy1pcy12ZXJ5LXNlYw==
#改行コード無しの場合
controlplane $ echo this-is-very-sec | base64
dGhpcy1pcy12ZXJ5LXNlYwo=
続いて、EncryptionConfigurationのマニフェストファイルを/etc/kubernetes/etcd/ec.yamlに作成します。
先ほどのサンプルファイルからざっくりコピーします。
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example # a custom resource API
providers:
# This configuration does not provide data confidentiality. The first
# configured provider is specifying the "identity" mechanism, which
# stores resources as plain text.
#
- identity: {} # plain text, in other words NO encryption
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
ここから修正を加えていきます。
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aesgcm:
keys:
- name: key1
secret: dGhpcy1pcy12ZXJ5LXNlYw==
- identity: {}
鍵のパスワードを先ほどbase64で暗号化したものに変えたくらいで、特に難しい操作はしていません。
identity: {}は既存の暗号化されていないシークレットを引き続き読み取るために必要なので記載しておきます。
仕上げに、これらのリソースを/etc/kubernetes/manifest/kube-apiserver.yamlで使用するように設定していきます。
catした内容は以下です。
追記個所は赤太文字にしています。
controlplane $ cat /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 172.30.1.2:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=172.30.1.2
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --encryption-provider-config=/etc/kubernetes/etcd/ec.yaml
- --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
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --secure-port=6443
- --service-account-issuer=https://kubernetes.default.svc.cluster.local
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/12
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: registry.k8s.io/kube-apiserver:v1.30.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 8
httpGet:
host: 172.30.1.2
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
name: kube-apiserver
readinessProbe:
failureThreshold: 3
httpGet:
host: 172.30.1.2
path: /readyz
port: 6443
scheme: HTTPS
periodSeconds: 1
timeoutSeconds: 15
resources:
requests:
cpu: 50m
startupProbe:
failureThreshold: 24
httpGet:
host: 172.30.1.2
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certs
readOnly: true
- mountPath: /etc/ca-certificates
name: etc-ca-certificates
readOnly: true
- mountPath: /etc/pki
name: etc-pki
readOnly: true
- mountPath: /etc/kubernetes/pki
name: k8s-certs
readOnly: true
- mountPath: /usr/local/share/ca-certificates
name: usr-local-share-ca-certificates
readOnly: true
- 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
securityContext:
seccompProfile:
type: RuntimeDefault
volumes:
- hostPath:
path: /etc/ssl/certs
type: DirectoryOrCreate
name: ca-certs
- hostPath:
path: /etc/ca-certificates
type: DirectoryOrCreate
name: etc-ca-certificates
- hostPath:
path: /etc/pki
type: DirectoryOrCreate
name: etc-pki
- hostPath:
path: /etc/kubernetes/pki
type: DirectoryOrCreate
name: k8s-certs
- hostPath:
path: /usr/local/share/ca-certificates
type: DirectoryOrCreate
name: usr-local-share-ca-certificates
- hostPath:
path: /usr/share/ca-certificates
type: DirectoryOrCreate
name: usr-share-ca-certificates
- hostPath:
path: /etc/kubernetes/etcd
type: DirectoryOrCreate
name: etcd
status: {}
controlplane $
diffをとった内容が以下です。
controlplane $ cp -p /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.org
controlplane $ vi /etc/kubernetes/manifests/kube-apiserver.yaml
controlplane $ diff /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.org
22d21
< - --encryption-provider-config=/etc/kubernetes/etcd/ec.yaml
98,100d96
< - mountPath: /etc/kubernetes/etcd
< name: etcd
< readOnly: true
132,135d127
< - hostPath:
< path: /etc/kubernetes/etcd
< type: DirectoryOrCreate
< name: etcd
詳しい内容は以下に記載されています。
22d21
< - --encryption-provider-config=/etc/kubernetes/etcd/ec.yaml
は、kube-apiserverが起動時に叩くコマンドを指定しています。
–encryption-provider-configには、EncryptionConfigurationファイルのパスを指定します。
98,100d96
< - mountPath: /etc/kubernetes/etcd
< name: etcd
< readOnly: true
132,135d127
< - hostPath:
< path: /etc/kubernetes/etcd
< type: DirectoryOrCreate
< name: etcd
は、まず
132,135d127
< - hostPath:
< path: /etc/kubernetes/etcd
< type: DirectoryOrCreate
< name: etcd
でホストの /etc/kubernetes/etcd をボリュームとしてアタッチしています。
type: DirectoryOrCreateはホストに指定したディレクトリが存在しない場合は作成するといったオプションです。
そして、
98,100d96
< - mountPath: /etc/kubernetes/etcd
< name: etcd
< readOnly: true
で先ほどアタッチしたボリュームをkube-apiserver用のpodにおける/etc/kubernetes/etcdにマウントするように設定してます。
これで作業は完了です。
Encrypt existing Secrets
- Encrypt all existing Secrets in Namespace
one
using the new provider- Encrypt all existing Secrets in Namespace
two
using the new provider- Encrypt all existing Secrets in Namespace
three
using the new provider
既存のSecretsを暗号化せよ、と言っています。
json形式で出力し、その内容に対して k replace を実行して暗号化していきます。
行末のハイフンは標準入力を表わしています。
kubectl -n one get secrets -o json | kubectl replace -f -
kubectl -n two get secrets -o json | kubectl replace -f -
kubectl -n three get secrets -o json | kubectl replace -f -
これでOKです。