STRIDEをKubernetesに当てはめる
一言でいうと
脅威モデリングは、「何が怖いのか」を勘で並べるのではなく、分類体系で漏れなくたどる方法です。 STRIDEの6項目をKubernetesのオブジェクトに当てはめると、点検項目が自然に出てきます。
なぜ必要なのか
セキュリティレビューで人によって結論が違うのは、それぞれが自分の知っている脅威しか見ないからです。 ネットワーク担当者はファイアウォールを、開発者はインジェクションを見ます。STRIDEは、「この6つをすべて問いかけたか」 というチェックリストを与えてくれます。見落としを構造的に減らすことが目的です。
どう動くのか
6項目のKubernetesへの読み替え
| STRIDE | 意味 | Kubernetesでの姿 | 主な対応 |
|---|---|---|---|
| Spoofing | 身元の偽装 | SAトークンの窃取、証明書の偽造、--asによるなりすまし |
強力な認証、トークンの有効期間の短縮、impersonate権限の統制 |
| Tampering | 改ざん | イメージのすり替え、etcdへの直接書き込み、マニフェストの操作 | イメージ署名、etcdアクセスの統制、GitOps |
| Repudiation | 否認 | 誰が行ったかを証明できない | 監査ログ、個別の身元(共有アカウントの禁止) |
| Information Disclosure | 情報の露出 | Secretの漏えい、kubeletの読み取りポート、ログ内の認証情報 | 保存時の暗号化、RBAC、ログの衛生管理 |
| Denial of Service | サービス拒否 | リソースの枯渇、apiserverの暴走、Webhookの障害 | ResourceQuota、LimitRange、PDB、APIの優先度 |
| Elevation of Privilege | 権限昇格 | privilegedのPod、hostPath、RBACの設定ミス | PSA、最小権限、アドミッションポリシー |
権限昇格の経路: KCSAの核心
権限昇格とは、「管理者権限を直接受け取ること」ではなく、「管理者ではない権限から管理者へ至る経路」 のことです。代表的な4つの経路を覚えておいてください。
1) pods/execまたはpods/attach
Podに入れれば、そのPodのServiceAccountトークンを読めます。
そのSAが自分より権限が高ければ、その権限をそのまま手に入れます。デバッグの利便のために与えた権限が、
「クラスター内のすべてのSAのうち最も強いもの」へ続くはしごになります。
2) secretsの読み取り
Secretには、ほかのSAのトークン、DBの認証情報、外部APIキーが入っています。
ネームスペースにget secretsがあれば、そのネームスペースのすべてのSecretを読めます。
3) rolebindings/clusterrolebindingsに対するcreate
自分自身により高い権限を付与できます。Kubernetesはこれを知っているので、デフォルトでは
自分が持っていない権限は付与できないようにブロックします(権限昇格の防止)。その防御を破るのが次の項目です。
4) escalate、bind、impersonateの動詞
escalate: 自分が持っていない権限もRoleに入れられるようにするメタ権限bind: 自分が持っていないRoleをバインドできるようにする権限impersonate: ほかのユーザー/グループ/SAになりすます権限。system:mastersになりすませば終わりです
そして、Podスペックそのものが昇格の経路です。
privileged: true→ 事実上ノードのroothostPathで/をマウント → ノードのファイルシステム全体(kubeletの証明書を含む)hostNetwork/hostPID→ ノードのネットワーク・プロセスのネームスペースcapabilities.add: [SYS_ADMIN]→ コンテナ脱出の道具箱
「Podを作れる」ということは、潜在的には「そのノードを所有できる」に近いのです。 そのため、PSAやポリシーエンジンでPodスペックを制限することは、RBACと同じくらい重要です。
サプライチェーンの脅威
自分がクラスターをどれだけ堅く守っても、自分から進んで取得して実行するものには効果がありません。
- ベースイメージ: 脆弱なライブラリがそのまま入ってくる
- タグの移動:
myapp:1.4.2が昨日と今日で別の内容を指すことがある - 依存関係の汚染: typosquatting、アカウント乗っ取りによる悪意あるバージョンの配布
- ビルドパイプライン: CIがクラスターへのデプロイ権限を持っているので、CIの侵害はクラスターの侵害
- ビルド引数の残存:
--build-argで渡したシークレットは、docker history --no-truncでそのまま出てきます。 後続のレイヤーでファイルを削除しても前のレイヤーに残り、プッシュしていればそのイメージを取得した全員が読めます
対応は出所と完全性です。信頼するレジストリだけを許可し、ダイジェストで固定し、イメージ署名を検証し、 SBOMで何が入っているかを一覧にします。
永続化(persistence)の確保手法
侵入に成功した攻撃者は、再び入るための道を作っておきます。整理しておけば検知項目になります。
- DaemonSetの配置: 全ノードに1つずつ置かれ、ノードが追加されれば自動で追従する
- CronJob: 定期的に復活するバックドア
- アドミッションWebhookの登録: すべてのオブジェクト作成を横取りし、こっそり変更(mutating)できる
- ClusterRoleBindingの追加: 目立たない名前でcluster-adminを付けておく
- SAトークンの確保: 長期トークンのSecretを作っておけば、アカウントを削除しても生き残れる
(
--service-account-lookup=falseのクラスターでは特に) - 静的Pod: ノードのマニフェストディレクトリにファイルを置くと、apiserverを経由せずに実行されます。 kubectlからは正常なミラーPodに見えます
現場での姿
著者のブログがまとめたRBACの事故パターンのうち最もよくあるのが、「すべてのServiceAccountがクラスターの
リソースを読める異常な状態」が見つかるケースです。診断は、
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin")'
でcluster-adminのバインディングの主体を全件調べることで、これはまさに永続化手法の検知と同じクエリです。
サプライチェーンの側には、もっと実感の湧く事例があります。docker build --build-arg NPM_TOKEN=...で渡した
トークンが、docker history --no-truncにそのまま出てくることです。
RUNステップでファイルを削除しても、前のレイヤーに残ります。正しい方法は、
RUN --mount=type=secret,id=npmrc,...のように、ビルドシークレットのマウントを使うことです。
そして、シークレット漏えいへの対応における順序についての著者の指摘が、脅威モデリングと重なります。 最もよくある反応はGit履歴の書き換えですが、順序が間違っています。 公開リポジトリにプッシュされた認証情報は、数秒から数分のうちに自動化されたスキャナーに収集され、 履歴を消しても、フォーク・既存のクローン・プラットフォームが保管するPR参照・検索キャッシュ・CIアーティファクトに残ります。 取り消し(revocation)が先で、履歴の整理は再発を減らす衛生作業です。 脅威モデリングが、対応の順序まで変える例です。
次のクイズで確認すること
このモジュールはクイズで締めくくります。次のモジュールでは、Podスペックに基づく権限昇格を実際に防ぐ仕組みである PSAを扱い、ラボでrestrictedに違反するPodが拒否されることを自分で確認します。