SPIREで始める、ゼロトラストなマイクロサービス間認証——なぜ証明書管理がAI時代の基盤になるのか?
はじめに:AI時代の「誰だ?」問題
あなたのインフラで動いている「通信主体」のうち、人間が占める割合はどれくらいでしょうか。マイクロサービス、CI/CDパイプライン、そしてAIエージェント——いまやデータセンター内を流れるトラフィックの多くは、非人間主体(NHI: Non-Human Identity)によるものです。マシンアイデンティティの数は人間アカウントを大きく上回っており、この傾向はAIエージェントの普及によってさらに加速すると予想されます。
VPNの時代は終わった
長年、私たちのセキュリティモデルは「城壁と堀(Castle and Moat)」に依存してきました。VPNで内部ネットワークに入れば「信頼できる」、外にいれば「信頼できない」——この二値モデルです。しかし、この前提はすでに破綻しています。
破綻の理由は3つあります。
1. マイクロサービスの動的スケール Kubernetesのようなコンテナオーケストレーション環境では、数百のサービスが日々スケールアウト・スケールインを繰り返します。PodのIPアドレスは数分単位で変動し、「このIPは安全」という前提が意味を持ちません。IPベースのファイアウォールルールをメンテナンスする運用コストは、スケールに比例して増大します。
2. 東西通信(ラテラルムーブメント)の防衛不在 一度VPNを抜けられ、あるいは内部のひとつのサービスが侵害されると、攻撃者は「信頼された内部ネットワーク」を自由に横断できます。内部の「信頼された」領域に侵入された後の水平展開(ラテラルムーブメント)が被害拡大の直接的要因となるケースは、セキュリティ業界で広く指摘されています。ネットワーク境界で守られていたはずの「内部」は、ひとたび侵入されれば無防備な平原になります。
3. AIエージェントによる通信の増加 近年とくに注目すべき変化は、AIエージェントが自律的にシステム間を通信するようになったことです。AIエージェントは人間のユーザーとは異なり、短時間に多数のAPIを呼び出し、プロンプトの指示に従って動的にアクセス先を変更します。この「非人間主体(NHI)」の通信を、従来のIPアドレスやAPIキーで管理することは困難になりつつあります。
想定シナリオ:退職者のPATでエージェントが稼働し続ける場合
以下は、NHI問題を理解するための想定シナリオ(架空の例)です。
ある企業のDevOpsエンジニアが退職しました。彼女のPersonal Access Token(PAT)は、CI/CDパイプラインに組み込まれたAIエージェントの認証に使われていました。退職後、アカウントは無効化されましたが、PATの失効が漏れていた場合、エージェントは稼働し続ける可能性があります。エージェントは本番データベースへの読み取りアクセスを持ち、プロンプトインジェクションの標的にもなりかねない状態になります。
これは極端な例ではなく、共有サービスアカウントで動くAIエージェント、複数のワークロードで使い回されるAPIキーといった問題が、実際のエンタープライズ環境で懸念として挙げられています。
この記事でできるようになること
この記事は、SPIFFE/SPIRE(SPIFFE Runtime Environment)を使ったワークロード間のゼロトラスト認証を、実践的に学ぶチュートリアルです。
読み終える頃には、以下ができるようになります:
- ゼロトラストの原則(NIST SP 800-207)がなぜAI時代に必須なのかを理解している
- SPIREをKubernetesにデプロイし、ワークロードに暗号学的アイデンティティを付与できる
- mTLS(相互TLS)通信を自動化し、証明書のローテーションをシームレスに実現できる
- AIエージェントに安全なアイデンティティを持たせる設計ができる
それでは、「ゼロトラスト」とはそもそも何なのか、その核心から見ていきましょう。
ゼロトラストとは何か——NIST SP 800-207の核心
「Never Trust, Always Verify(決して信頼せず、常に検証する)」
これはゼロトラストの概念を象徴する広く知られたスローガンであり、米国国立標准技術研究所(NIST)が発表した SP 800-207「Zero Trust Architecture」 が示す設計原則の精神を要約したものです(出典: NIST SP 800-207)。この一文は簡潔ですが、その背後にはセキュリティ設計のパラダイムシフトが含まれています。
「内か外か」から「誰が何を」へ
従来のセキュリティモデルは、ネットワークの位置で信頼を判定していました。
VPNの内側 = 信頼できる(Trust) VPNの外側 = 信頼できない(Untrust)
しかし、この前提には致命的な欠陥があります。一度内部に侵入されれば、攻撃者は「信頼された主体」として振る舞えるからです。クラウドネイティブ環境では「内部ネットワーク」という境界線そのものが曖昧になっており、このモデルは物理的にも論理的にも成立しません。
ゼロトラストは、信頼の判定基準をネットワークの位置からアイデンティティの検証へと移します。VPNの中にいようと外にいようと、すべての通信は「誰が、何に、なぜアクセスしようとしているのか」を検証されます。
ZTAを構成する3つのコアコンポーネント
NIST SP 800-207は、ゼロトラストアーキテクチャ(ZTA)を3つの論理コンポーネントで定義しています。
| コンポーネント | 役割 | 例え |
|---|---|---|
| Policy Engine(PE) ポリシーエンジン |
アクセス要求を評価し、許可・拒否を決定する「頭脳」。ユーザーの属性、デバイスの状態、リスクスコア等を統合して判断 | 入館審査を行う警備員 |
| Policy Administrator(PA) ポリシー管理者 |
PEの決定に基づき、セッションの確立・切断を制御 | 審査結果に基づき鍵を渡す受付 |
| Policy Enforcement Point(PEP) ポリシー適用ポイント |
実際の通信経路上でポリシーを強制する「門」。PAの指示に従い通信を許可・遮断 | エントランスの改札 |
graph LR
W[ワークロード<br>アクセス要求] --> PEP[Policy Enforcement Point<br>ポリシー適用ポイント]
PEP --> PA[Policy Administrator<br>ポリシー管理者]
PA --> PE[Policy Engine<br>ポリシーエンジン]
PE -->|許可/拒否| PA
PA -->|セッション制御| PEP
PEP -->|通信許可時| R[対象リソース]
style PE fill:#4dabf7,stroke:#1971c2
style PA fill:#ffd43b,stroke:#f08c00
style PEP fill:#51cf66,stroke:#2b8a3e
NIST SP 800-207では、これらのコンポーネントがアクセス要求に対して認証・認可を行い、セッションを確立する仕組みが定義されています。セッション確立後も継続的な評価が推奨されますが、「一度認証したら以降は完全に自由」という暗黙の信頼は存在しないのが基本方針です。
ワークロード・アイデンティティがZTA実装の要
NIST SP 800-207で特に注目すべき点は、ゼロトラストの対象を人間のユーザーだけに限定していないことです。マイクロサービス、APIクライアント、そしてAIエージェント——これらすべての「ワークロード」が、ZTAにおける「主体(Subject)」となります。
ここで浮かび上がる核心的な問いはこれです:
「ワークロードのアイデンティティを、どうやって暗号学的に証明するのか?」
人間であれば、パスワードや多要素認証(MFA)で自分を証明できます。しかし、Kubernetes Podで動くマイクロサービスやAIエージェントはどうでしょうか。静的なAPIキーを環境変数に埋め込む——これが現在の一般的なやり方ですが、NIST SP 800-207の原則に照らせば不十分です。なぜなら、APIキーは「知っている人なら誰でも」そのワークロードになりすませてしまうからです。
この問いに答えるのが、SPIFFE(Secure Production Identity Framework for Everyone)というオープン標準であり、そのリファレンス実装であるSPIREです。ワークロードに対して、パスワードやAPIキーを使わずに暗号学的なアイデンティティを付与する——それが次世代のゼロトラスト基盤となります。
ですが、そこに至る前に、まずは従来の認証手法がなぜ限界を迎えているのか、具体的に整理しておきましょう。
従来の認証手法が限界を迎える理由
ゼロトラストの必要性を理解したところで、では現在あなたのシステムが使っている認証手法は、どこが限界なのでしょうか。ここでは、マイクロサービスおよびAIエージェントの認証で広く使われている3つのアプローチを取り上げ、それぞれの問題を整理します。
パターン1:静的IP + ファイアウォール
最も古典的なアプローチは「IPアドレスベースのアクセス制御」です。ファイアウォールルールで「10.0.1.5(決済サービス)→ 10.0.2.10(DB)の通信は許可」といった設定を行います。
限界の理由:
Kubernetes環境では、PodのIPアドレスはスケジューリングのたびに変動します。ある瞬間には10.0.1.5が決済サービスであっても、次の瞬間にはまったく別のサービスに割り当てられている可能性があります。数百のサービスが数分単位でIPアドレスを変える環境で、静的なIPベースのルールを正確にメンテナンスするのは事実上不可能です。
さらに、IPアドレスはなりすまし(IPスプーフィング)に対する防御を持ちません。「内部IPからの通信だから安全」という前提は、ひとたび内部ネットワークに侵入されれば崩壊します。
パターン2:APIキー / 共有シークレット
現在、マイクロサービス間認証で最もよく使われている手法がAPIキーです。環境変数やKubernetes SecretにAPIキーを格納し、サービス間通信のHTTPヘッダーに付与します。
限界の理由:
APIキーの根本的な問題は、それが静的なクレデンシャル(Static Credential)であることです。
- 漏洩リスク: APIキーはHTTPヘッダーに平文で送信されることが多く、ログやミドルウェアに意図せず残存します。AIエージェントが扱うプロンプトやツール実行ログにもAPIキーが混入するリスクがあります。
- ローテーション運用負荷: セキュリティのベストプラクティスでは、クレデンシャルは定期的にローテーションすべきです。しかし、数十から数百のサービスのAPIキーを手動または半手動でローテーションする運用は、SREチームにとって深刻な負担になります。
- 監査不可能: 複数のワークロードが同一のAPIキーを共有している場合、「誰がいつそのAPIキーを使ったか」を追跡できません。
パターン3:共有サービスアカウント
「svc-payment」のような共通サービスアカウントを複数のワークロードで使い回すパターンです。KubernetesのServiceAccountとして実装されることが多く、RBACルールを単純化できるという理由で採用されがちです。
限界の理由:
共有アカウントの最大の問題は責任分界(Accountability)の不明確さです。5つのサービスが同じsvc-paymentアカウントを使っている場合、異常なアクセスが検知されても「どのサービスが原因か」を特定できません。これはゼロトラストの「すべてのアクセスを検証・記録する」という原則に真っ向から反します。
AIエージェントがもたらす新たな脅威
これら3つの従来パターンは、マイクロサービス時代ですでに限界を迎えていましたが、AIエージェントの台頭によってその限界はより顕著になります。
AIエージェント特有のリスクとして、以下が指摘されています。
プロンプトインジェクション経由の権限昇格: AIエージェントは自然言語の指示に従って動きます。悪意のあるプロンプト(例えば、Jiraのチケット内容に埋め込まれた指示)によって、エージェントが本来アクセスすべきでないリソースにアクセスさせられるリスクがあります。プロンプトインジェクションを起点とした権限昇格は、AIセキュリティ研究において重要な脅威モデルとして議論されています。
自律行動による想定外アクセス: AIエージェントは自律的にツールを選択し、APIを呼び出します。設計時に想定されていないアクセスパターンが「エージェントの判断」で実行される可能性があり、静的なAPIキーベースの認可ではこの動的なアクセスを制御できません。
「Credential Zero」問題——鶏と卵のパラドックス
ここで、すべての従来手法に共通する根源的な問題を整理します。
Vault(シークレット管理ツール)を使えば良い、と考えるかもしれません。しかし、Vaultからシークレットを取得するための認証情報は、どうやってワークロードに渡すのか?——これが「Credential Zero問題(Secure Introduction問題とも呼ばれる)」です。
ワークロード → Vaultからシークレット取得 → そのシークレットでDBに認証
↑
この「Vaultへの認証」はどうやる?
Vaultに認証するためのトークンを環境変数に埋め込む——それでは元の木で、別の静的クレデンシャルを生み出しただけです。このパラドックスを解消するには、静的クレデンシャルに依存しない認証メカニズム、すなわちプラットフォームのメタデータから自動的にアイデンティティを証明する仕組みが必要です。
従来手法 vs SPIRE:比較まとめ
| 観点 | 静的IP + FW | APIキー / 共有シークレット | 共有サービスアカウント | SPIRE(SPIFFE) |
|---|---|---|---|---|
| アイデンティティの一意性 | ✗(IPは変動) | △(キーは共有されがち) | ✗(アカウント共有) | ✓(ワークロード単位で一意) |
| クレデンシャル漏洩リスク | — | ✗(静的で高リスク) | ✗(共有で高リスク) | ✓(静的クレデンシャル不要) |
| 自動ローテーション | — | ✗(運用負荷大) | ✗ | ✓(自動・シームレス) |
| 監査性 | ✗ | ✗(追跡不可) | ✗(責任不明) | ✓(発行・使用を追跡可能) |
| 動的スケール対応 | ✗(IP変動で破綻) | △ | △ | ✓(プラットフォーム連携) |
SPIREは、これらすべての課題に対して「静的クレデンシャルを使わずに、ワークロードに暗号学的アイデンティティを自動付与する」というアプローチで答えます。
では次に、SPIFFE/SPIREがどのような仕組みでこれを実現しているのか、そのアーキテクチャを解き明かしていきましょう。
SPIFFE / SPIREの仕組み — ワークロードにパスポートを
ここまで「従来の認証がなぜ壊れるのか」を見てきました。では、どうやってワークロードに安全なアイデンティティを与えるのでしょうか。答えはSPIFFEとそのリファレンス実装であるSPIREにあります。
SPIFFE(Secure Production Identity Framework for Everyone)は、CNCFのgraduated project(2022年9月卒業)であり、マイクロサービスやワークロードに暗号学的なアイデンティティを付与するオープン標準です。一言で言えば、「サービスにパスポートを発行する仕組み」です。
SPIFFE ID — ワークロードのURIパスポート
SPIFFEの中核となるのがSPIFFE IDです。これはURI形式の識別子で、各ワークロードを一意に特定します。
spiffe://example.org/ns/default/sa/my-app
└scheme┘ └─trust domain─┘ └─── path ────────┘
- scheme: 常に
spiffe:// - trust domain: 信頼の境界を表すドメイン(例:
example.org、ai.example.com) - path: ワークロードの属性(namespace、service accountなど)
SPIFFE IDの優れた点は、IPアドレスに依存しないことです。PodがスケールしてIPが変わっても、Podが別ノードに再スケジュールされても、SPIFFE IDは変わりません。アイデンティティが「どこにあるか」ではなく「何であるか」で定義されます。
SVID — 暗号学的身分証
SPIFFE IDだけでは単なる文字列です。これを暗号学的に証明するのがSVID(SPIFFE Verifiable Identity Document)です。SVIDには2種類あります。
| SVID種別 | 形式 | 用途 |
|---|---|---|
| X.509-SVID | 短命のX.509証明書 | mTLS(相互TLS認証) |
| JWT-SVID | JWTトークン | L7認証(HTTPヘッダー、トークンベース) |
ほとんどのユースケースではX.509-SVIDを使ったmTLSが主役になります。重要なのは、これらの証明書が短命であることです。従来のPKIでは証明書の有効期限が1年や2年でしたが、SPIFFEではTTL(有効期限)を設定により短くでき、数時間やそれ以下にも設定可能です。短命な証明書は、漏洩時の露出期間を短縮する補完的対策として機能します。
⚠️ TTLに関する注意: X.509-SVIDおよびJWT-SVIDの具体的なデフォルトTTL、設定可能範囲、ローテーション挙動は、SPIREのバージョン、Server設定、Registration EntryのTTL設定、およびSDK/プロキシの実装に依存します。詳細は公式ドキュメントを参照してください。
SPIREアーキテクチャ — 3層構造
SPIRE(SPIFFE Runtime Environment)は、SPIFFE標準のリファレンス実装です。Go言語で書かれたOSSで、以下の3層構造で動作します。
graph TB
subgraph "Kubernetes Cluster"
SS[SPIRE Server<br>StatefulSet]
subgraph "Node 1"
SA1[SPIRE Agent<br>DaemonSet]
W1[Workload A<br>Pod]
W2[Workload B<br>Pod]
end
subgraph "Node 2"
SA2[SPIRE Agent<br>DaemonSet]
W3[Workload C<br>Pod]
end
end
SS -->|SVID配布| SA1
SS -->|SVID配布| SA2
SA1 -->|Workload API<br>UNIX Socket| W1
SA1 -->|Workload API<br>UNIX Socket| W2
SA2 -->|Workload API<br>UNIX Socket| W3
W1 -.->|mTLS| W3
W2 -.->|mTLS| W1
SPIRE Server(中央管理者)
SPIRE Serverはクラスター内でStatefulSetとしてデプロイされ、以下の役割を担います。
- SVIDの発行: 登録されたワークロードに対してX.509-SVID/JWT-SVIDを発行
- Trust Bundleの管理: 信頼するルート証明書(CA)の公開鍵セットを管理
- Registration Entryの管理: 「どのような属性を持つワークロードに、どのSPIFFE IDを付与するか」のポリシーを管理
SPIRE Agent(各ノードの配布拠点)
SPIRE AgentはDaemonSetとして各ノードに1つずつデプロイされます。
- SVIDの配布: Workload APIを通じて、ノード上のワークロードにSVIDを配布
- 自動ローテーション: 証明書のTTLが近づくと自動的に新しいSVIDを取得・配布(ローテーションのタイミングは設定に依存)
- キャッシュ: 取得したSVIDをローカルにキャッシュし、Serverが一時的にダウンしても既存のSVIDで通信を継続
Workload API(ワークロードとの接点)
ワークロードはUNIXドメインソケット(通常 /run/spire/sockets/agent.sock)経由でAgentにアクセスします。SPIFFE Workload APIはgRPCベースであり、SVIDの取得には公式ツールやSDKを使用します。
# spire-agent CLIを使ったSVID取得確認
$ kubectl exec -it <pod-name> -- \
/opt/spire/bin/spire-agent api fetch \
-socketPath /run/spire/sockets/agent.sock
ここが最大のポイントです。ワークロードは事前にデプロイされた認証情報なしにSVIDを取得できます。パスワードもAPIキーも不要です。これを実現するのが次の「アテステーション」です。
アテステーション — 静的クレデンシャルを不要にする革新
SPIREが従来の証明書管理システムと根本的に異なるのが、アテステーション(Attestation)の仕組みです。これこそが「Credential Zero」を実現する鍵です。
Node Attestation(ノードの認証)
SPIRE Agentが正当なものであることを検証します。Kubernetes環境ではk8s_psat Node Attestorを使用します。k8s_psatはKubernetesのProjected Service Account Token(ServiceAccountに紐づく投影トークン)を利用します。
Kubernetes API Server
│
▼
Projected SA Token ──→ SPIRE Server が検証
│ │
▼ ▼
Agent Podの正しさを 属性情報を抽出
検証(クラスタ情報、 (cluster name、namespace、
ServiceAccount等) ServiceAccount name)
SPIRE ServerはKubernetes API Serverの署名を検証することで、そのトークンを提示したSPIRE Agent Podが自クラスタの正当なPodであることを暗号学的に確認します。事前のシークレット配布が不要です。検証される属性や信頼前提の詳細については、SPIRE公式ドキュメントのNode Attestor説明を参照してください。
Workload Attestation(ワークロードの認証)
ノード上のSPIRE Agentが、実行中のワークロード(Pod)を特定します。Kubernetesでは以下のselectorを使用します。
k8s:ns:<namespace>— 対象のnamespacek8s:sa:<service-account>— 対象のServiceAccount
AgentはLinux cgroupsやprocfsを参照して、ソケットに接続してきたプロセスがどのPodかを特定します。そして、そのPodのnamespaceとServiceAccountが登録エントリ(Registration Entry)と一致するかを確認します。
# Registration Entryのイメージ
spiffeID: spiffe://example.org/ns/default/sa/my-app
parentID: spiffe://example.org/ns/spire/sa/spire-agent
selectors:
- k8s:ns:default # default namespace
- k8s:sa:my-app-sa # my-app-sa ServiceAccount
この仕組みの何が嬉しいのか。新しいPodをデプロイするだけで、自動的に正しいアイデンティティが付与されるのです。CI/CDパイプラインで証明書をプロビジョニングする必要はありません。Secretリソースに鍵を詰め込む必要もありません。Podがスケールアウトすれば、新しいPodも自動的にSVIDを取得します。
Trust Domain — 信頼の境界
SPIFFEでは、Trust Domainが信頼の境界を定義します。
spiffe://prod.example.org/ns/default/sa/api-server
└────────────────┘
Trust Domain = prod.example.org
- 同一Trust Domain内では、SPIRE Serverが発行したSVIDがすべて信頼される
- 異なるTrust Domain間で信頼を構築するにはFederationを設定する
- プロダクション環境とステージング環境で別のTrust Domainを使うのがベストプラクティス
環境ごとのTrust Domain設計例:
本番環境 → spiffe://prod.example.org
ステージング → spiffe://staging.example.org
開発環境 → spiffe://dev.example.org
この分離により、ステージング環境の証明書が本番環境で使われる事故を防げます。
SPIFFE/SPIREが解決する課題のまとめ
これまでのアプローチとSPIREを比較してみましょう。
| 課題 | 従来(APIキー/IP) | SPIFFE/SPIRE |
|---|---|---|
| 認証情報の配布 | Secret/CIで手動配布 | アテステーションで自動付与 |
| ローテーション | 手動 or 半自動(数ヶ月) | 自動(TTLに基づく) |
| スケール時の追跡 | IP変動で追跡困難 | SPIFFE IDで常に追跡可能 |
| 監査性 | 共有キーで誰が実行したか不明 | ワークロードごとに一意のID |
| 漏洩時の影響 | 有効期限が長く影響大 | 短命で露出期間を短縮 |
SPIFFE/SPIREは、「クレデンシャルを配布する」という行為自体を不要にするパラダイムシフトです。次のセクションでは、実際にKubernetes環境にSPIREをデプロイし、この仕組みを体験してみましょう。
ステップバイステップ:Kubernetes環境でのSPIREデプロイ
仕組みを理解したところで、実際にKubernetesクラスタにSPIREをデプロイしてみましょう。ここでは公式チュートリアル(spiffe.io)に沿ったハンズオン形式で進めます。
検証バージョン: 公式QuickstartはKubernetes 1.29〜1.34での動作を確認しています。お使いのSPIREバージョンおよびspire-tutorialsリポジトリのリビジョンに応じて、マニフェストの内容が異なる場合があります。必ず最新の公式ドキュメントと照合してください。
前提環境
以下の環境を用意してください。
- Kubernetes: バージョン 1.29〜1.34(EKS、GKE、AKS、Minikubeいずれでも可)
- kubectl: クラスタにアクセス可能な状態
- git: チュートリアルリポジトリのクローン用
💡 Minikubeを使用する場合の注意: MinikubeではService Account Token Signingの追加設定が必要です。起動時に以下のフラグを指定してください。
Step 0: チュートリアルリポジトリの準備
公式チュートリアルのマニフェスト群をクローンします。
git clone https://github.com/spiffe/spire-tutorials.git
cd spire-tutorials/k8s/quickstart
このディレクトリ内には、デプロイに必要なすべてのYAMLマニフェストが含まれています。
Step 1: Namespaceの作成
まず、SPIREコンポーネントを配置するNamespaceを作成します。
kubectl apply -f spire-namespace.yaml
# 確認
kubectl get namespaces | grep spire
出力例:
spire Active 10s
Step 2: SPIRE Serverのデプロイ
Server用のServiceAccount、ConfigMap、RBACリソースを適用します。
# ServiceAccount と RBAC
kubectl apply \
-f server-account.yaml \
-f spire-bundle-configmap.yaml \
-f server-cluster-role.yaml
# Server の設定・StatefulSet・Service
kubectl apply \
-f server-configmap.yaml \
-f server-statefulset.yaml \
-f server-service.yaml
公式QuickstartではStatefulSetとしてデプロイされます。
# デプロイ確認
kubectl get pods -n spire -l app=spire-server
出力例:
NAME READY STATUS RESTARTS AGE
spire-server-0 1/1 Running 0 86s
READY 1/1 で Running になっていれば成功です。
Step 3: SPIRE Agentのデプロイ
各ノードで動作するAgentをDaemonSetとしてデプロイします。
kubectl apply \
-f agent-account.yaml \
-f agent-cluster-role.yaml \
-f agent-configmap.yaml \
-f agent-daemonset.yaml
DaemonSetのため、ノード数分のAgent Podが起動します。
# デプロイ確認
kubectl get pods -n spire -l app=spire-agent
Step 4: Nodeの登録
SPIRE Agentが自分のノードを正当化するための「Node登録」を行います。ここでは k8s_psat(Projected Service Account Token)を使用したアテステーションを設定します。
kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://example.org/ns/spire/sa/spire-agent \
-selector k8s_psat:cluster:demo-cluster \
-selector k8s_psat:agent_ns:spire \
-selector k8s_psat:agent_sa:spire-agent \
-node
ここで設定しているselectorの意味を確認しておきましょう。
| Selector | 意味 |
|---|---|
k8s_psat:cluster:demo-cluster |
クラスタ名が demo-cluster であること |
k8s_psat:agent_ns:spire |
Agentが spire namespaceで動作していること |
k8s_psat:agent_sa:spire-agent |
AgentのServiceAccountが spire-agent であること |
この3つの条件をすべて満たすAgentのみが、正当なものとして認証されます。
Step 5: ワークロードの登録
次に、実際のアプリケーションPodに対する登録エントリを作成します。ここでは default namespace、default ServiceAccountで動作するワークロードを登録します。
kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://example.org/ns/default/sa/default \
-parentID spiffe://example.org/ns/spire/sa/spire-agent \
-selector k8s:ns:default \
-selector k8s:sa:default
ポイントを解説します。
-parentID: このワークロードを管理するAgentのSPIFFE IDを指定-selector k8s:ns:default:defaultnamespaceのPodであること-selector k8s:sa:default:defaultServiceAccountを使用していること
つまり、「default namespaceで default ServiceAccountを使っているPodには、spiffe://example.org/ns/default/sa/default というアイデンティティを付与する」というルールを定義しています。
Step 6: SVIDの取得確認
実際にワークロードからSVIDを取得できるか確認します。チュートリアルリポジトリには検証用のclient Podが含まれています。
# client Pod名を取得
CLIENT_POD=$(kubectl get pods -l app=client \
-o=jsonpath='{.items[0].metadata.name}')
# SVIDを取得
kubectl exec -it $CLIENT_POD -- \
/opt/spire/bin/spire-agent api fetch \
-socketPath /run/spire/sockets/agent.sock
取得できると、SPIFFE IDとX.509-SVIDの情報が表示されます。🎉 ワークロードは事前のシークレット設定なしに、暗号学的アイデンティティを取得できています。
💡 出力例について: 上記のコマンドは公式Quickstartで確認されている手順です。実行結果のPod名、タイムスタンプ、ハッシュ値は実行環境により異なります。実際の出力はご自身の環境で確認してください。
全体の動作確認
最後に、すべてのコンポーネントが正常に稼働しているか確認しましょう。
# SPIREコンポーネント全体の確認
kubectl get all -n spire
プロダクション環境への適用に向けて
このハンズオンではYAMLマニフェストを直接適用しましたが、プロダクション環境ではHelm Chartsの使用が推奨されます。
helm repo add spiffe https://spiffe.github.io/helm-charts
helm install spire spiffe/spire \
--namespace spire \
--create-namespace
Helm Chartsを使うことで、HA構成、データストアバックエンドの選択、監査ログ、Federation、モニタリング等のプロダクション要件を設定しやすくなります。プロダクション環境の構成(データストアの選択、HA方式、バックアップ戦略等)については、公式Helm ChartsおよびSPIREのデプロイメントガイドを参照してください。
⚠️ 重要: このチュートリアルのTrust Domainはexample.orgです。プロダクション環境では、自組織の実際のドメイン(例:prod.yourcompany.com)を使用してください。
実装例:GoサービスでのmTLS自動化
前のセクションで、ワークロードがSVIDを取得できることを確認しました。ここからは、そのSVIDを使って実際のサービス間mTLS通信を実装します。Go言語の公式SPIFFEライブラリを使ったアプローチと、Envoyサイドカーを使ったアプローチの2つを紹介します。
アーキテクチャ概要
実装するのは、2つのGoマイクロサービス間のmTLS通信です。
┌─────────────┐ mTLS ┌─────────────┐
│ Client Go │ ──── X.509-SVID ────→ │ Server Go │
│ Service │ ←── X.509-SVID ───── │ Service │
└─────────────┘ └─────────────┘
│ │
└──────── SPIRE Agent ───────────────┘
(Workload API)
- Client サービス:
spiffe://example.org/ns/default/sa/client - Server サービス:
spiffe://example.org/ns/default/sa/server
両者はSPIRE AgentのWorkload APIからX.509-SVIDを取得し、それを使って相互TLS認証を行います。
パターン1: アプリケーション直接統合(Goコード例)
SPIFFEの公式Goライブラリ(go-spiffe/v2)を使うと、わずか数行のコードでmTLSを実現できます。
事前準備
go get github.com/spiffe/go-spiffe/v2
Server側のコード
package main
import (
"context"
"crypto/tls"
"log"
"net/http"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
func main() {
ctx := context.Background()
// 1. Workload APIからX.509-SVIDソースを取得
source, err := workloadapi.NewX509Source(ctx,
workloadapi.WithClientOptions(
workloadapi.WithAddr("unix:///run/spire/sockets/agent.sock"),
),
)
if err != nil {
log.Fatalf("Failed to create X509Source: %v", err)
}
defer source.Close()
svid, ok := source.GetX509SVID()
if ok {
log.Printf("Server SVID: %s", svid.ID.String())
}
// 2. 許可するクライアントのSPIFFE IDをパース
clientID := spiffeid.MustParseURI("spiffe://example.org/ns/default/sa/client")
// 3. mTLSサーバー設定
tlsConfig := tlsconfig.MTLSServerConfig(
source,
source,
tlsconfig.AuthorizeID(clientID),
tlsconfig.MinVersion(tls.VersionTLS13),
)
// 4. HTTPSサーバー起動
server := &http.Server{
Addr: ":8443",
TLSConfig: tlsConfig,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from secure server!\n"))
}),
}
log.Println("Server listening on :8443 with mTLS")
// SVIDソースから証明書を取得するため、ファイルパスは不要
// (TLSConfigがすでにSVIDソースから動的に証明書を取得するよう設定済み)
log.Fatal(server.ListenAndServeTLS("", ""))
}
⚠️ コードに関する注意: 上記のコードはgo-spiffe/v2のAPIパターンを示したものです。Goやgo-spiffeのバージョン、TLSConfigの設定によってはListenAndServeTLS("", "")の挙動が異なる場合があります。実際のプロジェクトでは、go-spiffe公式サンプルを参照し、対象バージョンでビルド・動作検証を行ってください。
Client側のコード
package main
import (
"context"
"crypto/tls"
"io"
"log"
"net/http"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
func main() {
ctx := context.Background()
// 1. Workload APIからX.509-SVIDソースを取得
source, err := workloadapi.NewX509Source(ctx,
workloadapi.WithClientOptions(
workloadapi.WithAddr("unix:///run/spire/sockets/agent.sock"),
),
)
if err != nil {
log.Fatalf("Failed to create X509Source: %v", err)
}
defer source.Close()
svid, ok := source.GetX509SVID()
if ok {
log.Printf("Client SVID: %s", svid.ID.String())
}
// 2. 接続先サーバーの期待するSPIFFE IDをパース
serverID := spiffeid.MustParseURI("spiffe://example.org/ns/default/sa/server")
// 3. mTLSクライアント設定
tlsConfig := tlsconfig.MTLSClientConfig(
source,
source,
tlsconfig.AuthorizeID(serverID),
tlsconfig.MinVersion(tls.VersionTLS13),
)
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
// 4. リクエスト送信
resp, err := client.Get("https://server:8443")
if err != nil {
log.Fatalf("Request failed: %v", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
log.Printf("Response: %s", body)
}
クライアント側でも tlsconfig.AuthorizeID を使って、接続先サーバーが期待したSPIFFE IDを持っているかを検証しています。これが相互認証(Mutual TLS)です。クライアントだけがサーバーを検証するのではなく、サーバーもクライアントを検証し、双方が暗号学的に相手の身元を確認します。
コードから見るSPIFFEの威力
従来のmTLS実装と比較してみましょう。
| 項目 | 従来のmTLS | SPIFFEベースmTLS |
|---|---|---|
| 証明書の配置 | ファイルとしてディスクに配置 | Workload APIから動的取得 |
| 証明書の更新 | cron/certbot等で手動 or 半自動 | Agentが自動ローテーション |
| 認証局の管理 | 別途CAサーバー構築が必要 | SPIRE Serverが内蔵CA |
| クライアント認証 | クライアント証明書を配布する必要あり | アテステーションで自動取得 |
| 証明書失効 | CRL/OCSPの運用が必要 | 短命な証明書で失効の必要性を大幅に軽減 |
⚠️ 短命な証明書は露出期間を短縮する補完的対策であり、侵害時の検知・隔離・認可変更や、CA/秘密鍵の侵害対応を不要にするものではありません。
証明書の自動ローテーションフロー
アプリケーションコードを変更せずに、証明書は自動的に更新されます。その仕組みを確認しておきましょう。
sequenceDiagram
participant W as Workload
participant A as SPIRE Agent
participant S as SPIRE Server
Note over W,A: 初回SVID取得
W->>A: Fetch SVID (via Workload API)
A->>A: Workload Attestation
A->>S: Request SVID for attested workload
S->>S: Verify registration entry
S-->>A: Return X.509-SVID + Private Key
A-->>W: Deliver SVID
Note over W,A: 自動ローテーション(TTLが近づくと)
A->>S: Auto-rotate expiring SVID
S-->>A: New X.509-SVID
A-->>W: Push updated SVID
ローテーションのポイント:
- TTLの一定割合に達した時点で自動実行: ローテーションのタイミングや挙動はSPIREのバージョンと設定に依存します
- シームレスな更新: 新旧証明書の切り替えは、アプリケーションやプロキシ、接続プールの設計に依存します
- アプリの再起動不要:
go-spiffeライブラリはSVIDソースをラップしており、ローテーションを透過的に処理 - Server障害時の耐性: AgentはSVIDをキャッシュするため、Serverが一時的にダウンしても既存のSVIDで通信継続が可能
パターン2: Envoyサイドカー統合(アプリ変更なし)
アプリケーションコードを変更できない、あるいは変更したくないケースもあるでしょう。その場合はEnvoyプロキシをサイドカーとして配置し、EnvoyのSecret Discovery Service(SDS)経由でSVIDを取得する方法が使えます。
┌─────────────────── Pod ───────────────────┐
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ App │◄───►│ Envoy Sidecar │ │
│ │ (変更なし)│ │ SDS → SPIRE │ │
│ └──────────┘ │ Agent │ │
│ └──────────────────┘ │
└───────────────────────────────────────────┘
Envoy SDSとSPIRE Agentを連携させる設定については、SPIREのバージョンやデプロイ方法によって接続方式が異なります。詳細な設定例と動作確認済みのマニフェストについては、SPIRE公式ドキュメントおよび対象バージョンの設定リファレンスを参照してください。
このアプローチの利点はアプリケーションコードを変更せずにmTLSを有効化できる可能性があることです。EnvoyがTLS処理を請け負い、アプリは通常のHTTPでEnvoyと通信します。
TLS 1.3による低レイテンシー認証
コード例で tls.VersionTLS13 を指定していることに注目してください。TLS 1.3は以下の理由でマイクロサービス間通信に適しています。
- ハンドシェイクが1-RTT: TLS 1.2の2-RTTと比較してレイテンシーを半減
- 前方秘匿性: 全セッションで一時鍵を使用し、長期的な秘密鍵漏洩から保護
- レガシーアルゴリズムの削除: RC4、SHA-1、MD5等の脆弱なアルゴリズムを完全排除
SPIREとTLS 1.3の組み合わせにより、セキュリティとパフォーマンスを両立したサービス間通信が実現します。
SPIRE vs その他の認証・シークレット管理ソリューション
「ワークロード間の認証をどうするか」という問いに対して、業界では複数のアプローチが存在します。SPIRE、HashiCorp Vault、手動mTLS、そしてOAuth2/JWT——それぞれが異なる前提と得意領域を持っています。このセクションでは、4つのソリューションを多角的に比較し、「どれを選ぶか」ではなく「どう組み合わせるか」の判断材料を提供します。
比較の前提:認証とシークレット管理は別のレイヤー
比較に入る前に、最も重要な概念の切り分けを明確にしておきます。
- 認証(Authentication): 「あなたは誰か」を暗号学的に証明するレイヤー
- シークレット管理(Secret Management): 「必要な鍵やパスワードを、必要な時に、安全に提供する」レイヤー
SPIREは前者を担い、Vaultは後者を担います。これらは競合ではなく、補完関係にあります。この切り分けを念頭に置いて比較表を見てください。
4ソリューション比較表
| 比較項目 | SPIRE (SPIFFE) | HashiCorp Vault | 手動mTLS(cert-manager等) | OAuth2 / JWT |
|---|---|---|---|---|
| 主目的 | ワークロード・アイデンティティの自動発行・検証 | シークレットの保管・動的発行・ローテーション | サービス間通信の暗号化 | ユーザー/アプリの認可・委譲 |
| 認証方式 | アテステーション(環境メタデータによる自動認証) | 事前配布されたトークン or 設定による認証 | 手動/半手動で発行されたX.509証明書 | Bearer Token(署名済みJWT) |
| Credential Zero | ✅ 解決する(静的クレデンシャル不要) | ❌ 直面する(Vaultへの認証情報が必要) | ❌ プライベートCAの秘密鍵管理が必要 | ❌ 署名鍵 or クライアントシークレットが必要 |
| 証明書のライフサイクル | 自動発行・自動ローテーション(TTL設定可能) | 動的シークレットのリース期間で管理 | cert-manager等で自動化可能だが、CA管理は別途必要 | トークン有効期限で管理 |
| Kubernetes統合 | DaemonSet + Workload API | Vault Agent Injector / CSI Provider | cert-manager + ワークロード側で参照 | Ingress / API Gateway層で検証 |
「どれか一つ」ではなく「どう組み合わせるか」
比較表を見ると、それぞれのソリューションが異なるレイヤーをカバーしていることがわかります。エンタープライズ環境では、以下のような多層防御がベストプラクティスです。
パターン1: SPIRE + Vault(推奨:ゼロトラストのゴールドスタンダード)
ワークロード → [SPIRE AgentからSVID取得] → [SVIDでVaultに認証] → [動的シークレット取得] → [DB/外部APIアクセス]
この組み合わせが解決するのは「Credential Zero問題」です。Vault単体の場合、Vault自身にアクセスするための認証情報(トークンやAppRole)をどう安全に配布するかという「鶏と卵」問題に直面します。SPIREのアテステーションを使えば、環境メタデータ(KubernetesのNamespace、ServiceAccount等)に基づいて自動的にSVIDが発行されるため、静的クレデンシャルを持ちません。
Vault側では、JWT Auth Method(JWT-SVIDの署名・issuer・audienceを検証)またはKubernetes Auth Method(Kubernetes ServiceAccount JWTを検証)を使ってワークロードを認証します。JWT-SVIDをVaultのJWT Auth Methodで検証する場合は、SPIREのTrust Bundle(JWKSエンドポイント経由またはbundle endpoint)をVault側に配布し、JWT-SVIDの署名を検証できるようにする必要があります。これはVaultのKubernetes Auth Methodとは別の方式であり、設定要件も異なります。詳細はSPIRE Vault連携の公式ドキュメントおよびVaultの認証方法ドキュメントを参照してください。
パターン2: SPIRE + Istio(サービスメッシュ環境)
Istioは内部でSPIFFE ID形式(spiffe://cluster.local/ns/<namespace>/sa/<service-account>)を使用しています。SPIREを外部CAとして統合することで、証明書の発行とローテーションを一元化できます。ただし、IstioとSPIREの統合方式(IstioのCAとしてSPIREを利用する方法や、CSI Driver経由での統合等)は、IstioおよびSPIREのバージョン、デプロイ構成に依存します。具体的な統合手順については、対象バージョンのIstio公式ドキュメントおよびSPIRE公式ドキュメントを参照してください。
パターン3: OAuth2/JWTは「人間とエージェントの境界」で使う
OAuth2/JWT(OIDC)は、ユーザー認可や外部APIへの委譲には適していますが、マイクロサービス間の常時通信には向いていないケースが多いです。理由は以下の通りです:
- JWTはBearer Tokenであり、奪取されると有効期限まで悪用可能
- トークン発行のためにIdP(Identity Provider)へのラウンドトリップが発生
- サービス間の信頼関係ではなく、ユーザーの委譲(Delegation)をモデル化している
ただし、AIエージェントがユーザーの権限で外部SaaS APIにアクセスするケース(例:Slack、GitHub、Google Workspace)では、OAuth2 + Workload Identity Federationが適しています。内部通信はmTLS(SPIRE)、外部通信はOAuth2(WIF)という使い分けが、一部のエンタープライズ環境で採用されています。
コスト比較の現実的な視点
ライセンス費用だけで比較すると、SPIRE(OSS)は無料ですが、運用コストは無視できません。以下の観点で総所有コスト(TCO)を評価すべきです:
| コスト要因 | SPIRE | Vault | 手動mTLS | OAuth2/JWT |
|---|---|---|---|---|
| 初期導入 | 中(アーキテクチャ設計が必要) | 中〜高(クラスター構築) | 低(cert-managerで完結しやすい) | 低(マネージドIdP利用) |
| 継続運用 | 中(Server/Agentの監視、アップグレード) | 高(バックアップ、シール/アンシール管理) | 低〜中(CA証明書の更新管理) | 低(IdPがマネージド) |
| スケール時 | 低(Agent追加のみで対応) | 中(クラスター構成の考慮) | 低 | 低 |
| インシデント時 | 低(SVIDは短命で被害範囲が限定) | 中〜高(シークレット漏洩の影響範囲が大きい) | 中(CA侵害時の影響大) | 中(トークン漏洩の影響) |
まとめ:選択の指針
- 新規マイクロサービス環境 → SPIRE + IstioでmTLSを自動化し、必要に応じてVaultを追加
- 既存のVault環境がある → SPIREを追加して「Credential Zero」を実現し、Vaultへの安全なアクセスを確立
- レガシー環境で段階的に移行 → まずcert-managerでmTLSを導入し、成熟したらSPIREへ移行
- AIエージェントが外部SaaSにアクセス → 内部通信はSPIRE mTLS、外部APIはWorkload Identity Federation + OAuth2
重要なのは、「どれか一つを選ぶ」のではなく、各ソリューションの得意領域を理解し、レイヤーとして組み合わせることです。
AIエージェント時代のワークロードID——「非人間ID」の課題と解決策
エンタープライズ環境において急速に増加している通信主体は人間ではありません。AIエージェント、バッチ推論パイプライン、RAGデータローダー、自動化ボット——これら「非人間主体(Non-Human Identity: NHI)」の数は、人間のユーザーアカウントを大きく上回っています。
このセクションでは、AIエージェントが「安全なアイデンティティを持つ」という課題(NHI問題)を深掘りし、SPIFFE/SPIREを活用した解決策を提示します。
非人間ID(NHI)問題とは何か
従来のエンタープライズ認証は「人間が中心」でした。Active Directory、Okta、Azure AD——これらはすべて人間のユーザーがシステムにログインすることを前提に設計されています。しかし、AIエージェントの台頭により、この前提は根本から揺らいでいます。
NHIが引き起こす3つの構造的課題
1. 認証情報の「借用」問題
現在、多くのAIエージェントは人間の認証情報を「借用」して動いています。退職者のPersonal Access Token(PAT)がエージェントに埋め込まれたまま稼働し続けたり、共有サービスアカウントのAPIキーが複数のエージェント間で使い回されたりするリスクが指摘されています。
想定シナリオ(架空の例): Jiraの課題経由でプロンプトインジェクションを受けたAIエージェントが、借用していたHRシステムのAPIキーを使って従業員データにアクセスしてしまうケース。エージェント自身は「指示に従った」までですが、結果として権限スコープを大きく超えるデータアクセスが発生する可能性があります。これは実際のインシデントではなく、NHI問題の理解のための脅威モデルです。
2. 監査のブラックボックス化
複数のエージェントが同一のサービスアカウントを共有していると、「誰が(どのエージェントが)いつ、どのデータにアクセスしたか」が追跡できなくなります。コンプライアンス監査において、これは致命的な欠陥です。
3. 権限昇格のリスク
AIエージェントは自律的に意思決定を行うため、プロンプトインジェクションやツールの誤用を通じて、意図せず権限スコープを超えたアクションを実行するリスクがあります。人間のユーザーであれば「この操作はおかしい」と気づける場面でも、エージェントは与えられた権限の範囲内であれば躊躇なく実行してしまいます。
AIエージェント向けSPIFFE ID設計
SPIREによるNHI解決の核心は、各エージェントに固有の暗号学的アイデンティティを付与し、認可ポリシーで最小権限を強制することです。以下は、AIパイプラインにおけるSPIFFE ID設計の例です。
# トレーニングジョブ(モデルごと・実行ごとに固有)
spiffe://ai.example.com/training/{model-name}/{run-id}
# フィーチャーストアへの読み取りアクセス(チーム単位)
spiffe://ai.example.com/feature-store/reader/{team}
# モデルレジストリ(環境ごとに分離)
spiffe://ai.example.com/registry/{environment}
# 推論サービス(モデル・バージョンごとに固有)
spiffe://ai.example.com/serving/{model-name}/{version}
# データアクセス(目的・データセット単位でスコープ制限)
spiffe://ai.example.com/data-access/{purpose}/{dataset}
この設計の重要なポイントは粒度です。従来の「チーム単位のAPIキー」では、あるデータサイエンティストのエージェントが別チームのデータセットにアクセスできるかもしれません。しかし、SPIFFE IDが data-access/{purpose}/{dataset} 単位で発行されていれば、認可ポリシーで「このエージェントはこのデータセットにだけGETリクエストを送れる」ことを暗号学的に強制できます。
Istio AuthorizationPolicyによるきめ細かい制御
SPIFFE IDを設計したら、次はそれを使った認可ポリシーの実装です。IstioのAuthorizationPolicyを使えば、SPIFFE IDベースでアクセス制御を宣言的に定義できます。
# 例: 詐欺検知モデルのトレーニングのみがfraud-dataにアクセス可能
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: fraud-data-access
namespace: ml-data
spec:
selector:
matchLabels:
app: fraud-data-service
rules:
- from:
- source:
principals:
- "spiffe://ai.example.com/training/fraud-model/*"
to:
- operation:
methods: ["GET"]
paths: ["/api/v1/features/*"]
このポリシーは、「詐欺検知モデルのトレーニングジョブ(fraud-model/*にマッチするSPIFFE IDを持つワークロード)だけが、fraud-data-serviceのfeaturesエンドポイントにGETリクエストを送れる」ことを意味します。
「鍵を一切持たせない」設計:Workload Identity Federation
AIエージェントの認証において注目すべきアプローチが Workload Identity Federation(WIF) とSPIREの組み合わせです。
従来のアプローチでは、クラウドAPI(AWS、GCP、Azure)にアクセスするために長期的なクレデンシャル(アクセスキーやサービスアカウントキー)をエージェントに配布する必要がありました。これは「鍵を守るために鍵が必要」というパラドックスを生み出します。
WIFを使えば、外部ワークロード(オンプレミスやマルチクラウドのエージェント)が、長期的なクレデンシャルを持たずにクラウドリソースにアクセスできます。SPIREが発行するJWT-SVIDをWIFで検証し、一時的なクラウドクレデンシャルと交換します。
[AIエージェント] → SPIRE AgentからJWT-SVID取得 → WIFで検証 → 一時的クレデンシャル発行 → クラウドAPIアクセス
このフローでは、エージェントは一切の静的クレデンシャルを持ちません。JWT-SVIDは短命で自動ローテーションされ、WIFで発行されるクラウドクレデンシャルも一時的です。「鍵を一切持たせない」ことが、プロンプトインジェクションやエージェントの誤動作による被害を最小化する鍵です。
想定シナリオから学ぶ:SPIRE導入による軽減
前述のJira経由プロンプトインジェクションの想定シナリオ(架空の例)を、SPIRE導入後の姿で振り返ってみましょう。
従来構成での被害シナリオ(想定):
- 攻撃者がJiraの課題記述欄にプロンプトインジェクションを仕込む
- AIエージェントがJira API(PAT認証)で課題を取得
- インジェクションにより、エージェントがHR API(同一PATでアクセス可能)から従業員データを取得
- 取得したデータをJiraのコメント欄に投稿(外部公開)
SPIRE + 最小権限ポリシーでの軽減シナリオ:
- 攻撃者が同様にプロンプトインジェクションを仕込む(同じ)
- AIエージェントがJira APIにアクセス——ただしSPIFFE IDは
spiffe://corp/jira-reader/{agent-id}に限定 - インジェクションによりHR APIへのアクセスを試みる
- AuthorizationPolicyが
jira-readerSPIFFE IDからのHR APIアクセスを拒否 - アクセス拒否が監査ログに記録され、セキュリティチームにアラート
重要なのは、SPIREはエージェントの「自律性」を制限するものではないという点です。エージェントは引き続き自律的にタスクを実行できますが、暗号学的アイデンティティに基づく認可ポリシーが、意図せぬ権限スコープの拡大を防ぐ最後の防壁となります。
実運用のためのベストプラクティスと落とし穴
SPIREの概念を理解し、Quickstartを完了したら、次は「本番環境で運用する」フェーズです。このセクションでは、導入プロジェクトで一般的に指摘されるベストプラクティスと、陥りやすい落とし穴を整理します。
Trust Domain設計:最初で最も重要な決断
SPIREを導入する際、最初に決めなければならないのがTrust Domainの設計です。Trust Domainは暗号学的な信頼の境界であり、後からの変更が非常に困難です(すべてのSVIDの再発行が必要になるため)。
ベストプラクティス:環境ごとのTrust Domain分離
# 推奨: 環境ごとに分離
spiffe://prod.example.org # 本番環境
spiffe://staging.example.org # ステージング環境
spiffe://dev.example.org # 開発環境
環境ごとにTrust Domainを分離することで、ステージング環境で発行されたSVIDが本番環境で使用されることを暗号学的に防げます。
落とし穴:Trust Domainの「後からの変更」
Trust Domainを変更するには、全ノードのSPIRE Agentの再登録、全ワークロードのSVID再取得、信頼するルート証明書の全環境での配布し直しが必要です。最初から正しく設計することが圧倒的に安上がりです。
証明書のTTLとローテーション戦略
SPIREの強みは「短命な証明書の自動ローテーション」ですが、TTL(Time To Live)の設定は環境の特性に合わせて調整する必要があります。
用途別のTTL設定目安
| ワークロード種別 | TTL目安 | 理由 |
|---|---|---|
| マイクロサービス(mTLS) | 1〜2時間 | 十分な余裕があり、侵害時の露出期間を短縮 |
| AI推論エージェント | 30分〜1時間 | 長時間稼働するが、再起動時の影響を最小化 |
| バッチジョブ(短期) | ジョブ実行時間の2〜3倍 | ジョブ実行中の証明書切れを防止 |
| CI/CDパイプライン | 15〜30分 | 実行時間が短く、頻繁に再発行される |
⚠️ TTLの具体的なデフォルト値、設定可能範囲、ローテーションのタイミング(TTLの何%に達したら開始するか等)は、SPIREのバージョンと設定に依存します。公式の設定リファレンスで確認してください。
落とし穴:ローテーション中のレースコンディション
まれに、証明書のローテーションと実際の通信のタイミングが重なり、古い証明書で確立した接続が新しい証明書の検証に失敗するケースがあります。これを防ぐには:
- Envoy SDS統合を使用する: SDSは動的な証明書更新をサポートし、コネクションの再確立を処理します
- アプリケーション直接統合の場合はSDKの自動更新を信頼する: 最新のSPIFFE Go SDKは、バックグラウンドで自動的にSVIDを更新します
- TTLを極端に短くしすぎない: ローテーションのオーバーヘッドが無視できなくなります
アテステーションの設計とSelector戦略
ベストプラクティス:最小特権のSelector設計
# ✅ 良い例: Namespace + ServiceAccountで厳密に特定
-selector k8s:ns:payment \
-selector k8s:sa:payment-processor
# ❌ 悪い例: Namespaceだけだと広すぎる
-selector k8s:ns:default
ワークロードごとにServiceAccountを作成することを初期段階から徹底してください。
可観測性:監査ログとメトリクス
SPIREは「ブラックボックス」で運用してはいけません。SVIDの発行、検証、ローテーションのすべてを可観測にする必要があります。
3つの監視レイヤー:
- SPIRE Serverの監視: SVID発行数の推移、登録エントリの変更履歴、リソース使用量
- SPIRE Agentの監視: 各ノードのAgentの稼働状況、Workload APIの応答レイテンシ
- mTLS通信の監視: 証明書検証の失敗率、Trust Bundleの同期遅延
よくある導入失敗パターン
| 失敗パターン | 原因 | 対策 |
|---|---|---|
| 「全部の通信を一度にmTLS化しようとした」 | 一度に全サービスを移行すると、トラブルシュートが困難 | 段階的移行:まず2サービス間でmTLSを確立し、徐々に拡大 |
| 「アプリにSVID取得コードを埋め込んだ」 | 言語別SDKの制約やアップデート追従の負荷 | Envoy SDSサイドカーでアプリ変更なしにmTLSを追加 |
| 「Trust Domainを考えずに始めた」 | 環境分離ができず、後からの移行コストが巨大 | 導入前にTrust Domain設計を必ずレビューする |
| 「証明書のTTLを長くしすぎた」 | 短命さこそがSPIREの価値。長TTLは侵害時のリスク拡大 | デフォルトで短いTTLを設定 |
| 「監視なしで本番導入した」 | SVID発行失敗やローテーション失敗に気づかない | 導入前にPrometheus/Grafanaダッシュボードとアラートを準備 |
導入ロードマップの推奨順序
- フェーズ1(1〜2週間): Trust Domain設計、開発環境でのQuickstart検証
- フェーズ2(2〜4週間): 2つのマイクロサービス間でmTLSを確立(Envoy SDS使用)
- フェーズ3(4〜8週間): Istioとの統合、全サービスへの段階的mTLS展開
- フェーズ4(8〜12週間): Vault統合、AIエージェントへのSPIFFE ID付与
- フェーズ5(継続): マルチクラスタFederation、監査ダッシュボードの完成
FAQ — よくある質問と回答
SPIRE、SPIFFE、mTLS、ゼロトラストアーキテクチャについて、導入検討時に寄せられるよくある質問に回答します。
Q: SPIREとmTLSの違いは何ですか?
A: SPIREはワークロードのアイデンティティを発行・管理するシステムであり、mTLSはそのアイデンティティを使って通信を暗号化・相互認証するプロトコルです。例えるなら、SPIREが「パスポート(身分証)」を発行し、mTLSがそのパスポートを使って「互いの身元を確認しながら暗号化して通信する」手続きにあたります。両者は補完関係にあります。
Q: SPIREは無料で使えますか?
A: はい。SPIFFE/SPIREはCNCF graduated projectであり、Apache 2.0ライセンスのオープンソースソフトウェアです。ライセンス費用は無料で、商用利用も可能です。ただし、本番運用にはプラットフォームエンジニアリングのリソースが必要です。商用サポートやマネージド体験が必要な場合は、Spirl等の商用ディストリビューションも提供されています。
Q: IstioとSPIREはどう違いますか?競合しますか?
A: 競合ではなく補完関係です。Istioはサービスメッシュとして通信制御・ポリシー適用・可観測性を提供するプラットフォームであり、SPIREはワークロード・アイデンティティの発行を担当します。Istioは内部でSPIFFE標準を使用しており、SPIREを外部アイデンティティプロバイダーとして統合することで、マルチクラスタやハイブリッドクラウド環境での一元的なアイデンティティ管理が可能になります。
Q: SPIREとHashiCorp Vaultはどちらを使うべきですか?
A: 両方を使うのがベストプラクティスです。SPIREとVaultは解決する課題が異なります。SPIREはワークロードの認証(アイデンティティ証明)、Vaultはシークレットの保管・動的発行を担当します。SPIREのSVIDを使ってVaultに認証することで、「Vaultにアクセスするための認証情報をどこに保管するか」というブートストラップ問題(Credential Zero問題)を解決できます。
Q: Kubernetes以外の環境でSPIREは使えますか?
A: はい。SPIREはKubernetesだけでなく、AWS EC2、Google Compute Engine、Azure VM、オンプレミスサーバーなど多様な環境で動作します。各プラットフォームに対応するNode Attestorが提供されています。
Q: 証明書のローテーション頻度は?ダウンタイムは発生しますか?
A: SPIREでは、X.509-SVIDのTTLを設定によりカスタマイズできます。ローテーションは自動化されており、Workload API経由でSVIDを取得しているアプリケーションやEnvoy SDS経由で設定されているサイドカープロキシには、新しい証明書がプッシュされます。ダウンタイムの有無は、アプリケーションのTLS実装、接続プールの設計、プロキシの構成に依存します。
Q: AIエージェントにSPIREのアイデンティティを持たせることは可能ですか?
A: 可能です。AIエージェントは非人間主体(NHI: Non-Human Identity)として、従来のAPIキー認証では監査性とセキュリティの両面で限界があります。SPIREをAIパイプラインに適用することで、エージェントの役割に応じたSPIFFE IDを付与でき、IstioのAuthorizationPolicyと組み合わせることで「どのエージェントがどのデータにアクセス可能か」をきめ細かく制御できます。
Q: SPIREの導入で最も難しいのはどの部分ですか?
A: Trust Domainの設計が最も難易度が高い部分です。組織全体で1つのTrust Domainにするか、環境ごとに分けるか、マルチクラスタ環境でFederationを構築するかなど、組織のセキュリティ要件を総合的に考慮する必要があります。まずは単一クラスタの検証環境で小さく始め、Trust Domainの運用モデルを確立してから本番環境へ展開することをお勧めします。
今後のロードマップとトレンド
SPIFFE/SPIREエコシステムは、CNCF graduated projectとして成熟期に入りました。本章では、注目すべきトレンドを整理します。
注目すべきトレンド
1. 非人間アイデンティティ(NHI)の標準化
AIエージェント、CI/CDパイプライン、自動化スクリプトなど、非人間主体の通信が増加する中、OAuth 2.0 Portfolio(RFC 9700〜)やSPIFFEを組み合わせたNHI管理の標準化が進んでいます。SPIREはこの流れの中心的技術のひとつとして位置づけられています。
2. Workload Identity Federation(WIF)との統合
Google Cloud、Azure、AWS各社が提供するWorkload Identity FederationとSPIFFEを組み合わせた「鍵ゼロ」設計がベストプラクティスとして認知されつつあります。
3. サービスメッシュとの統合の深化
IstioをはじめとするサービスメッシュにおけるSPIFFEネイティブサポートが進んでいます。ただし、統合方式や安定性は製品・バージョン・構成に依存するため、対象バージョンの公式ドキュメントで確認してください。
4. 証明書ライフサイクルの短命化
セキュリティ業界全体で「証明書は短命であるほど良い」というコンセンサスが強まっています。自動ローテーションが前提となるため、運用負荷を増やすことなくセキュリティ姿勢を向上させることができます。
公式リソース
| リソース | URL |
|---|---|
| SPIFFE公式サイト | spiffe.io |
| SPIRE Kubernetes Quickstart | Getting Started with SPIRE on K8s |
| NIST SP 800-207(Zero Trust Architecture) | csrc.nist.gov/pubs/sp/800/207/final |
| SPIRE Tutorials(GitHub) | github.com/spiffe/spire-tutorials |
| SPIRE Helm Charts | github.com/spiffe/helm-charts |
| CNCF Slack #spiffe チャンネル | cloud-native.slack.com |
まとめ — アイデンティティがAI時代のセキュリティ基盤である理由
本記事では、SPIFFE/SPIREを用いたゼロトラストなワークロード間認証の全体像を、概念からハンズオン実装、AIエージェントへの応用まで解説してきました。
本記事の主要ポイント
1. ネットワーク境界防御の時代は終わった
VPN + ファイアウォールによる「内か外か」のセキュリティモデルは、マイクロサービスの動的スケーリングとAIエージェントの増加により限界を迎えています。NIST SP 800-207が示すゼロトラストアーキテクチャへの移行が推奨されています。
2. SPIFFE/SPIREは「ワークロードのパスポート」を発行する
静的IPアドレスもAPIキーも使わず、プラットフォームのメタデータによるアテステーションで自動的にワークロードを認証し、短命なX.509-SVIDを発行します。これが「Credential Zero」問題の解決策です。
3. Kubernetesへのデプロイは数時間で完了する
公式チュートリアルとHelm Chartsにより、SPIRE Server・AgentのデプロイからSVID取得までの流れは技術的に難しくありません。プロダクション運用ではTrust Domain設計と運用体制の構築が重要です。
4. mTLSで通信を暗号化し、自動ローテーションで運用を軽減
SPIREが発行するX.509-SVIDを使えば、Goアプリケーションの直接統合でもEnvoyサイドカー経由でも、mTLS通信を実現できます。証明書のローテーションは自動化されています。
5. SPIREとVaultは「組み合わせて使う」のが正解
SPIREが認証(アイデンティティ証明)、Vaultがシークレット管理(保管・動的発行)を担当し、SVIDでVaultに認証するフローがベストプラクティスです。
6. AIエージェントこそがSPIREの重要な受益者
AIエージェントに役割ベースのSPIFFE IDを付与し、IstioのAuthorizationPolicyでアクセス制御することで、プロンプトインジェクション経由の権限昇格を防ぐことができます。
読者へのネクストステップ
🚀 今すぐ(今日〜今週)
- SPIRE Kubernetes Quickstartを試す: ローカルのKubernetes環境でSPIREをデプロイし、2つのサービス間でmTLS通信を確認する。所要時間は約1時間です。
- spire-tutorials リポジトリをクローンする: ハンズオンで必要なマニフェストファイルがすべて揃っています。
📐 設計フェーズ(来週〜来月)
- Trust Domain設計ワークショップを開催する: プラットフォームチーム、セキュリティチーム、SREが集まり、組織のTrust Domain境界を議論する。
- 既存の認証フローを棚卸しする: 現在稼働中のサービス間通信の認証手法を可視化し、SPIRE移行の優先度を判断する。
🏗️ 実装フェーズ(1〜3ヶ月)
- ステージング環境でSPIRE + Istioを構築する: 単一クラスタで実際のワークロードでmTLSとAuthorizationPolicyを検証する。
- AIエージェントのパイプラインにSPIFFE IDを導入する: 各コンポーネントに役割ベースのSPIFFE IDを付与し、最小権限の原則を実装する。
📈 スケールフェーズ(3〜6ヶ月)
- プロダクション展開と監査ログ統合: SIEMにSPIREの監査ログを統合する。
- マルチクラスタFederationの検討: 複数クラスタやハイブリッドクラウド環境で、Trust Domainをまたぐ信頼関係を構築する。
"Trust is not a network property. It's an identity property."
アイデンティティこそが、セキュリティの基盤です。