ServiceAccount — ワークロードのアイデンティティ
一言でいうと
人は外部の認証システムでアイデンティティを証明しますが、PodはServiceAccountで証明します。その証明書がトークンであり、最近のトークンは有効期限が切れます。
なぜ必要なのか
クラスターの中で動くプログラムも、APIを呼び出します。Operator、コントローラー、モニタリングエージェント、CIランナーが、すべてそうです。これらに人間のアカウントを発行することはできないため、Kubernetesはワークロード専用のアイデンティティであるServiceAccountを用意しています。Podは自分のSAのトークンをファイルとしてマウントされ、apiserverはそのトークンでサブジェクトを識別します。その後の判定は、前のモジュールで学んだRBACが行います。認証(誰なのか)と認可(何ができるのか)は別物という構造が、ここで鮮明に見えます。
問題は、以前の方式でした。1.24より前は、SAを作成するとトークンを含むSecretが自動的に1つ作られ、そのトークンには有効期限がありませんでした。一度漏れると永遠に有効な認証情報が、クラスターごとに何十個も転がっていたということです。ログに出力されても、イメージレイヤーに残っても、コピーを追跡する方法もありませんでした。
どう動くのか
1.24からデフォルトが変わりました。SAを作成してもSecretが自動的には作られず、Podには有効期間が決められたprojectedトークンがマウントされます。kubeletが期限切れの前に自動で更新するので、アプリケーションはファイルを読み直すだけで済みます。
トークンを手動で取得するときは、TokenRequest APIを使います。
kubectl create token payments -n sa-lab --duration=3600s --audience=vault
このトークンのペイロードを開くと、3つのことが見えます。subはサブジェクト(system:serviceaccount:<네임스페이스>:<이름>)、audはこのトークンをどこで使うのか、expとiatは有効期間です。audが重要なのは、再利用攻撃を防ぐからです。Vaultに渡すために発行したトークンがapiserverにも通用すると、1か所が突破されたときにほかの場所まで開いてしまいます。対象が埋め込まれたトークンは、その対象でのみ有効です。
Podの仕様に直接書くと、次のようになります。
volumes:
- name: vault-token
projected:
sources:
- serviceAccountToken:
audience: vault
expirationSeconds: 3600
path: vault-token
この仕組みが、クラウドのワークロードアイデンティティの基盤です。Podが受け取ったこの短命のトークンをクラウドのSTSに提示すると、一時的な認証情報に交換してくれます。保存すべき静的なキーそのものがなくなります。
逆方向の制御もあります。トークンが不要なPodには、そもそも渡さないことです。
spec:
automountServiceAccountToken: false
このフィールドは、PodにもServiceAccountにも設定できます。アカウント側に設定すると、そのアカウントを使うすべてのPodにデフォルトで適用されます。Webサーバーのように、APIをまったく呼び出さないワークロードは、トークンを持っている理由がなく、コンテナが突破されたときに攻撃者が持ち去れるものが1つ減ります。
レガシーな方式も、今でも作成できます。kubernetes.io/service-account-tokenタイプのSecretにkubernetes.io/service-account.nameアノテーションを付けると、有効期限のないトークンができます。クラスター外のツールがTokenRequestに対応していないときの最後の手段ですが、有効期限がないということは、取り消しの手続きを人が100%責任を持つということなので、推奨されません。
現場での姿
1つ目は、defaultサービスアカウントの乱用です。SAを指定しないPodは、ネームスペースのdefaultを使います。その結果、1つのネームスペースのすべてのPodが同じアイデンティティを共有することになり、権限を少しでも与えた瞬間に、その権限が全員に広がります。Podごとに専用のSAを与えるのが基本です。
2つ目は、権限は依然としてRBACが決めることです。SAを作成しただけでは、権限は何も生じません。逆に、便宜上設定しておいたバインディング1つが、ワークロードに過剰な権限を与えてしまうこともあります。--asで問い合わせる習慣は、ここでも有効です。
3つ目は、トークンファイルのパスです。デフォルトのマウントパスは/var/run/secrets/kubernetes.io/serviceaccountです。コンテナ乗っ取り事故の分析で、攻撃者が最初に確認するパスでもあります。サイドカーやデバッグコンテナも同じものを見られるという点を、覚えておく必要があります。
次のラボですること
専用のSAを作成してPodに付け、自動マウントされたトークンボリュームを確認します。トークンをオフにする2つの方法を、Podとアカウントの両方で適用してみて、TokenRequestで取得したトークンのペイロードをデコードし、サブジェクト・対象・有効期間を自分で読み取ります。対象が指定されたprojectedトークンを仕様に書き、レガシーの長期トークンSecretを作成してそのリスクを整理したあと、最後に使用状況の監査レポートを作成します。