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

KCSA — Kubernetesセキュリティアソシエイト

Secretの参照・Podの作成・実行時の安全性は別の境界

TT Labで続きを見る

一言でいうと

Secret APIを読む権限、Podを作成する権限、Podの実行のセキュリティ設定は、それぞれ別の境界です。 RestrictedなPodも、同じネームスペースのSecretをボリュームとして読めます。

なぜ必要なのか

例として挙げるシナリオです。デプロイ用のサービスアカウントからget secretsを削除しました。 kubectl auth can-iもnoと答えたため、シークレットにはアクセスできないと報告しました。 ところが、そのアカウントにはcreate podsが残っていました。攻撃者は、Secretを読むAPIの代わりに、 Secretボリュームを持つPodを作成するAPIを選びました。kubeletがボリュームを用意すると、 コンテナは通常のファイル読み取りで値を得ました。直接のアクセスの拒否と、間接的なアクセスの遮断は別物です。

どう動くのか

3つの問い

境界 尋ねる質問 このラボで見ること
認証 リクエスト元は誰ですか 管理者の証明書と、代理でリクエストしたrunner SAを区別する
認可 この動詞・リソース・範囲を許可していますか Secretの取得は拒否、appでのPod作成は許可
アドミッション 許可された作成リクエストのオブジェクトを受け入れますか Restrictedの実行条件を満たすPodかを検査

Pod作成リクエストの主体と、PodのserviceAccountNameは、同じ概念ではありません。 管理者がrunnerを指定したPodを作成したからといって、runnerの作成権限を使ったことにはなりません。 次のラボでは、--as=system:serviceaccount:app:runnerで実際の作成リクエストを送ります。 これは管理者のimpersonate権限を利用した教育用の代理リクエストであり、SAトークンの窃取や偽造ではありません。

Restrictedが防ぐものと残すもの

Pod Security StandardsのRestrictedは、privilege escalationやroot実行など、コンテナの 実行設定を制限します。baselineの段階から、hostPath・hostPID・hostNetwork・privilegedのような 危険な設定を制限します。しかし、Restrictedが許可するボリュームにはsecretとconfigMapが 含まれています。このポリシーは、あるユーザーが特定のSecretを業務上使ってよいかを判断しません。

runAsNonRoot: true、capabilitiesのdrop ALL、allowPrivilegeEscalation: false、 seccompProfile: RuntimeDefaultをすべて入れても、コンテナが正当にマウントされたファイルの 読み取りまでは禁止されません。automountServiceAccountToken: falseも、APIトークンの自動 マウントを無効にするオプションであって、Secretボリュームを防ぐオプションではありません。

そのため、create podsは常にノードのrootになるという説明も間違いです。同じ権限でも、 アドミッションポリシー、選択できるSA、ネームスペースの範囲、ボリュームの種類によって、到達できる範囲が異なります。 pods/execは、入れる既存のコンテナの権限とデータの影響を受けます。 bind・escalate・impersonateも、許可されたリソースと主体の範囲を確認する必要があり、 この動詞が1つあるというだけで、無条件にcluster-adminだと断定してはいけません。

防御は、データとワークロードの境界をあわせて決める

互いに信頼しない作業を、機微なSecretと同じネームスペースに置かないことが 出発点です。ワークロードの作成権限と、選択できるサービスアカウントを最小化し、必要なら 別のアドミッションポリシーで、許可されたSecret参照・SAの選択を制限します。直接のget権限に resourceNamesを設定しても、Pod作成によって参照されるボリュームまで自動で制限されるわけではありません。 NetworkPolicyはネットワーク接続の統制なので、すでにマウントされたローカルファイルの読み取りは防ぎません。

PSAは、その上でやはり必要です。ノードへ越えていく危険な実行設定を減らす統制と、 ネームスペース内の業務データを誰が使えるかを決める統制を、あわせて適用する必要があります。 どちらか1つを有効にしたというチェックマークで、別の境界の検証を省略しないことが核心です。

保存時の暗号化も別の境界

SecretのAPIのdataの値はbase64の表現であり、暗号化ではありません。保存時の暗号化を構成して いない場合、保存先のデータは機密性を保証されません。今回のk3s/kine環境では、 SQLite内のシリアライズされたSecretで、合成マーカーを確認します。すべてのディストリビューションが同じファイル・ シリアライズ形式を使うという意味ではなく、マネージドサービスの暗号化のデフォルト値も別に確認する必要があります。

EncryptionConfigurationは、最初のプロバイダーで新しい値を書き、後ろのプロバイダーで以前の値を読みます。 identityを先頭に置くと、新しいデータも暗号化されません。設定を有効にしたり、キーを変更したりしても、 既存のオブジェクトが自動で保存し直されるわけではありません。書き直し・検証・古い読み取り経路の削除が必要です。 正常に認可されたAPIリクエストは復号された値を受け取るため、保存時の暗号化が、上のマウント経路を 遮断するわけではありません。キーとバックアップをあわせて露出すると、暗号化の効果も失われかねません。

現場での姿

点検の順序の例は、次のとおりです。まず特定のSAについて、ネームスペースを指定して can-i get secretsとcan-i create podsをそれぞれ尋ねます。次に、どんなPodを作成できるか、 アドミッションポリシーとSAの選択範囲を見ます。最後に、作成されたワークロードがどんな データをマウントするかを確認します。can-i --listは出発点であって、外部の認可・アドミッションの 結果をすべて完全に説明する証拠ではありません。実際のリクエストと失敗の原因をあわせて見る必要があります。

サービスアカウントの自動トークンマウントを無効にしても、Podに明示した設定が優先されることがあります。 このオプションを変更しても、すでに実行中のすべてのPodがただちに変わるわけではないので、影響を確認して 新しいPodで適用します。トークンが不要かどうかと、Secretが不要かどうかは、別々に判断します。

401と403も、区別して読みます。匿名認証が許可されたパスで資格のないリクエストは、 system:anonymousユーザーとsystem:unauthenticatedグループとして処理されることがあります。 したがって、匿名のリクエストが常に401になるわけではありません。パスごとの匿名の許可、無効な認証情報、 認可の結果によって変わります。403だけを見て、どのアドミッション統制まで通過したのかを断定せず、 レスポンスの理由と監査記録をあわせて確認します。

次のラボですること

合成Secretで保存時の暗号化と書き直しを確認したあと、Restrictedが適用されたappで、 runnerが作成した非rootのPodのSecretファイルのハッシュを比較します。実際のシークレットの値は出力しません。 APIでの直接の取得の拒否、Pod作成の成功、Secretボリュームの読み取りの成功を、別々の証拠として記録します。 そのあと、認証の境界と、本文のない監査ログを確認します。このVMは教育用のシングルノードであり、 この結果を、ほかのテナントや本番クラスター全体のセキュリティの保証に拡大解釈しません。

公式ドキュメントでさらに確認する