TT Lab
はじめる
学ぶ 学習パス コース

CKS — Kubernetesセキュリティスペシャリスト

Secretの扱いと保存時の暗号化

TT Labで続きを見る

目標

Secretをタイプ別に作成し、注入の方式によって露出する経路がどう変わるかを確認し、RBACで読み取りの範囲を絞ったうえで、保存時の暗号化の設定ファイルを自分で書きます。

なぜ重要なのか

Secretマニフェストの値がbase64になっているため暗号化されているように見えますが、base64はエンコードであって暗号化ではありません。鍵がなく、元に戻すのにコマンド1つで済みます。デフォルトの設定では、Secretはetcdに平文で保存されるため、etcdのバックアップファイルを手に入れた人は、すべてのSecretを読めます。EncryptionConfigurationを有効にして初めて、保存された値が暗号化され、このときidentityプロバイダーは必ずリストの最後でなければなりません。先頭に来ると、平文での保存に戻ります。

注入の方式も重要です。環境変数は/proc/PID/environで読まれ、子プロセスに継承され、クラッシュレポートやデバッグページにまるごと載って流出します。ボリュームマウントのほうが安全で、defaultModeでファイルの権限を絞るのが慣例です。

最後はRBACです。あるネームスペースでget secrets権限を持つ人は、そのネームスペースのすべてのSecretを読めます。resourceNamesで特定のSecretだけを許可できるという事実は、意外とあまり知られていません。

ステップ

  1. ネームスペースcks-secretsを作成し、その中にOpaque Secretdb-credを作成してください。キーはusername(値はapp)とpassword(値はpr0d-Db-Pass)の2つです。
  2. cks-secretsにkubernetes.io/dockerconfigjsonタイプのSecretregistry-credを作成してください。サーバーはregistry.cks.local、ユーザーはci、パスワードはci-tokenです。
  3. cks-secretsにkubernetes.io/tlsタイプのSecretshop-tlsを作成してください。tls.crtとtls.keyがどちらも空でないようにします。
  4. cks-secretsにPodenv-app(コンテナ名app、イメージnginx:1.27-alpine)を作成してください。コンテナのenvFrom[0].secretRef.nameはdb-credにします。
  5. cks-secretsにPodvol-app(コンテナ名app)を作成してください。ボリューム名はdb、secret.secretNameはdb-cred、secret.defaultModeは0400(10進数で256)とし、コンテナはそのボリュームを/etc/dbにreadOnly: trueでマウントします。
  6. cks-secretsにOpaque Secretapp-configを作成してください。キーはmode(値はstrict)の1つで、immutableはtrueにします。
  7. cks-secretsにServiceAccountappを作成し、Roledb-cred-readerを作成してください。apiGroupsはコアグループ、resourcesはsecrets、resourceNamesはdb-credの1つ、verbsはgetの1つです。RoleBindingapp-db-credでSAappにバインドします。そのあと、2つの質問の答え(yes/no)を順番に/root/cks-secrets/can-i.txtに2行で保存してください。1行目はappのSAがsecret/db-credをgetできるか、2行目はsecret/app-configをgetできるかです。
  8. /root/cks-secrets/encryption-config.yamlに、保存時の暗号化の設定を書いてください。apiVersion: apiserver.config.k8s.io/v1、kind: EncryptionConfiguration、resources[0].resourcesはsecretsの1つ、providersは2つで、1つ目がaescbc(キー名はkey1、secretは32バイトをbase64エンコードした値)、最後がidentityです。

参考

Opaque Secretを作成する

ネームスペースcks-secretsを作成し、その中にOpaque Secretdb-credを作成してください。キーはusername(値はapp)とpassword(値はpr0d-Db-Pass)の2つです。

kubectl create secret generic --from-literal=が最も速いです。タイプを指定しなければOpaqueです。

レジストリ認証情報のSecret

cks-secretsにkubernetes.io/dockerconfigjsonタイプのSecretregistry-credを作成してください。サーバーはregistry.cks.local、ユーザーはci、パスワードはci-tokenです。

タイプが決まっているSecretは、データのキー名も決まっています。dockerconfigjsonタイプは、.dockerconfigjsonキー1つだけを使います。

TLS Secret

cks-secretsにkubernetes.io/tlsタイプのSecretshop-tlsを作成してください。tls.crtとtls.keyがどちらも空でないようにします。

tls.crtとtls.keyの2つのキーが両方そろっていて初めて、APIサーバーが受け付けます。

環境変数として注入する

cks-secretsにPodenv-app(コンテナ名app、イメージnginx:1.27-alpine)を作成してください。コンテナのenvFrom[0].secretRef.nameはdb-credにします。

envFromのsecretRefは、Secretのすべてのキーを環境変数として展開します。便利ですが、プロセスの環境にそのまま残ることを覚えておいてください。

ボリュームとしてマウントし、権限を絞る

cks-secretsにPodvol-app(コンテナ名app)を作成してください。ボリューム名はdb、secret.secretNameはdb-cred、secret.defaultModeは0400(10進数で256)とし、コンテナはそのボリュームを/etc/dbにreadOnly: trueでマウントします。

defaultModeは、YAMLで8進数として書くと10進数で保存されます。0400は所有者の読み取り専用です。

変更不可のSecret

cks-secretsにOpaque Secretapp-configを作成してください。キーはmode(値はstrict)の1つで、immutableはtrueにします。

immutable: trueを有効にすると、データを変更できなくなり、削除して作り直す必要があります。kubeletのwatchの負荷も減ります。

特定のSecretだけを読めるように制限する

cks-secretsにServiceAccountappを作成し、Roledb-cred-readerを作成してください。apiGroupsはコアグループ、resourcesはsecrets、resourceNamesはdb-credの1つ、verbsはgetの1つです。RoleBindingapp-db-credでSAappにバインドします。そのあと、2つの質問の答え(yes/no)を順番に/root/cks-secrets/can-i.txtに2行で保存してください。1行目はappのSAがsecret/db-credをgetできるか、2行目はsecret/app-configをgetできるかです。

RoleのresourceNamesで名前を固定すれば、そのSecretだけを読めます。結果は、auth can-iにTYPE/NAME形式で問い合わせて確認してください。

保存時の暗号化の設定ファイル

/root/cks-secrets/encryption-config.yamlに、保存時の暗号化の設定を書いてください。apiVersion: apiserver.config.k8s.io/v1、kind: EncryptionConfiguration、resources[0].resourcesはsecretsの1つ、providersは2つで、1つ目がaescbc(キー名はkey1、secretは32バイトをbase64エンコードした値)、最後がidentityです。

プロバイダーのリストは、順序に意味があります。書き込みには最初のものが使われ、読み取りには順番に試されます。identityの位置を誤ると、平文での保存に戻ります。